xref: /aosp_15_r20/external/cronet/base/process/process_metrics_linux.cc (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
1*6777b538SAndroid Build Coastguard Worker // Copyright 2013 The Chromium Authors
2*6777b538SAndroid Build Coastguard Worker // Use of this source code is governed by a BSD-style license that can be
3*6777b538SAndroid Build Coastguard Worker // found in the LICENSE file.
4*6777b538SAndroid Build Coastguard Worker 
5*6777b538SAndroid Build Coastguard Worker #include "base/process/process_metrics.h"
6*6777b538SAndroid Build Coastguard Worker 
7*6777b538SAndroid Build Coastguard Worker #include <dirent.h>
8*6777b538SAndroid Build Coastguard Worker #include <fcntl.h>
9*6777b538SAndroid Build Coastguard Worker #include <inttypes.h>
10*6777b538SAndroid Build Coastguard Worker #include <stddef.h>
11*6777b538SAndroid Build Coastguard Worker #include <stdint.h>
12*6777b538SAndroid Build Coastguard Worker #include <sys/stat.h>
13*6777b538SAndroid Build Coastguard Worker #include <sys/sysmacros.h>
14*6777b538SAndroid Build Coastguard Worker #include <sys/time.h>
15*6777b538SAndroid Build Coastguard Worker #include <sys/types.h>
16*6777b538SAndroid Build Coastguard Worker #include <unistd.h>
17*6777b538SAndroid Build Coastguard Worker 
18*6777b538SAndroid Build Coastguard Worker #include <optional>
19*6777b538SAndroid Build Coastguard Worker #include <string_view>
20*6777b538SAndroid Build Coastguard Worker #include <utility>
21*6777b538SAndroid Build Coastguard Worker 
22*6777b538SAndroid Build Coastguard Worker #include "base/containers/span.h"
23*6777b538SAndroid Build Coastguard Worker #include "base/cpu.h"
24*6777b538SAndroid Build Coastguard Worker #include "base/files/dir_reader_posix.h"
25*6777b538SAndroid Build Coastguard Worker #include "base/files/file_util.h"
26*6777b538SAndroid Build Coastguard Worker #include "base/logging.h"
27*6777b538SAndroid Build Coastguard Worker #include "base/memory/ptr_util.h"
28*6777b538SAndroid Build Coastguard Worker #include "base/notreached.h"
29*6777b538SAndroid Build Coastguard Worker #include "base/numerics/clamped_math.h"
30*6777b538SAndroid Build Coastguard Worker #include "base/numerics/safe_conversions.h"
31*6777b538SAndroid Build Coastguard Worker #include "base/process/internal_linux.h"
32*6777b538SAndroid Build Coastguard Worker #include "base/strings/string_number_conversions.h"
33*6777b538SAndroid Build Coastguard Worker #include "base/strings/string_split.h"
34*6777b538SAndroid Build Coastguard Worker #include "base/strings/string_tokenizer.h"
35*6777b538SAndroid Build Coastguard Worker #include "base/strings/string_util.h"
36*6777b538SAndroid Build Coastguard Worker #include "base/system/sys_info.h"
37*6777b538SAndroid Build Coastguard Worker #include "base/threading/thread_restrictions.h"
38*6777b538SAndroid Build Coastguard Worker #include "base/types/expected.h"
39*6777b538SAndroid Build Coastguard Worker #include "base/values.h"
40*6777b538SAndroid Build Coastguard Worker #include "build/build_config.h"
41*6777b538SAndroid Build Coastguard Worker #include "third_party/abseil-cpp/absl/strings/ascii.h"
42*6777b538SAndroid Build Coastguard Worker 
43*6777b538SAndroid Build Coastguard Worker namespace base {
44*6777b538SAndroid Build Coastguard Worker 
45*6777b538SAndroid Build Coastguard Worker class ScopedAllowBlockingForProcessMetrics : public ScopedAllowBlocking {};
46*6777b538SAndroid Build Coastguard Worker 
47*6777b538SAndroid Build Coastguard Worker namespace {
48*6777b538SAndroid Build Coastguard Worker 
49*6777b538SAndroid Build Coastguard Worker #if BUILDFLAG(IS_CHROMEOS)
50*6777b538SAndroid Build Coastguard Worker // Read a file with a single number string and return the number as a uint64_t.
ReadFileToUint64(const FilePath & file)51*6777b538SAndroid Build Coastguard Worker uint64_t ReadFileToUint64(const FilePath& file) {
52*6777b538SAndroid Build Coastguard Worker   std::string file_contents;
53*6777b538SAndroid Build Coastguard Worker   if (!ReadFileToString(file, &file_contents))
54*6777b538SAndroid Build Coastguard Worker     return 0;
55*6777b538SAndroid Build Coastguard Worker   TrimWhitespaceASCII(file_contents, TRIM_ALL, &file_contents);
56*6777b538SAndroid Build Coastguard Worker   uint64_t file_contents_uint64 = 0;
57*6777b538SAndroid Build Coastguard Worker   if (!StringToUint64(file_contents, &file_contents_uint64))
58*6777b538SAndroid Build Coastguard Worker     return 0;
59*6777b538SAndroid Build Coastguard Worker   return file_contents_uint64;
60*6777b538SAndroid Build Coastguard Worker }
61*6777b538SAndroid Build Coastguard Worker #endif
62*6777b538SAndroid Build Coastguard Worker 
63*6777b538SAndroid Build Coastguard Worker // Get the total CPU from a proc stat buffer. Return value is a TimeDelta
64*6777b538SAndroid Build Coastguard Worker // converted from a number of jiffies on success or an error code if parsing
65*6777b538SAndroid Build Coastguard Worker // failed.
ParseTotalCPUTimeFromStats(base::span<const std::string> proc_stats)66*6777b538SAndroid Build Coastguard Worker base::expected<TimeDelta, ProcessCPUUsageError> ParseTotalCPUTimeFromStats(
67*6777b538SAndroid Build Coastguard Worker     base::span<const std::string> proc_stats) {
68*6777b538SAndroid Build Coastguard Worker   const std::optional<int64_t> utime =
69*6777b538SAndroid Build Coastguard Worker       internal::GetProcStatsFieldAsOptionalInt64(proc_stats,
70*6777b538SAndroid Build Coastguard Worker                                                  internal::VM_UTIME);
71*6777b538SAndroid Build Coastguard Worker   if (utime.value_or(-1) < 0) {
72*6777b538SAndroid Build Coastguard Worker     return base::unexpected(ProcessCPUUsageError::kSystemError);
73*6777b538SAndroid Build Coastguard Worker   }
74*6777b538SAndroid Build Coastguard Worker   const std::optional<int64_t> stime =
75*6777b538SAndroid Build Coastguard Worker       internal::GetProcStatsFieldAsOptionalInt64(proc_stats,
76*6777b538SAndroid Build Coastguard Worker                                                  internal::VM_STIME);
77*6777b538SAndroid Build Coastguard Worker   if (stime.value_or(-1) < 0) {
78*6777b538SAndroid Build Coastguard Worker     return base::unexpected(ProcessCPUUsageError::kSystemError);
79*6777b538SAndroid Build Coastguard Worker   }
80*6777b538SAndroid Build Coastguard Worker   const TimeDelta cpu_time = internal::ClockTicksToTimeDelta(
81*6777b538SAndroid Build Coastguard Worker       base::ClampAdd(utime.value(), stime.value()));
82*6777b538SAndroid Build Coastguard Worker   CHECK(!cpu_time.is_negative());
83*6777b538SAndroid Build Coastguard Worker   return base::ok(cpu_time);
84*6777b538SAndroid Build Coastguard Worker }
85*6777b538SAndroid Build Coastguard Worker 
86*6777b538SAndroid Build Coastguard Worker }  // namespace
87*6777b538SAndroid Build Coastguard Worker 
88*6777b538SAndroid Build Coastguard Worker // static
CreateProcessMetrics(ProcessHandle process)89*6777b538SAndroid Build Coastguard Worker std::unique_ptr<ProcessMetrics> ProcessMetrics::CreateProcessMetrics(
90*6777b538SAndroid Build Coastguard Worker     ProcessHandle process) {
91*6777b538SAndroid Build Coastguard Worker   return WrapUnique(new ProcessMetrics(process));
92*6777b538SAndroid Build Coastguard Worker }
93*6777b538SAndroid Build Coastguard Worker 
GetResidentSetSize() const94*6777b538SAndroid Build Coastguard Worker size_t ProcessMetrics::GetResidentSetSize() const {
95*6777b538SAndroid Build Coastguard Worker   return internal::ReadProcStatsAndGetFieldAsSizeT(process_, internal::VM_RSS) *
96*6777b538SAndroid Build Coastguard Worker          checked_cast<size_t>(getpagesize());
97*6777b538SAndroid Build Coastguard Worker }
98*6777b538SAndroid Build Coastguard Worker 
99*6777b538SAndroid Build Coastguard Worker base::expected<TimeDelta, ProcessCPUUsageError>
GetCumulativeCPUUsage()100*6777b538SAndroid Build Coastguard Worker ProcessMetrics::GetCumulativeCPUUsage() {
101*6777b538SAndroid Build Coastguard Worker   std::string buffer;
102*6777b538SAndroid Build Coastguard Worker   std::vector<std::string> proc_stats;
103*6777b538SAndroid Build Coastguard Worker   if (!internal::ReadProcStats(process_, &buffer) ||
104*6777b538SAndroid Build Coastguard Worker       !internal::ParseProcStats(buffer, &proc_stats)) {
105*6777b538SAndroid Build Coastguard Worker     return base::unexpected(ProcessCPUUsageError::kSystemError);
106*6777b538SAndroid Build Coastguard Worker   }
107*6777b538SAndroid Build Coastguard Worker 
108*6777b538SAndroid Build Coastguard Worker   return ParseTotalCPUTimeFromStats(proc_stats);
109*6777b538SAndroid Build Coastguard Worker }
110*6777b538SAndroid Build Coastguard Worker 
GetCumulativeCPUUsagePerThread(CPUUsagePerThread & cpu_per_thread)111*6777b538SAndroid Build Coastguard Worker bool ProcessMetrics::GetCumulativeCPUUsagePerThread(
112*6777b538SAndroid Build Coastguard Worker     CPUUsagePerThread& cpu_per_thread) {
113*6777b538SAndroid Build Coastguard Worker   cpu_per_thread.clear();
114*6777b538SAndroid Build Coastguard Worker 
115*6777b538SAndroid Build Coastguard Worker   internal::ForEachProcessTask(
116*6777b538SAndroid Build Coastguard Worker       process_,
117*6777b538SAndroid Build Coastguard Worker       [&cpu_per_thread](PlatformThreadId tid, const FilePath& task_path) {
118*6777b538SAndroid Build Coastguard Worker         FilePath thread_stat_path = task_path.Append("stat");
119*6777b538SAndroid Build Coastguard Worker 
120*6777b538SAndroid Build Coastguard Worker         std::string buffer;
121*6777b538SAndroid Build Coastguard Worker         std::vector<std::string> proc_stats;
122*6777b538SAndroid Build Coastguard Worker         if (!internal::ReadProcFile(thread_stat_path, &buffer) ||
123*6777b538SAndroid Build Coastguard Worker             !internal::ParseProcStats(buffer, &proc_stats)) {
124*6777b538SAndroid Build Coastguard Worker           return;
125*6777b538SAndroid Build Coastguard Worker         }
126*6777b538SAndroid Build Coastguard Worker 
127*6777b538SAndroid Build Coastguard Worker         const base::expected<TimeDelta, ProcessCPUUsageError> thread_time =
128*6777b538SAndroid Build Coastguard Worker             ParseTotalCPUTimeFromStats(proc_stats);
129*6777b538SAndroid Build Coastguard Worker         if (thread_time.has_value()) {
130*6777b538SAndroid Build Coastguard Worker           cpu_per_thread.emplace_back(tid, thread_time.value());
131*6777b538SAndroid Build Coastguard Worker         }
132*6777b538SAndroid Build Coastguard Worker       });
133*6777b538SAndroid Build Coastguard Worker 
134*6777b538SAndroid Build Coastguard Worker   return !cpu_per_thread.empty();
135*6777b538SAndroid Build Coastguard Worker }
136*6777b538SAndroid Build Coastguard Worker 
137*6777b538SAndroid Build Coastguard Worker #if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_ANDROID)
GetVmSwapBytes() const138*6777b538SAndroid Build Coastguard Worker uint64_t ProcessMetrics::GetVmSwapBytes() const {
139*6777b538SAndroid Build Coastguard Worker   return internal::ReadProcStatusAndGetKbFieldAsSizeT(process_, "VmSwap") *
140*6777b538SAndroid Build Coastguard Worker          1024;
141*6777b538SAndroid Build Coastguard Worker }
142*6777b538SAndroid Build Coastguard Worker 
GetPageFaultCounts(PageFaultCounts * counts) const143*6777b538SAndroid Build Coastguard Worker bool ProcessMetrics::GetPageFaultCounts(PageFaultCounts* counts) const {
144*6777b538SAndroid Build Coastguard Worker   // We are not using internal::ReadStatsFileAndGetFieldAsInt64(), since it
145*6777b538SAndroid Build Coastguard Worker   // would read the file twice, and return inconsistent numbers.
146*6777b538SAndroid Build Coastguard Worker   std::string stats_data;
147*6777b538SAndroid Build Coastguard Worker   if (!internal::ReadProcStats(process_, &stats_data))
148*6777b538SAndroid Build Coastguard Worker     return false;
149*6777b538SAndroid Build Coastguard Worker   std::vector<std::string> proc_stats;
150*6777b538SAndroid Build Coastguard Worker   if (!internal::ParseProcStats(stats_data, &proc_stats))
151*6777b538SAndroid Build Coastguard Worker     return false;
152*6777b538SAndroid Build Coastguard Worker 
153*6777b538SAndroid Build Coastguard Worker   counts->minor =
154*6777b538SAndroid Build Coastguard Worker       internal::GetProcStatsFieldAsInt64(proc_stats, internal::VM_MINFLT);
155*6777b538SAndroid Build Coastguard Worker   counts->major =
156*6777b538SAndroid Build Coastguard Worker       internal::GetProcStatsFieldAsInt64(proc_stats, internal::VM_MAJFLT);
157*6777b538SAndroid Build Coastguard Worker   return true;
158*6777b538SAndroid Build Coastguard Worker }
159*6777b538SAndroid Build Coastguard Worker #endif  // BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) ||
160*6777b538SAndroid Build Coastguard Worker         // BUILDFLAG(IS_ANDROID)
161*6777b538SAndroid Build Coastguard Worker 
GetOpenFdCount() const162*6777b538SAndroid Build Coastguard Worker int ProcessMetrics::GetOpenFdCount() const {
163*6777b538SAndroid Build Coastguard Worker   // Use /proc/<pid>/fd to count the number of entries there.
164*6777b538SAndroid Build Coastguard Worker   FilePath fd_path = internal::GetProcPidDir(process_).Append("fd");
165*6777b538SAndroid Build Coastguard Worker 
166*6777b538SAndroid Build Coastguard Worker   DirReaderPosix dir_reader(fd_path.value().c_str());
167*6777b538SAndroid Build Coastguard Worker   if (!dir_reader.IsValid())
168*6777b538SAndroid Build Coastguard Worker     return -1;
169*6777b538SAndroid Build Coastguard Worker 
170*6777b538SAndroid Build Coastguard Worker   int total_count = 0;
171*6777b538SAndroid Build Coastguard Worker   for (; dir_reader.Next(); ) {
172*6777b538SAndroid Build Coastguard Worker     const char* name = dir_reader.name();
173*6777b538SAndroid Build Coastguard Worker     if (strcmp(name, ".") != 0 && strcmp(name, "..") != 0)
174*6777b538SAndroid Build Coastguard Worker       ++total_count;
175*6777b538SAndroid Build Coastguard Worker   }
176*6777b538SAndroid Build Coastguard Worker 
177*6777b538SAndroid Build Coastguard Worker   return total_count;
178*6777b538SAndroid Build Coastguard Worker }
179*6777b538SAndroid Build Coastguard Worker 
GetOpenFdSoftLimit() const180*6777b538SAndroid Build Coastguard Worker int ProcessMetrics::GetOpenFdSoftLimit() const {
181*6777b538SAndroid Build Coastguard Worker   // Use /proc/<pid>/limits to read the open fd limit.
182*6777b538SAndroid Build Coastguard Worker   FilePath fd_path = internal::GetProcPidDir(process_).Append("limits");
183*6777b538SAndroid Build Coastguard Worker 
184*6777b538SAndroid Build Coastguard Worker   std::string limits_contents;
185*6777b538SAndroid Build Coastguard Worker   if (!ReadFileToStringNonBlocking(fd_path, &limits_contents))
186*6777b538SAndroid Build Coastguard Worker     return -1;
187*6777b538SAndroid Build Coastguard Worker 
188*6777b538SAndroid Build Coastguard Worker   for (const auto& line : SplitStringPiece(
189*6777b538SAndroid Build Coastguard Worker            limits_contents, "\n", KEEP_WHITESPACE, SPLIT_WANT_NONEMPTY)) {
190*6777b538SAndroid Build Coastguard Worker     if (!StartsWith(line, "Max open files"))
191*6777b538SAndroid Build Coastguard Worker       continue;
192*6777b538SAndroid Build Coastguard Worker 
193*6777b538SAndroid Build Coastguard Worker     auto tokens =
194*6777b538SAndroid Build Coastguard Worker         SplitStringPiece(line, " ", TRIM_WHITESPACE, SPLIT_WANT_NONEMPTY);
195*6777b538SAndroid Build Coastguard Worker     if (tokens.size() > 3) {
196*6777b538SAndroid Build Coastguard Worker       int limit = -1;
197*6777b538SAndroid Build Coastguard Worker       if (!StringToInt(tokens[3], &limit))
198*6777b538SAndroid Build Coastguard Worker         return -1;
199*6777b538SAndroid Build Coastguard Worker       return limit;
200*6777b538SAndroid Build Coastguard Worker     }
201*6777b538SAndroid Build Coastguard Worker   }
202*6777b538SAndroid Build Coastguard Worker   return -1;
203*6777b538SAndroid Build Coastguard Worker }
204*6777b538SAndroid Build Coastguard Worker 
205*6777b538SAndroid Build Coastguard Worker #if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_AIX)
ProcessMetrics(ProcessHandle process)206*6777b538SAndroid Build Coastguard Worker ProcessMetrics::ProcessMetrics(ProcessHandle process)
207*6777b538SAndroid Build Coastguard Worker     : process_(process), last_absolute_idle_wakeups_(0) {}
208*6777b538SAndroid Build Coastguard Worker #else
ProcessMetrics(ProcessHandle process)209*6777b538SAndroid Build Coastguard Worker ProcessMetrics::ProcessMetrics(ProcessHandle process) : process_(process) {}
210*6777b538SAndroid Build Coastguard Worker #endif
211*6777b538SAndroid Build Coastguard Worker 
GetSystemCommitCharge()212*6777b538SAndroid Build Coastguard Worker size_t GetSystemCommitCharge() {
213*6777b538SAndroid Build Coastguard Worker   SystemMemoryInfoKB meminfo;
214*6777b538SAndroid Build Coastguard Worker   if (!GetSystemMemoryInfo(&meminfo))
215*6777b538SAndroid Build Coastguard Worker     return 0;
216*6777b538SAndroid Build Coastguard Worker   return GetSystemCommitChargeFromMeminfo(meminfo);
217*6777b538SAndroid Build Coastguard Worker }
218*6777b538SAndroid Build Coastguard Worker 
GetSystemCommitChargeFromMeminfo(const SystemMemoryInfoKB & meminfo)219*6777b538SAndroid Build Coastguard Worker size_t GetSystemCommitChargeFromMeminfo(const SystemMemoryInfoKB& meminfo) {
220*6777b538SAndroid Build Coastguard Worker   // TODO(crbug.com/315988925): This math is incorrect: `cached` can be very
221*6777b538SAndroid Build Coastguard Worker   // large so that `free` + `buffers` + `cached` > `total`. Replace this with a
222*6777b538SAndroid Build Coastguard Worker   // more meaningful metric or remove it. In the meantime, convert underflows to
223*6777b538SAndroid Build Coastguard Worker   // 0 instead of crashing.
224*6777b538SAndroid Build Coastguard Worker   return ClampedNumeric<size_t>(meminfo.total) - meminfo.free -
225*6777b538SAndroid Build Coastguard Worker          meminfo.buffers - meminfo.cached;
226*6777b538SAndroid Build Coastguard Worker }
227*6777b538SAndroid Build Coastguard Worker 
ParseProcStatCPU(std::string_view input)228*6777b538SAndroid Build Coastguard Worker int ParseProcStatCPU(std::string_view input) {
229*6777b538SAndroid Build Coastguard Worker   // |input| may be empty if the process disappeared somehow.
230*6777b538SAndroid Build Coastguard Worker   // e.g. http://crbug.com/145811.
231*6777b538SAndroid Build Coastguard Worker   if (input.empty())
232*6777b538SAndroid Build Coastguard Worker     return -1;
233*6777b538SAndroid Build Coastguard Worker 
234*6777b538SAndroid Build Coastguard Worker   size_t start = input.find_last_of(')');
235*6777b538SAndroid Build Coastguard Worker   if (start == input.npos)
236*6777b538SAndroid Build Coastguard Worker     return -1;
237*6777b538SAndroid Build Coastguard Worker 
238*6777b538SAndroid Build Coastguard Worker   // Number of spaces remaining until reaching utime's index starting after the
239*6777b538SAndroid Build Coastguard Worker   // last ')'.
240*6777b538SAndroid Build Coastguard Worker   int num_spaces_remaining = internal::VM_UTIME - 1;
241*6777b538SAndroid Build Coastguard Worker 
242*6777b538SAndroid Build Coastguard Worker   size_t i = start;
243*6777b538SAndroid Build Coastguard Worker   while ((i = input.find(' ', i + 1)) != input.npos) {
244*6777b538SAndroid Build Coastguard Worker     // Validate the assumption that there aren't any contiguous spaces
245*6777b538SAndroid Build Coastguard Worker     // in |input| before utime.
246*6777b538SAndroid Build Coastguard Worker     DCHECK_NE(input[i - 1], ' ');
247*6777b538SAndroid Build Coastguard Worker     if (--num_spaces_remaining == 0) {
248*6777b538SAndroid Build Coastguard Worker       int utime = 0;
249*6777b538SAndroid Build Coastguard Worker       int stime = 0;
250*6777b538SAndroid Build Coastguard Worker       if (sscanf(&input.data()[i], "%d %d", &utime, &stime) != 2)
251*6777b538SAndroid Build Coastguard Worker         return -1;
252*6777b538SAndroid Build Coastguard Worker 
253*6777b538SAndroid Build Coastguard Worker       return utime + stime;
254*6777b538SAndroid Build Coastguard Worker     }
255*6777b538SAndroid Build Coastguard Worker   }
256*6777b538SAndroid Build Coastguard Worker 
257*6777b538SAndroid Build Coastguard Worker   return -1;
258*6777b538SAndroid Build Coastguard Worker }
259*6777b538SAndroid Build Coastguard Worker 
GetNumberOfThreads(ProcessHandle process)260*6777b538SAndroid Build Coastguard Worker int64_t GetNumberOfThreads(ProcessHandle process) {
261*6777b538SAndroid Build Coastguard Worker   return internal::ReadProcStatsAndGetFieldAsInt64(process,
262*6777b538SAndroid Build Coastguard Worker                                                    internal::VM_NUMTHREADS);
263*6777b538SAndroid Build Coastguard Worker }
264*6777b538SAndroid Build Coastguard Worker 
265*6777b538SAndroid Build Coastguard Worker const char kProcSelfExe[] = "/proc/self/exe";
266*6777b538SAndroid Build Coastguard Worker 
267*6777b538SAndroid Build Coastguard Worker namespace {
268*6777b538SAndroid Build Coastguard Worker 
269*6777b538SAndroid Build Coastguard Worker // The format of /proc/diskstats is:
270*6777b538SAndroid Build Coastguard Worker //  Device major number
271*6777b538SAndroid Build Coastguard Worker //  Device minor number
272*6777b538SAndroid Build Coastguard Worker //  Device name
273*6777b538SAndroid Build Coastguard Worker //  Field  1 -- # of reads completed
274*6777b538SAndroid Build Coastguard Worker //      This is the total number of reads completed successfully.
275*6777b538SAndroid Build Coastguard Worker //  Field  2 -- # of reads merged, field 6 -- # of writes merged
276*6777b538SAndroid Build Coastguard Worker //      Reads and writes which are adjacent to each other may be merged for
277*6777b538SAndroid Build Coastguard Worker //      efficiency.  Thus two 4K reads may become one 8K read before it is
278*6777b538SAndroid Build Coastguard Worker //      ultimately handed to the disk, and so it will be counted (and queued)
279*6777b538SAndroid Build Coastguard Worker //      as only one I/O.  This field lets you know how often this was done.
280*6777b538SAndroid Build Coastguard Worker //  Field  3 -- # of sectors read
281*6777b538SAndroid Build Coastguard Worker //      This is the total number of sectors read successfully.
282*6777b538SAndroid Build Coastguard Worker //  Field  4 -- # of milliseconds spent reading
283*6777b538SAndroid Build Coastguard Worker //      This is the total number of milliseconds spent by all reads (as
284*6777b538SAndroid Build Coastguard Worker //      measured from __make_request() to end_that_request_last()).
285*6777b538SAndroid Build Coastguard Worker //  Field  5 -- # of writes completed
286*6777b538SAndroid Build Coastguard Worker //      This is the total number of writes completed successfully.
287*6777b538SAndroid Build Coastguard Worker //  Field  6 -- # of writes merged
288*6777b538SAndroid Build Coastguard Worker //      See the description of field 2.
289*6777b538SAndroid Build Coastguard Worker //  Field  7 -- # of sectors written
290*6777b538SAndroid Build Coastguard Worker //      This is the total number of sectors written successfully.
291*6777b538SAndroid Build Coastguard Worker //  Field  8 -- # of milliseconds spent writing
292*6777b538SAndroid Build Coastguard Worker //      This is the total number of milliseconds spent by all writes (as
293*6777b538SAndroid Build Coastguard Worker //      measured from __make_request() to end_that_request_last()).
294*6777b538SAndroid Build Coastguard Worker //  Field  9 -- # of I/Os currently in progress
295*6777b538SAndroid Build Coastguard Worker //      The only field that should go to zero. Incremented as requests are
296*6777b538SAndroid Build Coastguard Worker //      given to appropriate struct request_queue and decremented as they
297*6777b538SAndroid Build Coastguard Worker //      finish.
298*6777b538SAndroid Build Coastguard Worker //  Field 10 -- # of milliseconds spent doing I/Os
299*6777b538SAndroid Build Coastguard Worker //      This field increases so long as field 9 is nonzero.
300*6777b538SAndroid Build Coastguard Worker //  Field 11 -- weighted # of milliseconds spent doing I/Os
301*6777b538SAndroid Build Coastguard Worker //      This field is incremented at each I/O start, I/O completion, I/O
302*6777b538SAndroid Build Coastguard Worker //      merge, or read of these stats by the number of I/Os in progress
303*6777b538SAndroid Build Coastguard Worker //      (field 9) times the number of milliseconds spent doing I/O since the
304*6777b538SAndroid Build Coastguard Worker //      last update of this field.  This can provide an easy measure of both
305*6777b538SAndroid Build Coastguard Worker //      I/O completion time and the backlog that may be accumulating.
306*6777b538SAndroid Build Coastguard Worker 
307*6777b538SAndroid Build Coastguard Worker const size_t kDiskDriveName = 2;
308*6777b538SAndroid Build Coastguard Worker const size_t kDiskReads = 3;
309*6777b538SAndroid Build Coastguard Worker const size_t kDiskReadsMerged = 4;
310*6777b538SAndroid Build Coastguard Worker const size_t kDiskSectorsRead = 5;
311*6777b538SAndroid Build Coastguard Worker const size_t kDiskReadTime = 6;
312*6777b538SAndroid Build Coastguard Worker const size_t kDiskWrites = 7;
313*6777b538SAndroid Build Coastguard Worker const size_t kDiskWritesMerged = 8;
314*6777b538SAndroid Build Coastguard Worker const size_t kDiskSectorsWritten = 9;
315*6777b538SAndroid Build Coastguard Worker const size_t kDiskWriteTime = 10;
316*6777b538SAndroid Build Coastguard Worker const size_t kDiskIO = 11;
317*6777b538SAndroid Build Coastguard Worker const size_t kDiskIOTime = 12;
318*6777b538SAndroid Build Coastguard Worker const size_t kDiskWeightedIOTime = 13;
319*6777b538SAndroid Build Coastguard Worker 
320*6777b538SAndroid Build Coastguard Worker }  // namespace
321*6777b538SAndroid Build Coastguard Worker 
ToDict() const322*6777b538SAndroid Build Coastguard Worker Value::Dict SystemMemoryInfoKB::ToDict() const {
323*6777b538SAndroid Build Coastguard Worker   Value::Dict res;
324*6777b538SAndroid Build Coastguard Worker   res.Set("total", total);
325*6777b538SAndroid Build Coastguard Worker   res.Set("free", free);
326*6777b538SAndroid Build Coastguard Worker   res.Set("available", available);
327*6777b538SAndroid Build Coastguard Worker   res.Set("buffers", buffers);
328*6777b538SAndroid Build Coastguard Worker   res.Set("cached", cached);
329*6777b538SAndroid Build Coastguard Worker   res.Set("active_anon", active_anon);
330*6777b538SAndroid Build Coastguard Worker   res.Set("inactive_anon", inactive_anon);
331*6777b538SAndroid Build Coastguard Worker   res.Set("active_file", active_file);
332*6777b538SAndroid Build Coastguard Worker   res.Set("inactive_file", inactive_file);
333*6777b538SAndroid Build Coastguard Worker   res.Set("swap_total", swap_total);
334*6777b538SAndroid Build Coastguard Worker   res.Set("swap_free", swap_free);
335*6777b538SAndroid Build Coastguard Worker   res.Set("swap_used", swap_total - swap_free);
336*6777b538SAndroid Build Coastguard Worker   res.Set("dirty", dirty);
337*6777b538SAndroid Build Coastguard Worker   res.Set("reclaimable", reclaimable);
338*6777b538SAndroid Build Coastguard Worker #if BUILDFLAG(IS_CHROMEOS)
339*6777b538SAndroid Build Coastguard Worker   res.Set("shmem", shmem);
340*6777b538SAndroid Build Coastguard Worker   res.Set("slab", slab);
341*6777b538SAndroid Build Coastguard Worker #endif
342*6777b538SAndroid Build Coastguard Worker 
343*6777b538SAndroid Build Coastguard Worker   return res;
344*6777b538SAndroid Build Coastguard Worker }
345*6777b538SAndroid Build Coastguard Worker 
ParseProcMeminfo(std::string_view meminfo_data,SystemMemoryInfoKB * meminfo)346*6777b538SAndroid Build Coastguard Worker bool ParseProcMeminfo(std::string_view meminfo_data,
347*6777b538SAndroid Build Coastguard Worker                       SystemMemoryInfoKB* meminfo) {
348*6777b538SAndroid Build Coastguard Worker   // The format of /proc/meminfo is:
349*6777b538SAndroid Build Coastguard Worker   //
350*6777b538SAndroid Build Coastguard Worker   // MemTotal:      8235324 kB
351*6777b538SAndroid Build Coastguard Worker   // MemFree:       1628304 kB
352*6777b538SAndroid Build Coastguard Worker   // Buffers:        429596 kB
353*6777b538SAndroid Build Coastguard Worker   // Cached:        4728232 kB
354*6777b538SAndroid Build Coastguard Worker   // ...
355*6777b538SAndroid Build Coastguard Worker   // There is no guarantee on the ordering or position
356*6777b538SAndroid Build Coastguard Worker   // though it doesn't appear to change very often
357*6777b538SAndroid Build Coastguard Worker 
358*6777b538SAndroid Build Coastguard Worker   // As a basic sanity check at the end, make sure the MemTotal value will be at
359*6777b538SAndroid Build Coastguard Worker   // least non-zero. So start off with a zero total.
360*6777b538SAndroid Build Coastguard Worker   meminfo->total = 0;
361*6777b538SAndroid Build Coastguard Worker 
362*6777b538SAndroid Build Coastguard Worker   for (std::string_view line : SplitStringPiece(
363*6777b538SAndroid Build Coastguard Worker            meminfo_data, "\n", KEEP_WHITESPACE, SPLIT_WANT_NONEMPTY)) {
364*6777b538SAndroid Build Coastguard Worker     std::vector<std::string_view> tokens = SplitStringPiece(
365*6777b538SAndroid Build Coastguard Worker         line, kWhitespaceASCII, TRIM_WHITESPACE, SPLIT_WANT_NONEMPTY);
366*6777b538SAndroid Build Coastguard Worker     // HugePages_* only has a number and no suffix so there may not be exactly 3
367*6777b538SAndroid Build Coastguard Worker     // tokens.
368*6777b538SAndroid Build Coastguard Worker     if (tokens.size() <= 1) {
369*6777b538SAndroid Build Coastguard Worker       DLOG(WARNING) << "meminfo: tokens: " << tokens.size()
370*6777b538SAndroid Build Coastguard Worker                     << " malformed line: " << line;
371*6777b538SAndroid Build Coastguard Worker       continue;
372*6777b538SAndroid Build Coastguard Worker     }
373*6777b538SAndroid Build Coastguard Worker 
374*6777b538SAndroid Build Coastguard Worker     int* target = nullptr;
375*6777b538SAndroid Build Coastguard Worker     if (tokens[0] == "MemTotal:")
376*6777b538SAndroid Build Coastguard Worker       target = &meminfo->total;
377*6777b538SAndroid Build Coastguard Worker     else if (tokens[0] == "MemFree:")
378*6777b538SAndroid Build Coastguard Worker       target = &meminfo->free;
379*6777b538SAndroid Build Coastguard Worker     else if (tokens[0] == "MemAvailable:")
380*6777b538SAndroid Build Coastguard Worker       target = &meminfo->available;
381*6777b538SAndroid Build Coastguard Worker     else if (tokens[0] == "Buffers:")
382*6777b538SAndroid Build Coastguard Worker       target = &meminfo->buffers;
383*6777b538SAndroid Build Coastguard Worker     else if (tokens[0] == "Cached:")
384*6777b538SAndroid Build Coastguard Worker       target = &meminfo->cached;
385*6777b538SAndroid Build Coastguard Worker     else if (tokens[0] == "Active(anon):")
386*6777b538SAndroid Build Coastguard Worker       target = &meminfo->active_anon;
387*6777b538SAndroid Build Coastguard Worker     else if (tokens[0] == "Inactive(anon):")
388*6777b538SAndroid Build Coastguard Worker       target = &meminfo->inactive_anon;
389*6777b538SAndroid Build Coastguard Worker     else if (tokens[0] == "Active(file):")
390*6777b538SAndroid Build Coastguard Worker       target = &meminfo->active_file;
391*6777b538SAndroid Build Coastguard Worker     else if (tokens[0] == "Inactive(file):")
392*6777b538SAndroid Build Coastguard Worker       target = &meminfo->inactive_file;
393*6777b538SAndroid Build Coastguard Worker     else if (tokens[0] == "SwapTotal:")
394*6777b538SAndroid Build Coastguard Worker       target = &meminfo->swap_total;
395*6777b538SAndroid Build Coastguard Worker     else if (tokens[0] == "SwapFree:")
396*6777b538SAndroid Build Coastguard Worker       target = &meminfo->swap_free;
397*6777b538SAndroid Build Coastguard Worker     else if (tokens[0] == "Dirty:")
398*6777b538SAndroid Build Coastguard Worker       target = &meminfo->dirty;
399*6777b538SAndroid Build Coastguard Worker     else if (tokens[0] == "SReclaimable:")
400*6777b538SAndroid Build Coastguard Worker       target = &meminfo->reclaimable;
401*6777b538SAndroid Build Coastguard Worker #if BUILDFLAG(IS_CHROMEOS)
402*6777b538SAndroid Build Coastguard Worker     // Chrome OS has a tweaked kernel that allows querying Shmem, which is
403*6777b538SAndroid Build Coastguard Worker     // usually video memory otherwise invisible to the OS.
404*6777b538SAndroid Build Coastguard Worker     else if (tokens[0] == "Shmem:")
405*6777b538SAndroid Build Coastguard Worker       target = &meminfo->shmem;
406*6777b538SAndroid Build Coastguard Worker     else if (tokens[0] == "Slab:")
407*6777b538SAndroid Build Coastguard Worker       target = &meminfo->slab;
408*6777b538SAndroid Build Coastguard Worker #endif
409*6777b538SAndroid Build Coastguard Worker     if (target)
410*6777b538SAndroid Build Coastguard Worker       StringToInt(tokens[1], target);
411*6777b538SAndroid Build Coastguard Worker   }
412*6777b538SAndroid Build Coastguard Worker 
413*6777b538SAndroid Build Coastguard Worker   // Make sure the MemTotal is valid.
414*6777b538SAndroid Build Coastguard Worker   return meminfo->total > 0;
415*6777b538SAndroid Build Coastguard Worker }
416*6777b538SAndroid Build Coastguard Worker 
ParseProcVmstat(std::string_view vmstat_data,VmStatInfo * vmstat)417*6777b538SAndroid Build Coastguard Worker bool ParseProcVmstat(std::string_view vmstat_data, VmStatInfo* vmstat) {
418*6777b538SAndroid Build Coastguard Worker   // The format of /proc/vmstat is:
419*6777b538SAndroid Build Coastguard Worker   //
420*6777b538SAndroid Build Coastguard Worker   // nr_free_pages 299878
421*6777b538SAndroid Build Coastguard Worker   // nr_inactive_anon 239863
422*6777b538SAndroid Build Coastguard Worker   // nr_active_anon 1318966
423*6777b538SAndroid Build Coastguard Worker   // nr_inactive_file 2015629
424*6777b538SAndroid Build Coastguard Worker   // ...
425*6777b538SAndroid Build Coastguard Worker   //
426*6777b538SAndroid Build Coastguard Worker   // Iterate through the whole file because the position of the
427*6777b538SAndroid Build Coastguard Worker   // fields are dependent on the kernel version and configuration.
428*6777b538SAndroid Build Coastguard Worker 
429*6777b538SAndroid Build Coastguard Worker   // Returns true if all of these 3 fields are present.
430*6777b538SAndroid Build Coastguard Worker   bool has_pswpin = false;
431*6777b538SAndroid Build Coastguard Worker   bool has_pswpout = false;
432*6777b538SAndroid Build Coastguard Worker   bool has_pgmajfault = false;
433*6777b538SAndroid Build Coastguard Worker 
434*6777b538SAndroid Build Coastguard Worker   // The oom_kill field is optional. The vmstat oom_kill field is available on
435*6777b538SAndroid Build Coastguard Worker   // upstream kernel 4.13. It's backported to Chrome OS kernel 3.10.
436*6777b538SAndroid Build Coastguard Worker   bool has_oom_kill = false;
437*6777b538SAndroid Build Coastguard Worker   vmstat->oom_kill = 0;
438*6777b538SAndroid Build Coastguard Worker 
439*6777b538SAndroid Build Coastguard Worker   for (std::string_view line : SplitStringPiece(
440*6777b538SAndroid Build Coastguard Worker            vmstat_data, "\n", KEEP_WHITESPACE, SPLIT_WANT_NONEMPTY)) {
441*6777b538SAndroid Build Coastguard Worker     std::vector<std::string_view> tokens =
442*6777b538SAndroid Build Coastguard Worker         SplitStringPiece(line, " ", KEEP_WHITESPACE, SPLIT_WANT_NONEMPTY);
443*6777b538SAndroid Build Coastguard Worker     if (tokens.size() != 2)
444*6777b538SAndroid Build Coastguard Worker       continue;
445*6777b538SAndroid Build Coastguard Worker 
446*6777b538SAndroid Build Coastguard Worker     uint64_t val;
447*6777b538SAndroid Build Coastguard Worker     if (!StringToUint64(tokens[1], &val))
448*6777b538SAndroid Build Coastguard Worker       continue;
449*6777b538SAndroid Build Coastguard Worker 
450*6777b538SAndroid Build Coastguard Worker     if (tokens[0] == "pswpin") {
451*6777b538SAndroid Build Coastguard Worker       vmstat->pswpin = val;
452*6777b538SAndroid Build Coastguard Worker       DCHECK(!has_pswpin);
453*6777b538SAndroid Build Coastguard Worker       has_pswpin = true;
454*6777b538SAndroid Build Coastguard Worker     } else if (tokens[0] == "pswpout") {
455*6777b538SAndroid Build Coastguard Worker       vmstat->pswpout = val;
456*6777b538SAndroid Build Coastguard Worker       DCHECK(!has_pswpout);
457*6777b538SAndroid Build Coastguard Worker       has_pswpout = true;
458*6777b538SAndroid Build Coastguard Worker     } else if (tokens[0] == "pgmajfault") {
459*6777b538SAndroid Build Coastguard Worker       vmstat->pgmajfault = val;
460*6777b538SAndroid Build Coastguard Worker       DCHECK(!has_pgmajfault);
461*6777b538SAndroid Build Coastguard Worker       has_pgmajfault = true;
462*6777b538SAndroid Build Coastguard Worker     } else if (tokens[0] == "oom_kill") {
463*6777b538SAndroid Build Coastguard Worker       vmstat->oom_kill = val;
464*6777b538SAndroid Build Coastguard Worker       DCHECK(!has_oom_kill);
465*6777b538SAndroid Build Coastguard Worker       has_oom_kill = true;
466*6777b538SAndroid Build Coastguard Worker     }
467*6777b538SAndroid Build Coastguard Worker   }
468*6777b538SAndroid Build Coastguard Worker 
469*6777b538SAndroid Build Coastguard Worker   return has_pswpin && has_pswpout && has_pgmajfault;
470*6777b538SAndroid Build Coastguard Worker }
471*6777b538SAndroid Build Coastguard Worker 
GetSystemMemoryInfo(SystemMemoryInfoKB * meminfo)472*6777b538SAndroid Build Coastguard Worker bool GetSystemMemoryInfo(SystemMemoryInfoKB* meminfo) {
473*6777b538SAndroid Build Coastguard Worker   // Used memory is: total - free - buffers - caches
474*6777b538SAndroid Build Coastguard Worker   // ReadFileToStringNonBlocking doesn't require ScopedAllowIO, and reading
475*6777b538SAndroid Build Coastguard Worker   // /proc/meminfo is fast. See crbug.com/1160988 for details.
476*6777b538SAndroid Build Coastguard Worker   FilePath meminfo_file("/proc/meminfo");
477*6777b538SAndroid Build Coastguard Worker   std::string meminfo_data;
478*6777b538SAndroid Build Coastguard Worker   if (!ReadFileToStringNonBlocking(meminfo_file, &meminfo_data)) {
479*6777b538SAndroid Build Coastguard Worker     DLOG(WARNING) << "Failed to open " << meminfo_file.value();
480*6777b538SAndroid Build Coastguard Worker     return false;
481*6777b538SAndroid Build Coastguard Worker   }
482*6777b538SAndroid Build Coastguard Worker 
483*6777b538SAndroid Build Coastguard Worker   if (!ParseProcMeminfo(meminfo_data, meminfo)) {
484*6777b538SAndroid Build Coastguard Worker     DLOG(WARNING) << "Failed to parse " << meminfo_file.value();
485*6777b538SAndroid Build Coastguard Worker     return false;
486*6777b538SAndroid Build Coastguard Worker   }
487*6777b538SAndroid Build Coastguard Worker 
488*6777b538SAndroid Build Coastguard Worker   return true;
489*6777b538SAndroid Build Coastguard Worker }
490*6777b538SAndroid Build Coastguard Worker 
ToDict() const491*6777b538SAndroid Build Coastguard Worker Value::Dict VmStatInfo::ToDict() const {
492*6777b538SAndroid Build Coastguard Worker   Value::Dict res;
493*6777b538SAndroid Build Coastguard Worker   // TODO(crbug.com/1334256): Make base::Value able to hold uint64_t and remove
494*6777b538SAndroid Build Coastguard Worker   // casts below.
495*6777b538SAndroid Build Coastguard Worker   res.Set("pswpin", static_cast<int>(pswpin));
496*6777b538SAndroid Build Coastguard Worker   res.Set("pswpout", static_cast<int>(pswpout));
497*6777b538SAndroid Build Coastguard Worker   res.Set("pgmajfault", static_cast<int>(pgmajfault));
498*6777b538SAndroid Build Coastguard Worker   return res;
499*6777b538SAndroid Build Coastguard Worker }
500*6777b538SAndroid Build Coastguard Worker 
GetVmStatInfo(VmStatInfo * vmstat)501*6777b538SAndroid Build Coastguard Worker bool GetVmStatInfo(VmStatInfo* vmstat) {
502*6777b538SAndroid Build Coastguard Worker   // Synchronously reading files in /proc is safe.
503*6777b538SAndroid Build Coastguard Worker   ScopedAllowBlockingForProcessMetrics allow_blocking;
504*6777b538SAndroid Build Coastguard Worker 
505*6777b538SAndroid Build Coastguard Worker   FilePath vmstat_file("/proc/vmstat");
506*6777b538SAndroid Build Coastguard Worker   std::string vmstat_data;
507*6777b538SAndroid Build Coastguard Worker   if (!ReadFileToStringNonBlocking(vmstat_file, &vmstat_data)) {
508*6777b538SAndroid Build Coastguard Worker     DLOG(WARNING) << "Failed to open " << vmstat_file.value();
509*6777b538SAndroid Build Coastguard Worker     return false;
510*6777b538SAndroid Build Coastguard Worker   }
511*6777b538SAndroid Build Coastguard Worker   if (!ParseProcVmstat(vmstat_data, vmstat)) {
512*6777b538SAndroid Build Coastguard Worker     DLOG(WARNING) << "Failed to parse " << vmstat_file.value();
513*6777b538SAndroid Build Coastguard Worker     return false;
514*6777b538SAndroid Build Coastguard Worker   }
515*6777b538SAndroid Build Coastguard Worker   return true;
516*6777b538SAndroid Build Coastguard Worker }
517*6777b538SAndroid Build Coastguard Worker 
SystemDiskInfo()518*6777b538SAndroid Build Coastguard Worker SystemDiskInfo::SystemDiskInfo() {
519*6777b538SAndroid Build Coastguard Worker   reads = 0;
520*6777b538SAndroid Build Coastguard Worker   reads_merged = 0;
521*6777b538SAndroid Build Coastguard Worker   sectors_read = 0;
522*6777b538SAndroid Build Coastguard Worker   read_time = 0;
523*6777b538SAndroid Build Coastguard Worker   writes = 0;
524*6777b538SAndroid Build Coastguard Worker   writes_merged = 0;
525*6777b538SAndroid Build Coastguard Worker   sectors_written = 0;
526*6777b538SAndroid Build Coastguard Worker   write_time = 0;
527*6777b538SAndroid Build Coastguard Worker   io = 0;
528*6777b538SAndroid Build Coastguard Worker   io_time = 0;
529*6777b538SAndroid Build Coastguard Worker   weighted_io_time = 0;
530*6777b538SAndroid Build Coastguard Worker }
531*6777b538SAndroid Build Coastguard Worker 
532*6777b538SAndroid Build Coastguard Worker SystemDiskInfo::SystemDiskInfo(const SystemDiskInfo&) = default;
533*6777b538SAndroid Build Coastguard Worker 
534*6777b538SAndroid Build Coastguard Worker SystemDiskInfo& SystemDiskInfo::operator=(const SystemDiskInfo&) = default;
535*6777b538SAndroid Build Coastguard Worker 
ToDict() const536*6777b538SAndroid Build Coastguard Worker Value::Dict SystemDiskInfo::ToDict() const {
537*6777b538SAndroid Build Coastguard Worker   Value::Dict res;
538*6777b538SAndroid Build Coastguard Worker 
539*6777b538SAndroid Build Coastguard Worker   // Write out uint64_t variables as doubles.
540*6777b538SAndroid Build Coastguard Worker   // Note: this may discard some precision, but for JS there's no other option.
541*6777b538SAndroid Build Coastguard Worker   res.Set("reads", static_cast<double>(reads));
542*6777b538SAndroid Build Coastguard Worker   res.Set("reads_merged", static_cast<double>(reads_merged));
543*6777b538SAndroid Build Coastguard Worker   res.Set("sectors_read", static_cast<double>(sectors_read));
544*6777b538SAndroid Build Coastguard Worker   res.Set("read_time", static_cast<double>(read_time));
545*6777b538SAndroid Build Coastguard Worker   res.Set("writes", static_cast<double>(writes));
546*6777b538SAndroid Build Coastguard Worker   res.Set("writes_merged", static_cast<double>(writes_merged));
547*6777b538SAndroid Build Coastguard Worker   res.Set("sectors_written", static_cast<double>(sectors_written));
548*6777b538SAndroid Build Coastguard Worker   res.Set("write_time", static_cast<double>(write_time));
549*6777b538SAndroid Build Coastguard Worker   res.Set("io", static_cast<double>(io));
550*6777b538SAndroid Build Coastguard Worker   res.Set("io_time", static_cast<double>(io_time));
551*6777b538SAndroid Build Coastguard Worker   res.Set("weighted_io_time", static_cast<double>(weighted_io_time));
552*6777b538SAndroid Build Coastguard Worker 
553*6777b538SAndroid Build Coastguard Worker   return res;
554*6777b538SAndroid Build Coastguard Worker }
555*6777b538SAndroid Build Coastguard Worker 
IsValidDiskName(std::string_view candidate)556*6777b538SAndroid Build Coastguard Worker bool IsValidDiskName(std::string_view candidate) {
557*6777b538SAndroid Build Coastguard Worker   if (candidate.length() < 3)
558*6777b538SAndroid Build Coastguard Worker     return false;
559*6777b538SAndroid Build Coastguard Worker 
560*6777b538SAndroid Build Coastguard Worker   if (candidate[1] == 'd' &&
561*6777b538SAndroid Build Coastguard Worker       (candidate[0] == 'h' || candidate[0] == 's' || candidate[0] == 'v')) {
562*6777b538SAndroid Build Coastguard Worker     // [hsv]d[a-z]+ case
563*6777b538SAndroid Build Coastguard Worker     for (size_t i = 2; i < candidate.length(); ++i) {
564*6777b538SAndroid Build Coastguard Worker       if (!absl::ascii_islower(static_cast<unsigned char>(candidate[i]))) {
565*6777b538SAndroid Build Coastguard Worker         return false;
566*6777b538SAndroid Build Coastguard Worker       }
567*6777b538SAndroid Build Coastguard Worker     }
568*6777b538SAndroid Build Coastguard Worker     return true;
569*6777b538SAndroid Build Coastguard Worker   }
570*6777b538SAndroid Build Coastguard Worker 
571*6777b538SAndroid Build Coastguard Worker   const char kMMCName[] = "mmcblk";
572*6777b538SAndroid Build Coastguard Worker   if (!StartsWith(candidate, kMMCName))
573*6777b538SAndroid Build Coastguard Worker     return false;
574*6777b538SAndroid Build Coastguard Worker 
575*6777b538SAndroid Build Coastguard Worker   // mmcblk[0-9]+ case
576*6777b538SAndroid Build Coastguard Worker   for (size_t i = strlen(kMMCName); i < candidate.length(); ++i) {
577*6777b538SAndroid Build Coastguard Worker     if (!absl::ascii_isdigit(static_cast<unsigned char>(candidate[i]))) {
578*6777b538SAndroid Build Coastguard Worker       return false;
579*6777b538SAndroid Build Coastguard Worker     }
580*6777b538SAndroid Build Coastguard Worker   }
581*6777b538SAndroid Build Coastguard Worker   return true;
582*6777b538SAndroid Build Coastguard Worker }
583*6777b538SAndroid Build Coastguard Worker 
GetSystemDiskInfo(SystemDiskInfo * diskinfo)584*6777b538SAndroid Build Coastguard Worker bool GetSystemDiskInfo(SystemDiskInfo* diskinfo) {
585*6777b538SAndroid Build Coastguard Worker   // Synchronously reading files in /proc does not hit the disk.
586*6777b538SAndroid Build Coastguard Worker   ScopedAllowBlockingForProcessMetrics allow_blocking;
587*6777b538SAndroid Build Coastguard Worker 
588*6777b538SAndroid Build Coastguard Worker   FilePath diskinfo_file("/proc/diskstats");
589*6777b538SAndroid Build Coastguard Worker   std::string diskinfo_data;
590*6777b538SAndroid Build Coastguard Worker   if (!ReadFileToStringNonBlocking(diskinfo_file, &diskinfo_data)) {
591*6777b538SAndroid Build Coastguard Worker     DLOG(WARNING) << "Failed to open " << diskinfo_file.value();
592*6777b538SAndroid Build Coastguard Worker     return false;
593*6777b538SAndroid Build Coastguard Worker   }
594*6777b538SAndroid Build Coastguard Worker 
595*6777b538SAndroid Build Coastguard Worker   std::vector<std::string_view> diskinfo_lines = SplitStringPiece(
596*6777b538SAndroid Build Coastguard Worker       diskinfo_data, "\n", KEEP_WHITESPACE, SPLIT_WANT_NONEMPTY);
597*6777b538SAndroid Build Coastguard Worker   if (diskinfo_lines.empty()) {
598*6777b538SAndroid Build Coastguard Worker     DLOG(WARNING) << "No lines found";
599*6777b538SAndroid Build Coastguard Worker     return false;
600*6777b538SAndroid Build Coastguard Worker   }
601*6777b538SAndroid Build Coastguard Worker 
602*6777b538SAndroid Build Coastguard Worker   diskinfo->reads = 0;
603*6777b538SAndroid Build Coastguard Worker   diskinfo->reads_merged = 0;
604*6777b538SAndroid Build Coastguard Worker   diskinfo->sectors_read = 0;
605*6777b538SAndroid Build Coastguard Worker   diskinfo->read_time = 0;
606*6777b538SAndroid Build Coastguard Worker   diskinfo->writes = 0;
607*6777b538SAndroid Build Coastguard Worker   diskinfo->writes_merged = 0;
608*6777b538SAndroid Build Coastguard Worker   diskinfo->sectors_written = 0;
609*6777b538SAndroid Build Coastguard Worker   diskinfo->write_time = 0;
610*6777b538SAndroid Build Coastguard Worker   diskinfo->io = 0;
611*6777b538SAndroid Build Coastguard Worker   diskinfo->io_time = 0;
612*6777b538SAndroid Build Coastguard Worker   diskinfo->weighted_io_time = 0;
613*6777b538SAndroid Build Coastguard Worker 
614*6777b538SAndroid Build Coastguard Worker   uint64_t reads = 0;
615*6777b538SAndroid Build Coastguard Worker   uint64_t reads_merged = 0;
616*6777b538SAndroid Build Coastguard Worker   uint64_t sectors_read = 0;
617*6777b538SAndroid Build Coastguard Worker   uint64_t read_time = 0;
618*6777b538SAndroid Build Coastguard Worker   uint64_t writes = 0;
619*6777b538SAndroid Build Coastguard Worker   uint64_t writes_merged = 0;
620*6777b538SAndroid Build Coastguard Worker   uint64_t sectors_written = 0;
621*6777b538SAndroid Build Coastguard Worker   uint64_t write_time = 0;
622*6777b538SAndroid Build Coastguard Worker   uint64_t io = 0;
623*6777b538SAndroid Build Coastguard Worker   uint64_t io_time = 0;
624*6777b538SAndroid Build Coastguard Worker   uint64_t weighted_io_time = 0;
625*6777b538SAndroid Build Coastguard Worker 
626*6777b538SAndroid Build Coastguard Worker   for (std::string_view line : diskinfo_lines) {
627*6777b538SAndroid Build Coastguard Worker     std::vector<std::string_view> disk_fields = SplitStringPiece(
628*6777b538SAndroid Build Coastguard Worker         line, kWhitespaceASCII, TRIM_WHITESPACE, SPLIT_WANT_NONEMPTY);
629*6777b538SAndroid Build Coastguard Worker 
630*6777b538SAndroid Build Coastguard Worker     // Fields may have overflowed and reset to zero.
631*6777b538SAndroid Build Coastguard Worker     if (!IsValidDiskName(disk_fields[kDiskDriveName]))
632*6777b538SAndroid Build Coastguard Worker       continue;
633*6777b538SAndroid Build Coastguard Worker 
634*6777b538SAndroid Build Coastguard Worker     StringToUint64(disk_fields[kDiskReads], &reads);
635*6777b538SAndroid Build Coastguard Worker     StringToUint64(disk_fields[kDiskReadsMerged], &reads_merged);
636*6777b538SAndroid Build Coastguard Worker     StringToUint64(disk_fields[kDiskSectorsRead], &sectors_read);
637*6777b538SAndroid Build Coastguard Worker     StringToUint64(disk_fields[kDiskReadTime], &read_time);
638*6777b538SAndroid Build Coastguard Worker     StringToUint64(disk_fields[kDiskWrites], &writes);
639*6777b538SAndroid Build Coastguard Worker     StringToUint64(disk_fields[kDiskWritesMerged], &writes_merged);
640*6777b538SAndroid Build Coastguard Worker     StringToUint64(disk_fields[kDiskSectorsWritten], &sectors_written);
641*6777b538SAndroid Build Coastguard Worker     StringToUint64(disk_fields[kDiskWriteTime], &write_time);
642*6777b538SAndroid Build Coastguard Worker     StringToUint64(disk_fields[kDiskIO], &io);
643*6777b538SAndroid Build Coastguard Worker     StringToUint64(disk_fields[kDiskIOTime], &io_time);
644*6777b538SAndroid Build Coastguard Worker     StringToUint64(disk_fields[kDiskWeightedIOTime], &weighted_io_time);
645*6777b538SAndroid Build Coastguard Worker 
646*6777b538SAndroid Build Coastguard Worker     diskinfo->reads += reads;
647*6777b538SAndroid Build Coastguard Worker     diskinfo->reads_merged += reads_merged;
648*6777b538SAndroid Build Coastguard Worker     diskinfo->sectors_read += sectors_read;
649*6777b538SAndroid Build Coastguard Worker     diskinfo->read_time += read_time;
650*6777b538SAndroid Build Coastguard Worker     diskinfo->writes += writes;
651*6777b538SAndroid Build Coastguard Worker     diskinfo->writes_merged += writes_merged;
652*6777b538SAndroid Build Coastguard Worker     diskinfo->sectors_written += sectors_written;
653*6777b538SAndroid Build Coastguard Worker     diskinfo->write_time += write_time;
654*6777b538SAndroid Build Coastguard Worker     diskinfo->io += io;
655*6777b538SAndroid Build Coastguard Worker     diskinfo->io_time += io_time;
656*6777b538SAndroid Build Coastguard Worker     diskinfo->weighted_io_time += weighted_io_time;
657*6777b538SAndroid Build Coastguard Worker   }
658*6777b538SAndroid Build Coastguard Worker 
659*6777b538SAndroid Build Coastguard Worker   return true;
660*6777b538SAndroid Build Coastguard Worker }
661*6777b538SAndroid Build Coastguard Worker 
GetUserCpuTimeSinceBoot()662*6777b538SAndroid Build Coastguard Worker TimeDelta GetUserCpuTimeSinceBoot() {
663*6777b538SAndroid Build Coastguard Worker   return internal::GetUserCpuTimeSinceBoot();
664*6777b538SAndroid Build Coastguard Worker }
665*6777b538SAndroid Build Coastguard Worker 
666*6777b538SAndroid Build Coastguard Worker #if BUILDFLAG(IS_CHROMEOS)
ToDict() const667*6777b538SAndroid Build Coastguard Worker Value::Dict SwapInfo::ToDict() const {
668*6777b538SAndroid Build Coastguard Worker   Value::Dict res;
669*6777b538SAndroid Build Coastguard Worker 
670*6777b538SAndroid Build Coastguard Worker   // Write out uint64_t variables as doubles.
671*6777b538SAndroid Build Coastguard Worker   // Note: this may discard some precision, but for JS there's no other option.
672*6777b538SAndroid Build Coastguard Worker   res.Set("num_reads", static_cast<double>(num_reads));
673*6777b538SAndroid Build Coastguard Worker   res.Set("num_writes", static_cast<double>(num_writes));
674*6777b538SAndroid Build Coastguard Worker   res.Set("orig_data_size", static_cast<double>(orig_data_size));
675*6777b538SAndroid Build Coastguard Worker   res.Set("compr_data_size", static_cast<double>(compr_data_size));
676*6777b538SAndroid Build Coastguard Worker   res.Set("mem_used_total", static_cast<double>(mem_used_total));
677*6777b538SAndroid Build Coastguard Worker   double ratio = compr_data_size ? static_cast<double>(orig_data_size) /
678*6777b538SAndroid Build Coastguard Worker                                        static_cast<double>(compr_data_size)
679*6777b538SAndroid Build Coastguard Worker                                  : 0;
680*6777b538SAndroid Build Coastguard Worker   res.Set("compression_ratio", ratio);
681*6777b538SAndroid Build Coastguard Worker 
682*6777b538SAndroid Build Coastguard Worker   return res;
683*6777b538SAndroid Build Coastguard Worker }
684*6777b538SAndroid Build Coastguard Worker 
ToDict() const685*6777b538SAndroid Build Coastguard Worker Value::Dict GraphicsMemoryInfoKB::ToDict() const {
686*6777b538SAndroid Build Coastguard Worker   Value::Dict res;
687*6777b538SAndroid Build Coastguard Worker 
688*6777b538SAndroid Build Coastguard Worker   res.Set("gpu_objects", gpu_objects);
689*6777b538SAndroid Build Coastguard Worker   res.Set("gpu_memory_size", static_cast<double>(gpu_memory_size));
690*6777b538SAndroid Build Coastguard Worker 
691*6777b538SAndroid Build Coastguard Worker   return res;
692*6777b538SAndroid Build Coastguard Worker }
693*6777b538SAndroid Build Coastguard Worker 
ParseZramMmStat(std::string_view mm_stat_data,SwapInfo * swap_info)694*6777b538SAndroid Build Coastguard Worker bool ParseZramMmStat(std::string_view mm_stat_data, SwapInfo* swap_info) {
695*6777b538SAndroid Build Coastguard Worker   // There are 7 columns in /sys/block/zram0/mm_stat,
696*6777b538SAndroid Build Coastguard Worker   // split by several spaces. The first three columns
697*6777b538SAndroid Build Coastguard Worker   // are orig_data_size, compr_data_size and mem_used_total.
698*6777b538SAndroid Build Coastguard Worker   // Example:
699*6777b538SAndroid Build Coastguard Worker   // 17715200 5008166 566062  0 1225715712  127 183842
700*6777b538SAndroid Build Coastguard Worker   //
701*6777b538SAndroid Build Coastguard Worker   // For more details:
702*6777b538SAndroid Build Coastguard Worker   // https://www.kernel.org/doc/Documentation/blockdev/zram.txt
703*6777b538SAndroid Build Coastguard Worker 
704*6777b538SAndroid Build Coastguard Worker   std::vector<std::string_view> tokens = SplitStringPiece(
705*6777b538SAndroid Build Coastguard Worker       mm_stat_data, kWhitespaceASCII, TRIM_WHITESPACE, SPLIT_WANT_NONEMPTY);
706*6777b538SAndroid Build Coastguard Worker   if (tokens.size() < 7) {
707*6777b538SAndroid Build Coastguard Worker     DLOG(WARNING) << "zram mm_stat: tokens: " << tokens.size()
708*6777b538SAndroid Build Coastguard Worker                   << " malformed line: " << mm_stat_data;
709*6777b538SAndroid Build Coastguard Worker     return false;
710*6777b538SAndroid Build Coastguard Worker   }
711*6777b538SAndroid Build Coastguard Worker 
712*6777b538SAndroid Build Coastguard Worker   if (!StringToUint64(tokens[0], &swap_info->orig_data_size))
713*6777b538SAndroid Build Coastguard Worker     return false;
714*6777b538SAndroid Build Coastguard Worker   if (!StringToUint64(tokens[1], &swap_info->compr_data_size))
715*6777b538SAndroid Build Coastguard Worker     return false;
716*6777b538SAndroid Build Coastguard Worker   if (!StringToUint64(tokens[2], &swap_info->mem_used_total))
717*6777b538SAndroid Build Coastguard Worker     return false;
718*6777b538SAndroid Build Coastguard Worker 
719*6777b538SAndroid Build Coastguard Worker   return true;
720*6777b538SAndroid Build Coastguard Worker }
721*6777b538SAndroid Build Coastguard Worker 
ParseZramStat(std::string_view stat_data,SwapInfo * swap_info)722*6777b538SAndroid Build Coastguard Worker bool ParseZramStat(std::string_view stat_data, SwapInfo* swap_info) {
723*6777b538SAndroid Build Coastguard Worker   // There are 11 columns in /sys/block/zram0/stat,
724*6777b538SAndroid Build Coastguard Worker   // split by several spaces. The first column is read I/Os
725*6777b538SAndroid Build Coastguard Worker   // and fifth column is write I/Os.
726*6777b538SAndroid Build Coastguard Worker   // Example:
727*6777b538SAndroid Build Coastguard Worker   // 299    0    2392    0    1    0    8    0    0    0    0
728*6777b538SAndroid Build Coastguard Worker   //
729*6777b538SAndroid Build Coastguard Worker   // For more details:
730*6777b538SAndroid Build Coastguard Worker   // https://www.kernel.org/doc/Documentation/blockdev/zram.txt
731*6777b538SAndroid Build Coastguard Worker 
732*6777b538SAndroid Build Coastguard Worker   std::vector<std::string_view> tokens = SplitStringPiece(
733*6777b538SAndroid Build Coastguard Worker       stat_data, kWhitespaceASCII, TRIM_WHITESPACE, SPLIT_WANT_NONEMPTY);
734*6777b538SAndroid Build Coastguard Worker   if (tokens.size() < 11) {
735*6777b538SAndroid Build Coastguard Worker     DLOG(WARNING) << "zram stat: tokens: " << tokens.size()
736*6777b538SAndroid Build Coastguard Worker                   << " malformed line: " << stat_data;
737*6777b538SAndroid Build Coastguard Worker     return false;
738*6777b538SAndroid Build Coastguard Worker   }
739*6777b538SAndroid Build Coastguard Worker 
740*6777b538SAndroid Build Coastguard Worker   if (!StringToUint64(tokens[0], &swap_info->num_reads))
741*6777b538SAndroid Build Coastguard Worker     return false;
742*6777b538SAndroid Build Coastguard Worker   if (!StringToUint64(tokens[4], &swap_info->num_writes))
743*6777b538SAndroid Build Coastguard Worker     return false;
744*6777b538SAndroid Build Coastguard Worker 
745*6777b538SAndroid Build Coastguard Worker   return true;
746*6777b538SAndroid Build Coastguard Worker }
747*6777b538SAndroid Build Coastguard Worker 
748*6777b538SAndroid Build Coastguard Worker namespace {
749*6777b538SAndroid Build Coastguard Worker 
IgnoreZramFirstPage(uint64_t orig_data_size,SwapInfo * swap_info)750*6777b538SAndroid Build Coastguard Worker bool IgnoreZramFirstPage(uint64_t orig_data_size, SwapInfo* swap_info) {
751*6777b538SAndroid Build Coastguard Worker   if (orig_data_size <= 4096) {
752*6777b538SAndroid Build Coastguard Worker     // A single page is compressed at startup, and has a high compression
753*6777b538SAndroid Build Coastguard Worker     // ratio. Ignore this as it doesn't indicate any real swapping.
754*6777b538SAndroid Build Coastguard Worker     swap_info->orig_data_size = 0;
755*6777b538SAndroid Build Coastguard Worker     swap_info->num_reads = 0;
756*6777b538SAndroid Build Coastguard Worker     swap_info->num_writes = 0;
757*6777b538SAndroid Build Coastguard Worker     swap_info->compr_data_size = 0;
758*6777b538SAndroid Build Coastguard Worker     swap_info->mem_used_total = 0;
759*6777b538SAndroid Build Coastguard Worker     return true;
760*6777b538SAndroid Build Coastguard Worker   }
761*6777b538SAndroid Build Coastguard Worker   return false;
762*6777b538SAndroid Build Coastguard Worker }
763*6777b538SAndroid Build Coastguard Worker 
ParseZramPath(SwapInfo * swap_info)764*6777b538SAndroid Build Coastguard Worker void ParseZramPath(SwapInfo* swap_info) {
765*6777b538SAndroid Build Coastguard Worker   FilePath zram_path("/sys/block/zram0");
766*6777b538SAndroid Build Coastguard Worker   uint64_t orig_data_size =
767*6777b538SAndroid Build Coastguard Worker       ReadFileToUint64(zram_path.Append("orig_data_size"));
768*6777b538SAndroid Build Coastguard Worker   if (IgnoreZramFirstPage(orig_data_size, swap_info))
769*6777b538SAndroid Build Coastguard Worker     return;
770*6777b538SAndroid Build Coastguard Worker 
771*6777b538SAndroid Build Coastguard Worker   swap_info->orig_data_size = orig_data_size;
772*6777b538SAndroid Build Coastguard Worker   swap_info->num_reads = ReadFileToUint64(zram_path.Append("num_reads"));
773*6777b538SAndroid Build Coastguard Worker   swap_info->num_writes = ReadFileToUint64(zram_path.Append("num_writes"));
774*6777b538SAndroid Build Coastguard Worker   swap_info->compr_data_size =
775*6777b538SAndroid Build Coastguard Worker       ReadFileToUint64(zram_path.Append("compr_data_size"));
776*6777b538SAndroid Build Coastguard Worker   swap_info->mem_used_total =
777*6777b538SAndroid Build Coastguard Worker       ReadFileToUint64(zram_path.Append("mem_used_total"));
778*6777b538SAndroid Build Coastguard Worker }
779*6777b538SAndroid Build Coastguard Worker 
GetSwapInfoImpl(SwapInfo * swap_info)780*6777b538SAndroid Build Coastguard Worker bool GetSwapInfoImpl(SwapInfo* swap_info) {
781*6777b538SAndroid Build Coastguard Worker   // Synchronously reading files in /sys/block/zram0 does not hit the disk.
782*6777b538SAndroid Build Coastguard Worker   ScopedAllowBlockingForProcessMetrics allow_blocking;
783*6777b538SAndroid Build Coastguard Worker 
784*6777b538SAndroid Build Coastguard Worker   // Since ZRAM update, it shows the usage data in different places.
785*6777b538SAndroid Build Coastguard Worker   // If file "/sys/block/zram0/mm_stat" exists, use the new way, otherwise,
786*6777b538SAndroid Build Coastguard Worker   // use the old way.
787*6777b538SAndroid Build Coastguard Worker   static std::optional<bool> use_new_zram_interface;
788*6777b538SAndroid Build Coastguard Worker   FilePath zram_mm_stat_file("/sys/block/zram0/mm_stat");
789*6777b538SAndroid Build Coastguard Worker   if (!use_new_zram_interface.has_value()) {
790*6777b538SAndroid Build Coastguard Worker     use_new_zram_interface = PathExists(zram_mm_stat_file);
791*6777b538SAndroid Build Coastguard Worker   }
792*6777b538SAndroid Build Coastguard Worker 
793*6777b538SAndroid Build Coastguard Worker   if (!use_new_zram_interface.value()) {
794*6777b538SAndroid Build Coastguard Worker     ParseZramPath(swap_info);
795*6777b538SAndroid Build Coastguard Worker     return true;
796*6777b538SAndroid Build Coastguard Worker   }
797*6777b538SAndroid Build Coastguard Worker 
798*6777b538SAndroid Build Coastguard Worker   std::string mm_stat_data;
799*6777b538SAndroid Build Coastguard Worker   if (!ReadFileToStringNonBlocking(zram_mm_stat_file, &mm_stat_data)) {
800*6777b538SAndroid Build Coastguard Worker     DLOG(WARNING) << "Failed to open " << zram_mm_stat_file.value();
801*6777b538SAndroid Build Coastguard Worker     return false;
802*6777b538SAndroid Build Coastguard Worker   }
803*6777b538SAndroid Build Coastguard Worker   if (!ParseZramMmStat(mm_stat_data, swap_info)) {
804*6777b538SAndroid Build Coastguard Worker     DLOG(WARNING) << "Failed to parse " << zram_mm_stat_file.value();
805*6777b538SAndroid Build Coastguard Worker     return false;
806*6777b538SAndroid Build Coastguard Worker   }
807*6777b538SAndroid Build Coastguard Worker   if (IgnoreZramFirstPage(swap_info->orig_data_size, swap_info))
808*6777b538SAndroid Build Coastguard Worker     return true;
809*6777b538SAndroid Build Coastguard Worker 
810*6777b538SAndroid Build Coastguard Worker   FilePath zram_stat_file("/sys/block/zram0/stat");
811*6777b538SAndroid Build Coastguard Worker   std::string stat_data;
812*6777b538SAndroid Build Coastguard Worker   if (!ReadFileToStringNonBlocking(zram_stat_file, &stat_data)) {
813*6777b538SAndroid Build Coastguard Worker     DLOG(WARNING) << "Failed to open " << zram_stat_file.value();
814*6777b538SAndroid Build Coastguard Worker     return false;
815*6777b538SAndroid Build Coastguard Worker   }
816*6777b538SAndroid Build Coastguard Worker   if (!ParseZramStat(stat_data, swap_info)) {
817*6777b538SAndroid Build Coastguard Worker     DLOG(WARNING) << "Failed to parse " << zram_stat_file.value();
818*6777b538SAndroid Build Coastguard Worker     return false;
819*6777b538SAndroid Build Coastguard Worker   }
820*6777b538SAndroid Build Coastguard Worker 
821*6777b538SAndroid Build Coastguard Worker   return true;
822*6777b538SAndroid Build Coastguard Worker }
823*6777b538SAndroid Build Coastguard Worker 
824*6777b538SAndroid Build Coastguard Worker }  // namespace
825*6777b538SAndroid Build Coastguard Worker 
GetSwapInfo(SwapInfo * swap_info)826*6777b538SAndroid Build Coastguard Worker bool GetSwapInfo(SwapInfo* swap_info) {
827*6777b538SAndroid Build Coastguard Worker   if (!GetSwapInfoImpl(swap_info)) {
828*6777b538SAndroid Build Coastguard Worker     *swap_info = SwapInfo();
829*6777b538SAndroid Build Coastguard Worker     return false;
830*6777b538SAndroid Build Coastguard Worker   }
831*6777b538SAndroid Build Coastguard Worker   return true;
832*6777b538SAndroid Build Coastguard Worker }
833*6777b538SAndroid Build Coastguard Worker 
834*6777b538SAndroid Build Coastguard Worker namespace {
835*6777b538SAndroid Build Coastguard Worker 
ParseSize(const std::string & value)836*6777b538SAndroid Build Coastguard Worker size_t ParseSize(const std::string& value) {
837*6777b538SAndroid Build Coastguard Worker   size_t pos = value.find(' ');
838*6777b538SAndroid Build Coastguard Worker   std::string base = value.substr(0, pos);
839*6777b538SAndroid Build Coastguard Worker   std::string units = value.substr(pos + 1);
840*6777b538SAndroid Build Coastguard Worker 
841*6777b538SAndroid Build Coastguard Worker   size_t ret = 0;
842*6777b538SAndroid Build Coastguard Worker 
843*6777b538SAndroid Build Coastguard Worker   base::StringToSizeT(base, &ret);
844*6777b538SAndroid Build Coastguard Worker 
845*6777b538SAndroid Build Coastguard Worker   if (units == "KiB") {
846*6777b538SAndroid Build Coastguard Worker     ret *= 1024;
847*6777b538SAndroid Build Coastguard Worker   } else if (units == "MiB") {
848*6777b538SAndroid Build Coastguard Worker     ret *= 1024 * 1024;
849*6777b538SAndroid Build Coastguard Worker   }
850*6777b538SAndroid Build Coastguard Worker 
851*6777b538SAndroid Build Coastguard Worker   return ret;
852*6777b538SAndroid Build Coastguard Worker }
853*6777b538SAndroid Build Coastguard Worker 
854*6777b538SAndroid Build Coastguard Worker struct DrmFdInfo {
855*6777b538SAndroid Build Coastguard Worker   size_t memory_total;
856*6777b538SAndroid Build Coastguard Worker   size_t memory_shared;
857*6777b538SAndroid Build Coastguard Worker };
858*6777b538SAndroid Build Coastguard Worker 
GetFdInfoFromPid(pid_t pid,std::map<unsigned int,struct DrmFdInfo> & fdinfo_table)859*6777b538SAndroid Build Coastguard Worker void GetFdInfoFromPid(pid_t pid,
860*6777b538SAndroid Build Coastguard Worker                       std::map<unsigned int, struct DrmFdInfo>& fdinfo_table) {
861*6777b538SAndroid Build Coastguard Worker   const FilePath pid_path =
862*6777b538SAndroid Build Coastguard Worker       FilePath("/proc").AppendASCII(base::NumberToString(pid));
863*6777b538SAndroid Build Coastguard Worker   const FilePath fd_path = pid_path.AppendASCII("fd");
864*6777b538SAndroid Build Coastguard Worker   DirReaderPosix dir_reader(fd_path.value().c_str());
865*6777b538SAndroid Build Coastguard Worker 
866*6777b538SAndroid Build Coastguard Worker   if (!dir_reader.IsValid()) {
867*6777b538SAndroid Build Coastguard Worker     return;
868*6777b538SAndroid Build Coastguard Worker   }
869*6777b538SAndroid Build Coastguard Worker 
870*6777b538SAndroid Build Coastguard Worker   for (; dir_reader.Next();) {
871*6777b538SAndroid Build Coastguard Worker     const char* name = dir_reader.name();
872*6777b538SAndroid Build Coastguard Worker 
873*6777b538SAndroid Build Coastguard Worker     if (strcmp(name, ".") == 0 || strcmp(name, "..") == 0) {
874*6777b538SAndroid Build Coastguard Worker       continue;
875*6777b538SAndroid Build Coastguard Worker     }
876*6777b538SAndroid Build Coastguard Worker 
877*6777b538SAndroid Build Coastguard Worker     struct stat stat;
878*6777b538SAndroid Build Coastguard Worker     int err = fstatat(dir_reader.fd(), name, &stat, 0);
879*6777b538SAndroid Build Coastguard Worker     if (err) {
880*6777b538SAndroid Build Coastguard Worker       continue;
881*6777b538SAndroid Build Coastguard Worker     }
882*6777b538SAndroid Build Coastguard Worker 
883*6777b538SAndroid Build Coastguard Worker     /* Skip fd's that are not drm device files: */
884*6777b538SAndroid Build Coastguard Worker     if (!S_ISCHR(stat.st_mode) || major(stat.st_rdev) != 226) {
885*6777b538SAndroid Build Coastguard Worker       continue;
886*6777b538SAndroid Build Coastguard Worker     }
887*6777b538SAndroid Build Coastguard Worker 
888*6777b538SAndroid Build Coastguard Worker     const FilePath fdinfo_path =
889*6777b538SAndroid Build Coastguard Worker         pid_path.AppendASCII("fdinfo").AppendASCII(name);
890*6777b538SAndroid Build Coastguard Worker 
891*6777b538SAndroid Build Coastguard Worker     std::string fdinfo_data;
892*6777b538SAndroid Build Coastguard Worker     if (!ReadFileToStringNonBlocking(fdinfo_path, &fdinfo_data)) {
893*6777b538SAndroid Build Coastguard Worker       continue;
894*6777b538SAndroid Build Coastguard Worker     }
895*6777b538SAndroid Build Coastguard Worker 
896*6777b538SAndroid Build Coastguard Worker     std::stringstream ss(fdinfo_data);
897*6777b538SAndroid Build Coastguard Worker     std::string line;
898*6777b538SAndroid Build Coastguard Worker     struct DrmFdInfo fdinfo = {};
899*6777b538SAndroid Build Coastguard Worker     unsigned int client_id = 0;
900*6777b538SAndroid Build Coastguard Worker 
901*6777b538SAndroid Build Coastguard Worker     while (std::getline(ss, line, '\n')) {
902*6777b538SAndroid Build Coastguard Worker       size_t pos = line.find(':');
903*6777b538SAndroid Build Coastguard Worker 
904*6777b538SAndroid Build Coastguard Worker       if (pos == std::string::npos) {
905*6777b538SAndroid Build Coastguard Worker         continue;
906*6777b538SAndroid Build Coastguard Worker       }
907*6777b538SAndroid Build Coastguard Worker 
908*6777b538SAndroid Build Coastguard Worker       std::string key = line.substr(0, pos);
909*6777b538SAndroid Build Coastguard Worker       std::string value = line.substr(pos + 1);
910*6777b538SAndroid Build Coastguard Worker 
911*6777b538SAndroid Build Coastguard Worker       /* trim leading space from the value: */
912*6777b538SAndroid Build Coastguard Worker       value = value.substr(value.find_first_not_of(" \t"));
913*6777b538SAndroid Build Coastguard Worker 
914*6777b538SAndroid Build Coastguard Worker       if (key == "drm-client-id") {
915*6777b538SAndroid Build Coastguard Worker         base::StringToUint(value, &client_id);
916*6777b538SAndroid Build Coastguard Worker       } else if (key == "drm-total-memory") {
917*6777b538SAndroid Build Coastguard Worker         fdinfo.memory_total = ParseSize(value);
918*6777b538SAndroid Build Coastguard Worker       } else if (key == "drm-shared-memory") {
919*6777b538SAndroid Build Coastguard Worker         fdinfo.memory_shared = ParseSize(value);
920*6777b538SAndroid Build Coastguard Worker       }
921*6777b538SAndroid Build Coastguard Worker     }
922*6777b538SAndroid Build Coastguard Worker 
923*6777b538SAndroid Build Coastguard Worker     /* The compositor only imports buffers.. so shared==total.  Skip this
924*6777b538SAndroid Build Coastguard Worker      * as it is not interesting:
925*6777b538SAndroid Build Coastguard Worker      */
926*6777b538SAndroid Build Coastguard Worker     if (client_id && fdinfo.memory_shared != fdinfo.memory_total) {
927*6777b538SAndroid Build Coastguard Worker       fdinfo_table[client_id] = fdinfo;
928*6777b538SAndroid Build Coastguard Worker     }
929*6777b538SAndroid Build Coastguard Worker   }
930*6777b538SAndroid Build Coastguard Worker }
931*6777b538SAndroid Build Coastguard Worker 
GetGraphicsMemoryInfoFdInfo(GraphicsMemoryInfoKB * gpu_meminfo)932*6777b538SAndroid Build Coastguard Worker bool GetGraphicsMemoryInfoFdInfo(GraphicsMemoryInfoKB* gpu_meminfo) {
933*6777b538SAndroid Build Coastguard Worker   // First parse clients file to get the tgid's of processes using the GPU
934*6777b538SAndroid Build Coastguard Worker   // so that we don't need to parse *all* processes:
935*6777b538SAndroid Build Coastguard Worker   const FilePath clients_path("/run/debugfs_gpu/clients");
936*6777b538SAndroid Build Coastguard Worker   std::string clients_data;
937*6777b538SAndroid Build Coastguard Worker   std::map<unsigned int, struct DrmFdInfo> fdinfo_table;
938*6777b538SAndroid Build Coastguard Worker 
939*6777b538SAndroid Build Coastguard Worker   if (!ReadFileToStringNonBlocking(clients_path, &clients_data)) {
940*6777b538SAndroid Build Coastguard Worker     return false;
941*6777b538SAndroid Build Coastguard Worker   }
942*6777b538SAndroid Build Coastguard Worker 
943*6777b538SAndroid Build Coastguard Worker   // This has been the format since kernel commit:
944*6777b538SAndroid Build Coastguard Worker   // 50d47cb318ed ("drm: Include task->name and master status in debugfs clients
945*6777b538SAndroid Build Coastguard Worker   // info")
946*6777b538SAndroid Build Coastguard Worker   //
947*6777b538SAndroid Build Coastguard Worker   // comm pid dev  master auth uid magic
948*6777b538SAndroid Build Coastguard Worker   // %20s %5d %3d   %c    %c %5d %10u\n
949*6777b538SAndroid Build Coastguard Worker   //
950*6777b538SAndroid Build Coastguard Worker   // In practice comm rarely contains spaces, but it can in fact contain
951*6777b538SAndroid Build Coastguard Worker   // any character.  So we parse based on the 20 char limit (plus one
952*6777b538SAndroid Build Coastguard Worker   // space):
953*6777b538SAndroid Build Coastguard Worker   std::istringstream clients_stream(clients_data);
954*6777b538SAndroid Build Coastguard Worker   std::string line;
955*6777b538SAndroid Build Coastguard Worker   while (std::getline(clients_stream, line)) {
956*6777b538SAndroid Build Coastguard Worker     pid_t pid;
957*6777b538SAndroid Build Coastguard Worker     int num_res = sscanf(&line.c_str()[21], "%5d", &pid);
958*6777b538SAndroid Build Coastguard Worker     if (num_res == 1) {
959*6777b538SAndroid Build Coastguard Worker       GetFdInfoFromPid(pid, fdinfo_table);
960*6777b538SAndroid Build Coastguard Worker     }
961*6777b538SAndroid Build Coastguard Worker   }
962*6777b538SAndroid Build Coastguard Worker 
963*6777b538SAndroid Build Coastguard Worker   if (fdinfo_table.size() == 0) {
964*6777b538SAndroid Build Coastguard Worker     return false;
965*6777b538SAndroid Build Coastguard Worker   }
966*6777b538SAndroid Build Coastguard Worker 
967*6777b538SAndroid Build Coastguard Worker   gpu_meminfo->gpu_memory_size = 0;
968*6777b538SAndroid Build Coastguard Worker 
969*6777b538SAndroid Build Coastguard Worker   for (auto const& p : fdinfo_table) {
970*6777b538SAndroid Build Coastguard Worker     gpu_meminfo->gpu_memory_size += p.second.memory_total;
971*6777b538SAndroid Build Coastguard Worker     /* TODO it would be nice to also be able to report shared */
972*6777b538SAndroid Build Coastguard Worker   }
973*6777b538SAndroid Build Coastguard Worker 
974*6777b538SAndroid Build Coastguard Worker   return true;
975*6777b538SAndroid Build Coastguard Worker }
976*6777b538SAndroid Build Coastguard Worker 
977*6777b538SAndroid Build Coastguard Worker }  // namespace
978*6777b538SAndroid Build Coastguard Worker 
GetGraphicsMemoryInfo(GraphicsMemoryInfoKB * gpu_meminfo)979*6777b538SAndroid Build Coastguard Worker bool GetGraphicsMemoryInfo(GraphicsMemoryInfoKB* gpu_meminfo) {
980*6777b538SAndroid Build Coastguard Worker   if (GetGraphicsMemoryInfoFdInfo(gpu_meminfo)) {
981*6777b538SAndroid Build Coastguard Worker     return true;
982*6777b538SAndroid Build Coastguard Worker   }
983*6777b538SAndroid Build Coastguard Worker #if defined(ARCH_CPU_X86_FAMILY)
984*6777b538SAndroid Build Coastguard Worker   // Reading i915_gem_objects on intel platform with kernel 5.4 is slow and is
985*6777b538SAndroid Build Coastguard Worker   // prohibited.
986*6777b538SAndroid Build Coastguard Worker   // TODO(b/170397975): Update if i915_gem_objects reading time is improved.
987*6777b538SAndroid Build Coastguard Worker   static bool is_newer_kernel =
988*6777b538SAndroid Build Coastguard Worker       base::StartsWith(base::SysInfo::KernelVersion(), "5.");
989*6777b538SAndroid Build Coastguard Worker   static bool is_intel_cpu = base::CPU().vendor_name() == "GenuineIntel";
990*6777b538SAndroid Build Coastguard Worker   if (is_newer_kernel && is_intel_cpu)
991*6777b538SAndroid Build Coastguard Worker     return false;
992*6777b538SAndroid Build Coastguard Worker #endif
993*6777b538SAndroid Build Coastguard Worker 
994*6777b538SAndroid Build Coastguard Worker #if defined(ARCH_CPU_ARM_FAMILY)
995*6777b538SAndroid Build Coastguard Worker   const FilePath geminfo_path("/run/debugfs_gpu/exynos_gem_objects");
996*6777b538SAndroid Build Coastguard Worker #else
997*6777b538SAndroid Build Coastguard Worker   const FilePath geminfo_path("/run/debugfs_gpu/i915_gem_objects");
998*6777b538SAndroid Build Coastguard Worker #endif
999*6777b538SAndroid Build Coastguard Worker   std::string geminfo_data;
1000*6777b538SAndroid Build Coastguard Worker   gpu_meminfo->gpu_objects = -1;
1001*6777b538SAndroid Build Coastguard Worker   gpu_meminfo->gpu_memory_size = -1;
1002*6777b538SAndroid Build Coastguard Worker   if (ReadFileToStringNonBlocking(geminfo_path, &geminfo_data)) {
1003*6777b538SAndroid Build Coastguard Worker     int gpu_objects = -1;
1004*6777b538SAndroid Build Coastguard Worker     int64_t gpu_memory_size = -1;
1005*6777b538SAndroid Build Coastguard Worker     int num_res = sscanf(geminfo_data.c_str(), "%d objects, %" SCNd64 " bytes",
1006*6777b538SAndroid Build Coastguard Worker                          &gpu_objects, &gpu_memory_size);
1007*6777b538SAndroid Build Coastguard Worker     if (num_res == 2) {
1008*6777b538SAndroid Build Coastguard Worker       gpu_meminfo->gpu_objects = gpu_objects;
1009*6777b538SAndroid Build Coastguard Worker       gpu_meminfo->gpu_memory_size = gpu_memory_size;
1010*6777b538SAndroid Build Coastguard Worker     }
1011*6777b538SAndroid Build Coastguard Worker   }
1012*6777b538SAndroid Build Coastguard Worker 
1013*6777b538SAndroid Build Coastguard Worker #if defined(ARCH_CPU_ARM_FAMILY)
1014*6777b538SAndroid Build Coastguard Worker   // Incorporate Mali graphics memory if present.
1015*6777b538SAndroid Build Coastguard Worker   FilePath mali_memory_file("/sys/class/misc/mali0/device/memory");
1016*6777b538SAndroid Build Coastguard Worker   std::string mali_memory_data;
1017*6777b538SAndroid Build Coastguard Worker   if (ReadFileToStringNonBlocking(mali_memory_file, &mali_memory_data)) {
1018*6777b538SAndroid Build Coastguard Worker     int64_t mali_size = -1;
1019*6777b538SAndroid Build Coastguard Worker     int num_res =
1020*6777b538SAndroid Build Coastguard Worker         sscanf(mali_memory_data.c_str(), "%" SCNd64 " bytes", &mali_size);
1021*6777b538SAndroid Build Coastguard Worker     if (num_res == 1)
1022*6777b538SAndroid Build Coastguard Worker       gpu_meminfo->gpu_memory_size += mali_size;
1023*6777b538SAndroid Build Coastguard Worker   }
1024*6777b538SAndroid Build Coastguard Worker #endif  // defined(ARCH_CPU_ARM_FAMILY)
1025*6777b538SAndroid Build Coastguard Worker 
1026*6777b538SAndroid Build Coastguard Worker   return gpu_meminfo->gpu_memory_size != -1;
1027*6777b538SAndroid Build Coastguard Worker }
1028*6777b538SAndroid Build Coastguard Worker 
1029*6777b538SAndroid Build Coastguard Worker #endif  // BUILDFLAG(IS_CHROMEOS)
1030*6777b538SAndroid Build Coastguard Worker 
1031*6777b538SAndroid Build Coastguard Worker #if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_AIX)
GetIdleWakeupsPerSecond()1032*6777b538SAndroid Build Coastguard Worker int ProcessMetrics::GetIdleWakeupsPerSecond() {
1033*6777b538SAndroid Build Coastguard Worker   uint64_t num_switches;
1034*6777b538SAndroid Build Coastguard Worker   static const char kSwitchStat[] = "voluntary_ctxt_switches";
1035*6777b538SAndroid Build Coastguard Worker   return internal::ReadProcStatusAndGetFieldAsUint64(process_, kSwitchStat,
1036*6777b538SAndroid Build Coastguard Worker                                                      &num_switches)
1037*6777b538SAndroid Build Coastguard Worker              ? CalculateIdleWakeupsPerSecond(num_switches)
1038*6777b538SAndroid Build Coastguard Worker              : 0;
1039*6777b538SAndroid Build Coastguard Worker }
1040*6777b538SAndroid Build Coastguard Worker #endif  // BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_AIX)
1041*6777b538SAndroid Build Coastguard Worker 
1042*6777b538SAndroid Build Coastguard Worker }  // namespace base
1043