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