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