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