• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright (c) 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "base/process/process_metrics.h"
6 
7 #include <dirent.h>
8 #include <fcntl.h>
9 #include <stddef.h>
10 #include <stdint.h>
11 #include <sys/stat.h>
12 #include <sys/time.h>
13 #include <sys/types.h>
14 #include <unistd.h>
15 #include <utility>
16 
17 #include "base/files/dir_reader_posix.h"
18 #include "base/files/file_util.h"
19 #include "base/logging.h"
20 #include "base/memory/ptr_util.h"
21 #include "base/optional.h"
22 #include "base/process/internal_linux.h"
23 #include "base/process/process_metrics_iocounters.h"
24 #include "base/strings/string_number_conversions.h"
25 #include "base/strings/string_split.h"
26 #include "base/strings/string_tokenizer.h"
27 #include "base/strings/string_util.h"
28 #include "base/threading/thread_restrictions.h"
29 #include "build/build_config.h"
30 
31 namespace base {
32 
33 namespace {
34 
TrimKeyValuePairs(StringPairs * pairs)35 void TrimKeyValuePairs(StringPairs* pairs) {
36   for (auto& pair : *pairs) {
37     TrimWhitespaceASCII(pair.first, TRIM_ALL, &pair.first);
38     TrimWhitespaceASCII(pair.second, TRIM_ALL, &pair.second);
39   }
40 }
41 
42 #if defined(OS_CHROMEOS)
43 // Read a file with a single number string and return the number as a uint64_t.
ReadFileToUint64(const FilePath & file)44 uint64_t ReadFileToUint64(const FilePath& file) {
45   std::string file_contents;
46   if (!ReadFileToString(file, &file_contents))
47     return 0;
48   TrimWhitespaceASCII(file_contents, TRIM_ALL, &file_contents);
49   uint64_t file_contents_uint64 = 0;
50   if (!StringToUint64(file_contents, &file_contents_uint64))
51     return 0;
52   return file_contents_uint64;
53 }
54 #endif
55 
56 // Read |filename| in /proc/<pid>/, split the entries into key/value pairs, and
57 // trim the key and value. On success, return true and write the trimmed
58 // key/value pairs into |key_value_pairs|.
ReadProcFileToTrimmedStringPairs(pid_t pid,StringPiece filename,StringPairs * key_value_pairs)59 bool ReadProcFileToTrimmedStringPairs(pid_t pid,
60                                       StringPiece filename,
61                                       StringPairs* key_value_pairs) {
62   std::string status_data;
63   {
64     // Synchronously reading files in /proc does not hit the disk.
65     ThreadRestrictions::ScopedAllowIO allow_io;
66     FilePath status_file = internal::GetProcPidDir(pid).Append(filename);
67     if (!ReadFileToString(status_file, &status_data))
68       return false;
69   }
70   SplitStringIntoKeyValuePairs(status_data, ':', '\n', key_value_pairs);
71   TrimKeyValuePairs(key_value_pairs);
72   return true;
73 }
74 
75 // Read /proc/<pid>/status and return the value for |field|, or 0 on failure.
76 // Only works for fields in the form of "Field: value kB".
ReadProcStatusAndGetFieldAsSizeT(pid_t pid,StringPiece field)77 size_t ReadProcStatusAndGetFieldAsSizeT(pid_t pid, StringPiece field) {
78   StringPairs pairs;
79   if (!ReadProcFileToTrimmedStringPairs(pid, "status", &pairs))
80     return 0;
81 
82   for (const auto& pair : pairs) {
83     const std::string& key = pair.first;
84     const std::string& value_str = pair.second;
85     if (key != field)
86       continue;
87 
88     std::vector<StringPiece> split_value_str =
89         SplitStringPiece(value_str, " ", TRIM_WHITESPACE, SPLIT_WANT_ALL);
90     if (split_value_str.size() != 2 || split_value_str[1] != "kB") {
91       NOTREACHED();
92       return 0;
93     }
94     size_t value;
95     if (!StringToSizeT(split_value_str[0], &value)) {
96       NOTREACHED();
97       return 0;
98     }
99     return value;
100   }
101   // This can be reached if the process dies when proc is read -- in that case,
102   // the kernel can return missing fields.
103   return 0;
104 }
105 
106 #if defined(OS_LINUX) || defined(OS_AIX)
107 // Read /proc/<pid>/status and look for |field|. On success, return true and
108 // write the value for |field| into |result|.
109 // Only works for fields in the form of "field    :     uint_value"
ReadProcStatusAndGetFieldAsUint64(pid_t pid,StringPiece field,uint64_t * result)110 bool ReadProcStatusAndGetFieldAsUint64(pid_t pid,
111                                        StringPiece field,
112                                        uint64_t* result) {
113   StringPairs pairs;
114   if (!ReadProcFileToTrimmedStringPairs(pid, "status", &pairs))
115     return false;
116 
117   for (const auto& pair : pairs) {
118     const std::string& key = pair.first;
119     const std::string& value_str = pair.second;
120     if (key != field)
121       continue;
122 
123     uint64_t value;
124     if (!StringToUint64(value_str, &value))
125       return false;
126     *result = value;
127     return true;
128   }
129   return false;
130 }
131 #endif  // defined(OS_LINUX) || defined(OS_AIX)
132 
133 // Get the total CPU of a single process.  Return value is number of jiffies
134 // on success or -1 on error.
GetProcessCPU(pid_t pid)135 int64_t GetProcessCPU(pid_t pid) {
136   std::string buffer;
137   std::vector<std::string> proc_stats;
138   if (!internal::ReadProcStats(pid, &buffer) ||
139       !internal::ParseProcStats(buffer, &proc_stats)) {
140     return -1;
141   }
142 
143   int64_t total_cpu =
144       internal::GetProcStatsFieldAsInt64(proc_stats, internal::VM_UTIME) +
145       internal::GetProcStatsFieldAsInt64(proc_stats, internal::VM_STIME);
146 
147   return total_cpu;
148 }
149 
150 #if defined(OS_CHROMEOS)
151 // Report on Chrome OS GEM object graphics memory. /run/debugfs_gpu is a
152 // bind mount into /sys/kernel/debug and synchronously reading the in-memory
153 // files in /sys is fast.
ReadChromeOSGraphicsMemory(SystemMemoryInfoKB * meminfo)154 void ReadChromeOSGraphicsMemory(SystemMemoryInfoKB* meminfo) {
155 #if defined(ARCH_CPU_ARM_FAMILY)
156   FilePath geminfo_file("/run/debugfs_gpu/exynos_gem_objects");
157 #else
158   FilePath geminfo_file("/run/debugfs_gpu/i915_gem_objects");
159 #endif
160   std::string geminfo_data;
161   meminfo->gem_objects = -1;
162   meminfo->gem_size = -1;
163   if (ReadFileToString(geminfo_file, &geminfo_data)) {
164     int gem_objects = -1;
165     long long gem_size = -1;
166     int num_res = sscanf(geminfo_data.c_str(), "%d objects, %lld bytes",
167                          &gem_objects, &gem_size);
168     if (num_res == 2) {
169       meminfo->gem_objects = gem_objects;
170       meminfo->gem_size = gem_size;
171     }
172   }
173 
174 #if defined(ARCH_CPU_ARM_FAMILY)
175   // Incorporate Mali graphics memory if present.
176   FilePath mali_memory_file("/sys/class/misc/mali0/device/memory");
177   std::string mali_memory_data;
178   if (ReadFileToString(mali_memory_file, &mali_memory_data)) {
179     long long mali_size = -1;
180     int num_res = sscanf(mali_memory_data.c_str(), "%lld bytes", &mali_size);
181     if (num_res == 1)
182       meminfo->gem_size += mali_size;
183   }
184 #endif  // defined(ARCH_CPU_ARM_FAMILY)
185 }
186 #endif  // defined(OS_CHROMEOS)
187 
188 }  // namespace
189 
190 // static
CreateProcessMetrics(ProcessHandle process)191 std::unique_ptr<ProcessMetrics> ProcessMetrics::CreateProcessMetrics(
192     ProcessHandle process) {
193   return WrapUnique(new ProcessMetrics(process));
194 }
195 
GetResidentSetSize() const196 size_t ProcessMetrics::GetResidentSetSize() const {
197   return internal::ReadProcStatsAndGetFieldAsSizeT(process_, internal::VM_RSS) *
198       getpagesize();
199 }
200 
GetCumulativeCPUUsage()201 TimeDelta ProcessMetrics::GetCumulativeCPUUsage() {
202   return internal::ClockTicksToTimeDelta(GetProcessCPU(process_));
203 }
204 
205 // For the /proc/self/io file to exist, the Linux kernel must have
206 // CONFIG_TASK_IO_ACCOUNTING enabled.
GetIOCounters(IoCounters * io_counters) const207 bool ProcessMetrics::GetIOCounters(IoCounters* io_counters) const {
208   StringPairs pairs;
209   if (!ReadProcFileToTrimmedStringPairs(process_, "io", &pairs))
210     return false;
211 
212   io_counters->OtherOperationCount = 0;
213   io_counters->OtherTransferCount = 0;
214 
215   for (const auto& pair : pairs) {
216     const std::string& key = pair.first;
217     const std::string& value_str = pair.second;
218     uint64_t* target_counter = nullptr;
219     if (key == "syscr")
220       target_counter = &io_counters->ReadOperationCount;
221     else if (key == "syscw")
222       target_counter = &io_counters->WriteOperationCount;
223     else if (key == "rchar")
224       target_counter = &io_counters->ReadTransferCount;
225     else if (key == "wchar")
226       target_counter = &io_counters->WriteTransferCount;
227     if (!target_counter)
228       continue;
229     bool converted = StringToUint64(value_str, target_counter);
230     DCHECK(converted);
231   }
232   return true;
233 }
234 
235 #if defined(OS_LINUX) || defined(OS_ANDROID)
GetVmSwapBytes() const236 uint64_t ProcessMetrics::GetVmSwapBytes() const {
237   return ReadProcStatusAndGetFieldAsSizeT(process_, "VmSwap") * 1024;
238 }
239 #endif  // defined(OS_LINUX) || defined(OS_ANDROID)
240 
241 #if defined(OS_LINUX) || defined(OS_ANDROID)
GetPageFaultCounts(PageFaultCounts * counts) const242 bool ProcessMetrics::GetPageFaultCounts(PageFaultCounts* counts) const {
243   // We are not using internal::ReadStatsFileAndGetFieldAsInt64(), since it
244   // would read the file twice, and return inconsistent numbers.
245   std::string stats_data;
246   if (!internal::ReadProcStats(process_, &stats_data))
247     return false;
248   std::vector<std::string> proc_stats;
249   if (!internal::ParseProcStats(stats_data, &proc_stats))
250     return false;
251 
252   counts->minor =
253       internal::GetProcStatsFieldAsInt64(proc_stats, internal::VM_MINFLT);
254   counts->major =
255       internal::GetProcStatsFieldAsInt64(proc_stats, internal::VM_MAJFLT);
256   return true;
257 }
258 #endif  // defined(OS_LINUX) || defined(OS_ANDROID)
259 
GetOpenFdCount() const260 int ProcessMetrics::GetOpenFdCount() const {
261   // Use /proc/<pid>/fd to count the number of entries there.
262   FilePath fd_path = internal::GetProcPidDir(process_).Append("fd");
263 
264   DirReaderPosix dir_reader(fd_path.value().c_str());
265   if (!dir_reader.IsValid())
266     return -1;
267 
268   int total_count = 0;
269   for (; dir_reader.Next(); ) {
270     const char* name = dir_reader.name();
271     if (strcmp(name, ".") != 0 && strcmp(name, "..") != 0)
272       ++total_count;
273   }
274 
275   return total_count;
276 }
277 
GetOpenFdSoftLimit() const278 int ProcessMetrics::GetOpenFdSoftLimit() const {
279   // Use /proc/<pid>/limits to read the open fd limit.
280   FilePath fd_path = internal::GetProcPidDir(process_).Append("limits");
281 
282   std::string limits_contents;
283   if (!ReadFileToString(fd_path, &limits_contents))
284     return -1;
285 
286   for (const auto& line : SplitStringPiece(
287            limits_contents, "\n", KEEP_WHITESPACE, SPLIT_WANT_NONEMPTY)) {
288     if (!line.starts_with("Max open files"))
289       continue;
290 
291     auto tokens =
292         SplitStringPiece(line, " ", TRIM_WHITESPACE, SPLIT_WANT_NONEMPTY);
293     if (tokens.size() > 3) {
294       int limit = -1;
295       if (!StringToInt(tokens[3], &limit))
296         return -1;
297       return limit;
298     }
299   }
300   return -1;
301 }
302 
303 #if defined(OS_LINUX) || defined(OS_AIX)
ProcessMetrics(ProcessHandle process)304 ProcessMetrics::ProcessMetrics(ProcessHandle process)
305     : process_(process), last_absolute_idle_wakeups_(0) {}
306 #else
ProcessMetrics(ProcessHandle process)307 ProcessMetrics::ProcessMetrics(ProcessHandle process) : process_(process) {}
308 #endif
309 
310 #if defined(OS_CHROMEOS)
311 // Private, Shared and Proportional working set sizes are obtained from
312 // /proc/<pid>/totmaps
GetTotalsSummary() const313 ProcessMetrics::TotalsSummary ProcessMetrics::GetTotalsSummary() const {
314   // The format of /proc/<pid>/totmaps is:
315   //
316   // Rss:                6120 kB
317   // Pss:                3335 kB
318   // Shared_Clean:       1008 kB
319   // Shared_Dirty:       4012 kB
320   // Private_Clean:         4 kB
321   // Private_Dirty:      1096 kB
322   // Referenced:          XXX kB
323   // Anonymous:           XXX kB
324   // AnonHugePages:       XXX kB
325   // Swap:                XXX kB
326   // Locked:              XXX kB
327   ProcessMetrics::TotalsSummary summary = {};
328 
329   const size_t kPrivate_CleanIndex = (4 * 3) + 1;
330   const size_t kPrivate_DirtyIndex = (5 * 3) + 1;
331   const size_t kSwapIndex = (9 * 3) + 1;
332 
333   std::string totmaps_data;
334   {
335     FilePath totmaps_file = internal::GetProcPidDir(process_).Append("totmaps");
336     ThreadRestrictions::ScopedAllowIO allow_io;
337     bool ret = ReadFileToString(totmaps_file, &totmaps_data);
338     if (!ret || totmaps_data.length() == 0)
339       return summary;
340   }
341 
342   std::vector<std::string> totmaps_fields = SplitString(
343       totmaps_data, kWhitespaceASCII, KEEP_WHITESPACE, SPLIT_WANT_NONEMPTY);
344 
345   DCHECK_EQ("Private_Clean:", totmaps_fields[kPrivate_CleanIndex - 1]);
346   DCHECK_EQ("Private_Dirty:", totmaps_fields[kPrivate_DirtyIndex - 1]);
347   DCHECK_EQ("Swap:", totmaps_fields[kSwapIndex-1]);
348 
349   int private_clean_kb = 0;
350   int private_dirty_kb = 0;
351   int swap_kb = 0;
352   bool success = true;
353   success &=
354       StringToInt(totmaps_fields[kPrivate_CleanIndex], &private_clean_kb);
355   success &=
356       StringToInt(totmaps_fields[kPrivate_DirtyIndex], &private_dirty_kb);
357   success &= StringToInt(totmaps_fields[kSwapIndex], &swap_kb);
358 
359   if (!success)
360     return summary;
361 
362   summary.private_clean_kb = private_clean_kb;
363   summary.private_dirty_kb = private_dirty_kb;
364   summary.swap_kb = swap_kb;
365 
366   return summary;
367 }
368 #endif
369 
GetSystemCommitCharge()370 size_t GetSystemCommitCharge() {
371   SystemMemoryInfoKB meminfo;
372   if (!GetSystemMemoryInfo(&meminfo))
373     return 0;
374   return meminfo.total - meminfo.free - meminfo.buffers - meminfo.cached;
375 }
376 
ParseProcStatCPU(StringPiece input)377 int ParseProcStatCPU(StringPiece input) {
378   // |input| may be empty if the process disappeared somehow.
379   // e.g. http://crbug.com/145811.
380   if (input.empty())
381     return -1;
382 
383   size_t start = input.find_last_of(')');
384   if (start == input.npos)
385     return -1;
386 
387   // Number of spaces remaining until reaching utime's index starting after the
388   // last ')'.
389   int num_spaces_remaining = internal::VM_UTIME - 1;
390 
391   size_t i = start;
392   while ((i = input.find(' ', i + 1)) != input.npos) {
393     // Validate the assumption that there aren't any contiguous spaces
394     // in |input| before utime.
395     DCHECK_NE(input[i - 1], ' ');
396     if (--num_spaces_remaining == 0) {
397       int utime = 0;
398       int stime = 0;
399       if (sscanf(&input.data()[i], "%d %d", &utime, &stime) != 2)
400         return -1;
401 
402       return utime + stime;
403     }
404   }
405 
406   return -1;
407 }
408 
GetNumberOfThreads(ProcessHandle process)409 int GetNumberOfThreads(ProcessHandle process) {
410   return internal::ReadProcStatsAndGetFieldAsInt64(process,
411                                                    internal::VM_NUMTHREADS);
412 }
413 
414 const char kProcSelfExe[] = "/proc/self/exe";
415 
416 namespace {
417 
418 // The format of /proc/diskstats is:
419 //  Device major number
420 //  Device minor number
421 //  Device name
422 //  Field  1 -- # of reads completed
423 //      This is the total number of reads completed successfully.
424 //  Field  2 -- # of reads merged, field 6 -- # of writes merged
425 //      Reads and writes which are adjacent to each other may be merged for
426 //      efficiency.  Thus two 4K reads may become one 8K read before it is
427 //      ultimately handed to the disk, and so it will be counted (and queued)
428 //      as only one I/O.  This field lets you know how often this was done.
429 //  Field  3 -- # of sectors read
430 //      This is the total number of sectors read successfully.
431 //  Field  4 -- # of milliseconds spent reading
432 //      This is the total number of milliseconds spent by all reads (as
433 //      measured from __make_request() to end_that_request_last()).
434 //  Field  5 -- # of writes completed
435 //      This is the total number of writes completed successfully.
436 //  Field  6 -- # of writes merged
437 //      See the description of field 2.
438 //  Field  7 -- # of sectors written
439 //      This is the total number of sectors written successfully.
440 //  Field  8 -- # of milliseconds spent writing
441 //      This is the total number of milliseconds spent by all writes (as
442 //      measured from __make_request() to end_that_request_last()).
443 //  Field  9 -- # of I/Os currently in progress
444 //      The only field that should go to zero. Incremented as requests are
445 //      given to appropriate struct request_queue and decremented as they
446 //      finish.
447 //  Field 10 -- # of milliseconds spent doing I/Os
448 //      This field increases so long as field 9 is nonzero.
449 //  Field 11 -- weighted # of milliseconds spent doing I/Os
450 //      This field is incremented at each I/O start, I/O completion, I/O
451 //      merge, or read of these stats by the number of I/Os in progress
452 //      (field 9) times the number of milliseconds spent doing I/O since the
453 //      last update of this field.  This can provide an easy measure of both
454 //      I/O completion time and the backlog that may be accumulating.
455 
456 const size_t kDiskDriveName = 2;
457 const size_t kDiskReads = 3;
458 const size_t kDiskReadsMerged = 4;
459 const size_t kDiskSectorsRead = 5;
460 const size_t kDiskReadTime = 6;
461 const size_t kDiskWrites = 7;
462 const size_t kDiskWritesMerged = 8;
463 const size_t kDiskSectorsWritten = 9;
464 const size_t kDiskWriteTime = 10;
465 const size_t kDiskIO = 11;
466 const size_t kDiskIOTime = 12;
467 const size_t kDiskWeightedIOTime = 13;
468 
469 }  // namespace
470 
ToValue() const471 std::unique_ptr<DictionaryValue> SystemMemoryInfoKB::ToValue() const {
472   auto res = std::make_unique<DictionaryValue>();
473   res->SetInteger("total", total);
474   res->SetInteger("free", free);
475   res->SetInteger("available", available);
476   res->SetInteger("buffers", buffers);
477   res->SetInteger("cached", cached);
478   res->SetInteger("active_anon", active_anon);
479   res->SetInteger("inactive_anon", inactive_anon);
480   res->SetInteger("active_file", active_file);
481   res->SetInteger("inactive_file", inactive_file);
482   res->SetInteger("swap_total", swap_total);
483   res->SetInteger("swap_free", swap_free);
484   res->SetInteger("swap_used", swap_total - swap_free);
485   res->SetInteger("dirty", dirty);
486   res->SetInteger("reclaimable", reclaimable);
487 #ifdef OS_CHROMEOS
488   res->SetInteger("shmem", shmem);
489   res->SetInteger("slab", slab);
490   res->SetInteger("gem_objects", gem_objects);
491   res->SetInteger("gem_size", gem_size);
492 #endif
493 
494   return res;
495 }
496 
ParseProcMeminfo(StringPiece meminfo_data,SystemMemoryInfoKB * meminfo)497 bool ParseProcMeminfo(StringPiece meminfo_data, SystemMemoryInfoKB* meminfo) {
498   // The format of /proc/meminfo is:
499   //
500   // MemTotal:      8235324 kB
501   // MemFree:       1628304 kB
502   // Buffers:        429596 kB
503   // Cached:        4728232 kB
504   // ...
505   // There is no guarantee on the ordering or position
506   // though it doesn't appear to change very often
507 
508   // As a basic sanity check at the end, make sure the MemTotal value will be at
509   // least non-zero. So start off with a zero total.
510   meminfo->total = 0;
511 
512   for (const StringPiece& line : SplitStringPiece(
513            meminfo_data, "\n", KEEP_WHITESPACE, SPLIT_WANT_NONEMPTY)) {
514     std::vector<StringPiece> tokens = SplitStringPiece(
515         line, kWhitespaceASCII, TRIM_WHITESPACE, SPLIT_WANT_NONEMPTY);
516     // HugePages_* only has a number and no suffix so there may not be exactly 3
517     // tokens.
518     if (tokens.size() <= 1) {
519       DLOG(WARNING) << "meminfo: tokens: " << tokens.size()
520                     << " malformed line: " << line.as_string();
521       continue;
522     }
523 
524     int* target = nullptr;
525     if (tokens[0] == "MemTotal:")
526       target = &meminfo->total;
527     else if (tokens[0] == "MemFree:")
528       target = &meminfo->free;
529     else if (tokens[0] == "MemAvailable:")
530       target = &meminfo->available;
531     else if (tokens[0] == "Buffers:")
532       target = &meminfo->buffers;
533     else if (tokens[0] == "Cached:")
534       target = &meminfo->cached;
535     else if (tokens[0] == "Active(anon):")
536       target = &meminfo->active_anon;
537     else if (tokens[0] == "Inactive(anon):")
538       target = &meminfo->inactive_anon;
539     else if (tokens[0] == "Active(file):")
540       target = &meminfo->active_file;
541     else if (tokens[0] == "Inactive(file):")
542       target = &meminfo->inactive_file;
543     else if (tokens[0] == "SwapTotal:")
544       target = &meminfo->swap_total;
545     else if (tokens[0] == "SwapFree:")
546       target = &meminfo->swap_free;
547     else if (tokens[0] == "Dirty:")
548       target = &meminfo->dirty;
549     else if (tokens[0] == "SReclaimable:")
550       target = &meminfo->reclaimable;
551 #if defined(OS_CHROMEOS)
552     // Chrome OS has a tweaked kernel that allows querying Shmem, which is
553     // usually video memory otherwise invisible to the OS.
554     else if (tokens[0] == "Shmem:")
555       target = &meminfo->shmem;
556     else if (tokens[0] == "Slab:")
557       target = &meminfo->slab;
558 #endif
559     if (target)
560       StringToInt(tokens[1], target);
561   }
562 
563   // Make sure the MemTotal is valid.
564   return meminfo->total > 0;
565 }
566 
ParseProcVmstat(StringPiece vmstat_data,VmStatInfo * vmstat)567 bool ParseProcVmstat(StringPiece vmstat_data, VmStatInfo* vmstat) {
568   // The format of /proc/vmstat is:
569   //
570   // nr_free_pages 299878
571   // nr_inactive_anon 239863
572   // nr_active_anon 1318966
573   // nr_inactive_file 2015629
574   // ...
575   //
576   // Iterate through the whole file because the position of the
577   // fields are dependent on the kernel version and configuration.
578   bool has_pswpin = false;
579   bool has_pswpout = false;
580   bool has_pgmajfault = false;
581   for (const StringPiece& line : SplitStringPiece(
582            vmstat_data, "\n", KEEP_WHITESPACE, SPLIT_WANT_NONEMPTY)) {
583     std::vector<StringPiece> tokens = SplitStringPiece(
584         line, " ", KEEP_WHITESPACE, SPLIT_WANT_NONEMPTY);
585     if (tokens.size() != 2)
586       continue;
587 
588     uint64_t val;
589     if (!StringToUint64(tokens[1], &val))
590       continue;
591 
592     if (tokens[0] == "pswpin") {
593       vmstat->pswpin = val;
594       DCHECK(!has_pswpin);
595       has_pswpin = true;
596     } else if (tokens[0] == "pswpout") {
597       vmstat->pswpout = val;
598       DCHECK(!has_pswpout);
599       has_pswpout = true;
600     } else if (tokens[0] == "pgmajfault") {
601       vmstat->pgmajfault = val;
602       DCHECK(!has_pgmajfault);
603       has_pgmajfault = true;
604     }
605     if (has_pswpin && has_pswpout && has_pgmajfault)
606       return true;
607   }
608 
609   return false;
610 }
611 
GetSystemMemoryInfo(SystemMemoryInfoKB * meminfo)612 bool GetSystemMemoryInfo(SystemMemoryInfoKB* meminfo) {
613   // Synchronously reading files in /proc and /sys are safe.
614   ThreadRestrictions::ScopedAllowIO allow_io;
615 
616   // Used memory is: total - free - buffers - caches
617   FilePath meminfo_file("/proc/meminfo");
618   std::string meminfo_data;
619   if (!ReadFileToString(meminfo_file, &meminfo_data)) {
620     DLOG(WARNING) << "Failed to open " << meminfo_file.value();
621     return false;
622   }
623 
624   if (!ParseProcMeminfo(meminfo_data, meminfo)) {
625     DLOG(WARNING) << "Failed to parse " << meminfo_file.value();
626     return false;
627   }
628 
629 #if defined(OS_CHROMEOS)
630   ReadChromeOSGraphicsMemory(meminfo);
631 #endif
632 
633   return true;
634 }
635 
ToValue() const636 std::unique_ptr<DictionaryValue> VmStatInfo::ToValue() const {
637   auto res = std::make_unique<DictionaryValue>();
638   res->SetInteger("pswpin", pswpin);
639   res->SetInteger("pswpout", pswpout);
640   res->SetInteger("pgmajfault", pgmajfault);
641   return res;
642 }
643 
GetVmStatInfo(VmStatInfo * vmstat)644 bool GetVmStatInfo(VmStatInfo* vmstat) {
645   // Synchronously reading files in /proc and /sys are safe.
646   ThreadRestrictions::ScopedAllowIO allow_io;
647 
648   FilePath vmstat_file("/proc/vmstat");
649   std::string vmstat_data;
650   if (!ReadFileToString(vmstat_file, &vmstat_data)) {
651     DLOG(WARNING) << "Failed to open " << vmstat_file.value();
652     return false;
653   }
654   if (!ParseProcVmstat(vmstat_data, vmstat)) {
655     DLOG(WARNING) << "Failed to parse " << vmstat_file.value();
656     return false;
657   }
658   return true;
659 }
660 
SystemDiskInfo()661 SystemDiskInfo::SystemDiskInfo() {
662   reads = 0;
663   reads_merged = 0;
664   sectors_read = 0;
665   read_time = 0;
666   writes = 0;
667   writes_merged = 0;
668   sectors_written = 0;
669   write_time = 0;
670   io = 0;
671   io_time = 0;
672   weighted_io_time = 0;
673 }
674 
675 SystemDiskInfo::SystemDiskInfo(const SystemDiskInfo& other) = default;
676 
ToValue() const677 std::unique_ptr<Value> SystemDiskInfo::ToValue() const {
678   auto res = std::make_unique<DictionaryValue>();
679 
680   // Write out uint64_t variables as doubles.
681   // Note: this may discard some precision, but for JS there's no other option.
682   res->SetDouble("reads", static_cast<double>(reads));
683   res->SetDouble("reads_merged", static_cast<double>(reads_merged));
684   res->SetDouble("sectors_read", static_cast<double>(sectors_read));
685   res->SetDouble("read_time", static_cast<double>(read_time));
686   res->SetDouble("writes", static_cast<double>(writes));
687   res->SetDouble("writes_merged", static_cast<double>(writes_merged));
688   res->SetDouble("sectors_written", static_cast<double>(sectors_written));
689   res->SetDouble("write_time", static_cast<double>(write_time));
690   res->SetDouble("io", static_cast<double>(io));
691   res->SetDouble("io_time", static_cast<double>(io_time));
692   res->SetDouble("weighted_io_time", static_cast<double>(weighted_io_time));
693 
694   return std::move(res);
695 }
696 
IsValidDiskName(StringPiece candidate)697 bool IsValidDiskName(StringPiece candidate) {
698   if (candidate.length() < 3)
699     return false;
700 
701   if (candidate[1] == 'd' &&
702       (candidate[0] == 'h' || candidate[0] == 's' || candidate[0] == 'v')) {
703     // [hsv]d[a-z]+ case
704     for (size_t i = 2; i < candidate.length(); ++i) {
705       if (!islower(candidate[i]))
706         return false;
707     }
708     return true;
709   }
710 
711   const char kMMCName[] = "mmcblk";
712   if (!candidate.starts_with(kMMCName))
713     return false;
714 
715   // mmcblk[0-9]+ case
716   for (size_t i = strlen(kMMCName); i < candidate.length(); ++i) {
717     if (!isdigit(candidate[i]))
718       return false;
719   }
720   return true;
721 }
722 
GetSystemDiskInfo(SystemDiskInfo * diskinfo)723 bool GetSystemDiskInfo(SystemDiskInfo* diskinfo) {
724   // Synchronously reading files in /proc does not hit the disk.
725   ThreadRestrictions::ScopedAllowIO allow_io;
726 
727   FilePath diskinfo_file("/proc/diskstats");
728   std::string diskinfo_data;
729   if (!ReadFileToString(diskinfo_file, &diskinfo_data)) {
730     DLOG(WARNING) << "Failed to open " << diskinfo_file.value();
731     return false;
732   }
733 
734   std::vector<StringPiece> diskinfo_lines = SplitStringPiece(
735       diskinfo_data, "\n", KEEP_WHITESPACE, SPLIT_WANT_NONEMPTY);
736   if (diskinfo_lines.empty()) {
737     DLOG(WARNING) << "No lines found";
738     return false;
739   }
740 
741   diskinfo->reads = 0;
742   diskinfo->reads_merged = 0;
743   diskinfo->sectors_read = 0;
744   diskinfo->read_time = 0;
745   diskinfo->writes = 0;
746   diskinfo->writes_merged = 0;
747   diskinfo->sectors_written = 0;
748   diskinfo->write_time = 0;
749   diskinfo->io = 0;
750   diskinfo->io_time = 0;
751   diskinfo->weighted_io_time = 0;
752 
753   uint64_t reads = 0;
754   uint64_t reads_merged = 0;
755   uint64_t sectors_read = 0;
756   uint64_t read_time = 0;
757   uint64_t writes = 0;
758   uint64_t writes_merged = 0;
759   uint64_t sectors_written = 0;
760   uint64_t write_time = 0;
761   uint64_t io = 0;
762   uint64_t io_time = 0;
763   uint64_t weighted_io_time = 0;
764 
765   for (const StringPiece& line : diskinfo_lines) {
766     std::vector<StringPiece> disk_fields = SplitStringPiece(
767         line, kWhitespaceASCII, TRIM_WHITESPACE, SPLIT_WANT_NONEMPTY);
768 
769     // Fields may have overflowed and reset to zero.
770     if (!IsValidDiskName(disk_fields[kDiskDriveName].as_string()))
771       continue;
772 
773     StringToUint64(disk_fields[kDiskReads], &reads);
774     StringToUint64(disk_fields[kDiskReadsMerged], &reads_merged);
775     StringToUint64(disk_fields[kDiskSectorsRead], &sectors_read);
776     StringToUint64(disk_fields[kDiskReadTime], &read_time);
777     StringToUint64(disk_fields[kDiskWrites], &writes);
778     StringToUint64(disk_fields[kDiskWritesMerged], &writes_merged);
779     StringToUint64(disk_fields[kDiskSectorsWritten], &sectors_written);
780     StringToUint64(disk_fields[kDiskWriteTime], &write_time);
781     StringToUint64(disk_fields[kDiskIO], &io);
782     StringToUint64(disk_fields[kDiskIOTime], &io_time);
783     StringToUint64(disk_fields[kDiskWeightedIOTime], &weighted_io_time);
784 
785     diskinfo->reads += reads;
786     diskinfo->reads_merged += reads_merged;
787     diskinfo->sectors_read += sectors_read;
788     diskinfo->read_time += read_time;
789     diskinfo->writes += writes;
790     diskinfo->writes_merged += writes_merged;
791     diskinfo->sectors_written += sectors_written;
792     diskinfo->write_time += write_time;
793     diskinfo->io += io;
794     diskinfo->io_time += io_time;
795     diskinfo->weighted_io_time += weighted_io_time;
796   }
797 
798   return true;
799 }
800 
GetUserCpuTimeSinceBoot()801 TimeDelta GetUserCpuTimeSinceBoot() {
802   return internal::GetUserCpuTimeSinceBoot();
803 }
804 
805 #if defined(OS_CHROMEOS)
ToValue() const806 std::unique_ptr<Value> SwapInfo::ToValue() const {
807   auto res = std::make_unique<DictionaryValue>();
808 
809   // Write out uint64_t variables as doubles.
810   // Note: this may discard some precision, but for JS there's no other option.
811   res->SetDouble("num_reads", static_cast<double>(num_reads));
812   res->SetDouble("num_writes", static_cast<double>(num_writes));
813   res->SetDouble("orig_data_size", static_cast<double>(orig_data_size));
814   res->SetDouble("compr_data_size", static_cast<double>(compr_data_size));
815   res->SetDouble("mem_used_total", static_cast<double>(mem_used_total));
816   double ratio = compr_data_size ? static_cast<double>(orig_data_size) /
817                                        static_cast<double>(compr_data_size)
818                                  : 0;
819   res->SetDouble("compression_ratio", ratio);
820 
821   return std::move(res);
822 }
823 
ParseZramMmStat(StringPiece mm_stat_data,SwapInfo * swap_info)824 bool ParseZramMmStat(StringPiece mm_stat_data, SwapInfo* swap_info) {
825   // There are 7 columns in /sys/block/zram0/mm_stat,
826   // split by several spaces. The first three columns
827   // are orig_data_size, compr_data_size and mem_used_total.
828   // Example:
829   // 17715200 5008166 566062  0 1225715712  127 183842
830   //
831   // For more details:
832   // https://www.kernel.org/doc/Documentation/blockdev/zram.txt
833 
834   std::vector<StringPiece> tokens = SplitStringPiece(
835       mm_stat_data, kWhitespaceASCII, TRIM_WHITESPACE, SPLIT_WANT_NONEMPTY);
836   if (tokens.size() < 7) {
837     DLOG(WARNING) << "zram mm_stat: tokens: " << tokens.size()
838                   << " malformed line: " << mm_stat_data.as_string();
839     return false;
840   }
841 
842   if (!StringToUint64(tokens[0], &swap_info->orig_data_size))
843     return false;
844   if (!StringToUint64(tokens[1], &swap_info->compr_data_size))
845     return false;
846   if (!StringToUint64(tokens[2], &swap_info->mem_used_total))
847     return false;
848 
849   return true;
850 }
851 
ParseZramStat(StringPiece stat_data,SwapInfo * swap_info)852 bool ParseZramStat(StringPiece stat_data, SwapInfo* swap_info) {
853   // There are 11 columns in /sys/block/zram0/stat,
854   // split by several spaces. The first column is read I/Os
855   // and fifth column is write I/Os.
856   // Example:
857   // 299    0    2392    0    1    0    8    0    0    0    0
858   //
859   // For more details:
860   // https://www.kernel.org/doc/Documentation/blockdev/zram.txt
861 
862   std::vector<StringPiece> tokens = SplitStringPiece(
863       stat_data, kWhitespaceASCII, TRIM_WHITESPACE, SPLIT_WANT_NONEMPTY);
864   if (tokens.size() < 11) {
865     DLOG(WARNING) << "zram stat: tokens: " << tokens.size()
866                   << " malformed line: " << stat_data.as_string();
867     return false;
868   }
869 
870   if (!StringToUint64(tokens[0], &swap_info->num_reads))
871     return false;
872   if (!StringToUint64(tokens[4], &swap_info->num_writes))
873     return false;
874 
875   return true;
876 }
877 
878 namespace {
879 
IgnoreZramFirstPage(uint64_t orig_data_size,SwapInfo * swap_info)880 bool IgnoreZramFirstPage(uint64_t orig_data_size, SwapInfo* swap_info) {
881   if (orig_data_size <= 4096) {
882     // A single page is compressed at startup, and has a high compression
883     // ratio. Ignore this as it doesn't indicate any real swapping.
884     swap_info->orig_data_size = 0;
885     swap_info->num_reads = 0;
886     swap_info->num_writes = 0;
887     swap_info->compr_data_size = 0;
888     swap_info->mem_used_total = 0;
889     return true;
890   }
891   return false;
892 }
893 
ParseZramPath(SwapInfo * swap_info)894 void ParseZramPath(SwapInfo* swap_info) {
895   FilePath zram_path("/sys/block/zram0");
896   uint64_t orig_data_size =
897       ReadFileToUint64(zram_path.Append("orig_data_size"));
898   if (IgnoreZramFirstPage(orig_data_size, swap_info))
899     return;
900 
901   swap_info->orig_data_size = orig_data_size;
902   swap_info->num_reads = ReadFileToUint64(zram_path.Append("num_reads"));
903   swap_info->num_writes = ReadFileToUint64(zram_path.Append("num_writes"));
904   swap_info->compr_data_size =
905       ReadFileToUint64(zram_path.Append("compr_data_size"));
906   swap_info->mem_used_total =
907       ReadFileToUint64(zram_path.Append("mem_used_total"));
908 }
909 
GetSwapInfoImpl(SwapInfo * swap_info)910 bool GetSwapInfoImpl(SwapInfo* swap_info) {
911   // Synchronously reading files in /sys/block/zram0 does not hit the disk.
912   ThreadRestrictions::ScopedAllowIO allow_io;
913 
914   // Since ZRAM update, it shows the usage data in different places.
915   // If file "/sys/block/zram0/mm_stat" exists, use the new way, otherwise,
916   // use the old way.
917   static Optional<bool> use_new_zram_interface;
918   FilePath zram_mm_stat_file("/sys/block/zram0/mm_stat");
919   if (!use_new_zram_interface.has_value()) {
920     use_new_zram_interface = PathExists(zram_mm_stat_file);
921   }
922 
923   if (!use_new_zram_interface.value()) {
924     ParseZramPath(swap_info);
925     return true;
926   }
927 
928   std::string mm_stat_data;
929   if (!ReadFileToString(zram_mm_stat_file, &mm_stat_data)) {
930     DLOG(WARNING) << "Failed to open " << zram_mm_stat_file.value();
931     return false;
932   }
933   if (!ParseZramMmStat(mm_stat_data, swap_info)) {
934     DLOG(WARNING) << "Failed to parse " << zram_mm_stat_file.value();
935     return false;
936   }
937   if (IgnoreZramFirstPage(swap_info->orig_data_size, swap_info))
938     return true;
939 
940   FilePath zram_stat_file("/sys/block/zram0/stat");
941   std::string stat_data;
942   if (!ReadFileToString(zram_stat_file, &stat_data)) {
943     DLOG(WARNING) << "Failed to open " << zram_stat_file.value();
944     return false;
945   }
946   if (!ParseZramStat(stat_data, swap_info)) {
947     DLOG(WARNING) << "Failed to parse " << zram_stat_file.value();
948     return false;
949   }
950 
951   return true;
952 }
953 
954 }  // namespace
955 
GetSwapInfo(SwapInfo * swap_info)956 bool GetSwapInfo(SwapInfo* swap_info) {
957   if (!GetSwapInfoImpl(swap_info)) {
958     *swap_info = SwapInfo();
959     return false;
960   }
961   return true;
962 }
963 #endif  // defined(OS_CHROMEOS)
964 
965 #if defined(OS_LINUX) || defined(OS_AIX)
GetIdleWakeupsPerSecond()966 int ProcessMetrics::GetIdleWakeupsPerSecond() {
967   uint64_t num_switches;
968   static const char kSwitchStat[] = "voluntary_ctxt_switches";
969   return ReadProcStatusAndGetFieldAsUint64(process_, kSwitchStat, &num_switches)
970              ? CalculateIdleWakeupsPerSecond(num_switches)
971              : 0;
972 }
973 #endif  // defined(OS_LINUX) || defined(OS_AIX)
974 
975 }  // namespace base
976