Home | History | Annotate | Download | only in browser
      1 // Copyright (c) 2009 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 "chrome/browser/process_info_snapshot.h"
      6 
      7 #include <sys/sysctl.h>
      8 
      9 #include <sstream>
     10 
     11 #include "base/command_line.h"
     12 #include "base/logging.h"
     13 #include "base/string_number_conversions.h"
     14 #include "base/string_util.h"
     15 #include "base/sys_info.h"
     16 #include "base/threading/thread.h"
     17 
     18 // Default constructor.
     19 ProcessInfoSnapshot::ProcessInfoSnapshot() { }
     20 
     21 // Destructor: just call |Reset()| to release everything.
     22 ProcessInfoSnapshot::~ProcessInfoSnapshot() {
     23   Reset();
     24 }
     25 
     26 const size_t ProcessInfoSnapshot::kMaxPidListSize = 1000;
     27 
     28 static bool GetKInfoForProcessID(pid_t pid, kinfo_proc* kinfo) {
     29   int mib[] = {CTL_KERN, KERN_PROC, KERN_PROC_PID, pid};
     30   size_t len = sizeof(*kinfo);
     31   if (sysctl(mib, arraysize(mib), kinfo, &len, NULL, 0) != 0) {
     32     PLOG(ERROR) << "sysctl() for KERN_PROC";
     33     return false;
     34   }
     35 
     36   if (len == 0) {
     37     // If the process isn't found then sysctl returns a length of 0.
     38     return false;
     39   }
     40 
     41   return true;
     42 }
     43 
     44 static bool GetExecutableNameForProcessID(
     45     pid_t pid,
     46     std::string* executable_name) {
     47   if (!executable_name) {
     48     NOTREACHED();
     49     return false;
     50   }
     51 
     52   static int s_arg_max = 0;
     53   if (s_arg_max == 0) {
     54     int mib[] = {CTL_KERN, KERN_ARGMAX};
     55     size_t size = sizeof(s_arg_max);
     56     if (sysctl(mib, arraysize(mib), &s_arg_max, &size, NULL, 0) != 0)
     57       PLOG(ERROR) << "sysctl() for KERN_ARGMAX";
     58   }
     59 
     60   if (s_arg_max == 0)
     61     return false;
     62 
     63   int mib[] = {CTL_KERN, KERN_PROCARGS, pid};
     64   size_t size = s_arg_max;
     65   executable_name->resize(s_arg_max + 1);
     66   if (sysctl(mib, arraysize(mib), &(*executable_name)[0],
     67              &size, NULL, 0) != 0) {
     68     // Don't log the error since it's normal for this to fail.
     69     return false;
     70   }
     71 
     72   // KERN_PROCARGS returns multiple NULL terminated strings. Truncate
     73   // executable_name to just the first string.
     74   size_t end_pos = executable_name->find('\0');
     75   if (end_pos == std::string::npos) {
     76     return false;
     77   }
     78 
     79   executable_name->resize(end_pos);
     80   return true;
     81 }
     82 
     83 // Converts a byte unit such as 'K' or 'M' into the scale for the unit.
     84 // The scale can then be used to calculate the number of bytes in a value.
     85 // The units are based on humanize_number(). See:
     86 // http://www.opensource.apple.com/source/libutil/libutil-21/humanize_number.c
     87 static bool ConvertByteUnitToScale(char unit, uint64_t* out_scale) {
     88   int shift = 0;
     89   switch (unit) {
     90     case 'B':
     91       shift = 0;
     92       break;
     93     case 'K':
     94     case 'k':
     95       shift = 1;
     96       break;
     97     case 'M':
     98       shift = 2;
     99       break;
    100     case 'G':
    101       shift = 3;
    102       break;
    103     case 'T':
    104       shift = 4;
    105       break;
    106     case 'P':
    107       shift = 5;
    108       break;
    109     case 'E':
    110       shift = 6;
    111       break;
    112     default:
    113       return false;
    114   }
    115 
    116   uint64_t scale = 1;
    117   for (int i = 0; i < shift; i++)
    118     scale *= 1024;
    119   *out_scale = scale;
    120 
    121   return true;
    122 }
    123 
    124 // Capture the information by calling '/bin/ps'.
    125 // Note: we ignore the "tsiz" (text size) display option of ps because it's
    126 // always zero (tested on 10.5 and 10.6).
    127 static bool GetProcessMemoryInfoUsingPS(
    128     const std::vector<base::ProcessId>& pid_list,
    129     std::map<int,ProcessInfoSnapshot::ProcInfoEntry>& proc_info_entries) {
    130   const char kPsPathName[] = "/bin/ps";
    131   std::vector<std::string> argv;
    132   argv.push_back(kPsPathName);
    133 
    134   // Get resident set size, virtual memory size.
    135   argv.push_back("-o");
    136   argv.push_back("pid=,rss=,vsz=");
    137   // Only display the specified PIDs.
    138   for (std::vector<base::ProcessId>::const_iterator it = pid_list.begin();
    139       it != pid_list.end(); ++it) {
    140     argv.push_back("-p");
    141     argv.push_back(base::Int64ToString(static_cast<int64>(*it)));
    142   }
    143 
    144   std::string output;
    145   CommandLine command_line(argv);
    146   // Limit output read to a megabyte for safety.
    147   if (!base::GetAppOutputRestricted(command_line, &output, 1024 * 1024)) {
    148     LOG(ERROR) << "Failure running " << kPsPathName << " to acquire data.";
    149     return false;
    150   }
    151 
    152   std::istringstream in(output, std::istringstream::in);
    153   std::string line;
    154 
    155   // Process lines until done.
    156   while (true) {
    157     // The format is as specified above to ps (see ps(1)):
    158     //   "-o pid=,rss=,vsz=".
    159     // Try to read the PID; if we get it, we should be able to get the rest of
    160     // the line.
    161     pid_t pid;
    162     in >> pid;
    163     if (in.eof())
    164       break;
    165 
    166     ProcessInfoSnapshot::ProcInfoEntry proc_info = proc_info_entries[pid];
    167     proc_info.pid = pid;
    168     in >> proc_info.rss;
    169     in >> proc_info.vsize;
    170     proc_info.rss *= 1024;                // Convert from kilobytes to bytes.
    171     proc_info.vsize *= 1024;
    172     in.ignore(1, ' ');                    // Eat the space.
    173     std::getline(in, proc_info.command);  // Get the rest of the line.
    174     if (!in.good()) {
    175       LOG(ERROR) << "Error parsing output from " << kPsPathName << ".";
    176       return false;
    177     }
    178 
    179     if (!proc_info.pid || ! proc_info.vsize) {
    180       LOG(WARNING) << "Invalid data from " << kPsPathName << ".";
    181       return false;
    182     }
    183 
    184     // Record the process information.
    185     proc_info_entries[proc_info.pid] = proc_info;
    186   }
    187 
    188   return true;
    189 }
    190 
    191 static bool GetProcessMemoryInfoUsingTop(
    192     std::map<int,ProcessInfoSnapshot::ProcInfoEntry>& proc_info_entries) {
    193   const char kTopPathName[] = "/usr/bin/top";
    194   std::vector<std::string> argv;
    195   argv.push_back(kTopPathName);
    196 
    197   // -stats tells top to print just the given fields as ordered.
    198   argv.push_back("-stats");
    199   argv.push_back("pid,"    // Process ID
    200                  "rsize,"  // Resident memory
    201                  "rshrd,"  // Resident shared memory
    202                  "rprvt,"  // Resident private memory
    203                  "vsize"); // Total virtual memory
    204   // Run top in logging (non-interactive) mode.
    205   argv.push_back("-l");
    206   argv.push_back("1");
    207   // Set the delay between updates to 0.
    208   argv.push_back("-s");
    209   argv.push_back("0");
    210 
    211   std::string output;
    212   CommandLine command_line(argv);
    213   // Limit output read to a megabyte for safety.
    214   if (!base::GetAppOutputRestricted(command_line, &output, 1024 * 1024)) {
    215     LOG(ERROR) << "Failure running " << kTopPathName << " to acquire data.";
    216     return false;
    217   }
    218 
    219   // Process lines until done. Lines should look something like this:
    220   // PID    RSIZE  RSHRD  RPRVT  VSIZE
    221   // 58539  1276K+ 336K+  740K+  2378M+
    222   // 58485  1888K+ 592K+  1332K+ 2383M+
    223   std::istringstream top_in(output, std::istringstream::in);
    224   std::string line;
    225   while (std::getline(top_in, line)) {
    226     std::istringstream in(line, std::istringstream::in);
    227 
    228     // Try to read the PID.
    229     pid_t pid;
    230     in >> pid;
    231     if (in.fail())
    232       continue;
    233 
    234     // Make sure that caller is interested in this process.
    235     if (proc_info_entries.find(pid) == proc_info_entries.end())
    236       continue;
    237 
    238     // Skip the - or + sign that top puts after the pid.
    239     in.get();
    240 
    241     uint64_t values[4];
    242     size_t i;
    243     for (i = 0; i < arraysize(values); i++) {
    244       in >> values[i];
    245       if (in.fail())
    246         break;
    247       std::string unit;
    248       in >> unit;
    249       if (in.fail())
    250         break;
    251 
    252       if (unit.empty())
    253         break;
    254 
    255       uint64_t scale;
    256       if (!ConvertByteUnitToScale(unit[0], &scale))
    257         break;
    258       values[i] *= scale;
    259     }
    260     if (i != arraysize(values))
    261       continue;
    262 
    263     ProcessInfoSnapshot::ProcInfoEntry proc_info = proc_info_entries[pid];
    264     proc_info.rss = values[0];
    265     proc_info.rshrd = values[1];
    266     proc_info.rprvt = values[2];
    267     proc_info.vsize = values[3];
    268     // Record the process information.
    269     proc_info_entries[proc_info.pid] = proc_info;
    270   }
    271 
    272   return true;
    273 }
    274 
    275 static bool GetProcessMemoryInfoUsingTop_10_5(
    276     std::map<int,ProcessInfoSnapshot::ProcInfoEntry>& proc_info_entries) {
    277   const char kTopPathName[] = "/usr/bin/top";
    278   std::vector<std::string> argv;
    279   argv.push_back(kTopPathName);
    280 
    281   // -p tells top to print just the given fields as ordered.
    282   argv.push_back("-p");
    283   argv.push_back("^aaaaaaaaaaaaaaaaaaaa "  // Process ID (PID)
    284                  "^jjjjjjjjjjjjjjjjjjjj "  // Resident memory (RSIZE)
    285                  "^iiiiiiiiiiiiiiiiiiii "  // Resident shared memory (RSHRD)
    286                  "^hhhhhhhhhhhhhhhhhhhh "  // Resident private memory (RPRVT)
    287                  "^llllllllllllllllllll"); // Total virtual memory (VSIZE)
    288   // Run top in logging (non-interactive) mode.
    289   argv.push_back("-l");
    290   argv.push_back("1");
    291   // Set the delay between updates to 0.
    292   argv.push_back("-s");
    293   argv.push_back("0");
    294 
    295   std::string output;
    296   CommandLine command_line(argv);
    297   // Limit output read to a megabyte for safety.
    298   if (!base::GetAppOutputRestricted(command_line, &output, 1024 * 1024)) {
    299     LOG(ERROR) << "Failure running " << kTopPathName << " to acquire data.";
    300     return false;
    301   }
    302 
    303   // Process lines until done. Lines should look something like this:
    304   // PID      RSIZE     RSHRD     RPRVT     VSIZE
    305   // 16943    815104    262144    290816    18489344
    306   // 16922    954368    720896    278528    18976768
    307   std::istringstream top_in(output, std::istringstream::in);
    308   std::string line;
    309   while (std::getline(top_in, line)) {
    310     std::istringstream in(line, std::istringstream::in);
    311 
    312     // Try to read the PID.
    313     pid_t pid;
    314     in >> pid;
    315     if (in.fail())
    316       continue;
    317 
    318     // Make sure that caller is interested in this process.
    319     if (proc_info_entries.find(pid) == proc_info_entries.end())
    320       continue;
    321 
    322     uint64_t values[4];
    323     size_t i;
    324     for (i = 0; i < arraysize(values); i++) {
    325       in >> values[i];
    326       if (in.fail())
    327         break;
    328     }
    329     if (i != arraysize(values))
    330       continue;
    331 
    332     ProcessInfoSnapshot::ProcInfoEntry proc_info = proc_info_entries[pid];
    333     proc_info.rss = values[0];
    334     proc_info.rshrd = values[1];
    335     proc_info.rprvt = values[2];
    336     proc_info.vsize = values[3];
    337     // Record the process information.
    338     proc_info_entries[proc_info.pid] = proc_info;
    339   }
    340 
    341   return true;
    342 }
    343 
    344 
    345 bool ProcessInfoSnapshot::Sample(std::vector<base::ProcessId> pid_list) {
    346   Reset();
    347 
    348   // Nothing to do if no PIDs given.
    349   if (pid_list.empty())
    350     return true;
    351   if (pid_list.size() > kMaxPidListSize) {
    352     // The spec says |pid_list| *must* not have more than this many entries.
    353     NOTREACHED();
    354     return false;
    355   }
    356 
    357   // Get basic process info from KERN_PROC.
    358   for (std::vector<base::ProcessId>::iterator it = pid_list.begin();
    359        it != pid_list.end(); ++it) {
    360     ProcInfoEntry proc_info;
    361     proc_info.pid = *it;
    362 
    363     kinfo_proc kinfo;
    364     if (!GetKInfoForProcessID(*it, &kinfo))
    365       return false;
    366 
    367     proc_info.ppid = kinfo.kp_eproc.e_ppid;
    368     proc_info.uid = kinfo.kp_eproc.e_pcred.p_ruid;
    369     proc_info.euid = kinfo.kp_eproc.e_ucred.cr_uid;
    370     // Note, p_comm is truncated to 16 characters.
    371     proc_info.command = kinfo.kp_proc.p_comm;
    372     proc_info_entries_[*it] = proc_info;
    373   }
    374 
    375   // Use KERN_PROCARGS to get the full executable name. This may fail if this
    376   // process doesn't have privileges to inspect the target process.
    377   for (std::vector<base::ProcessId>::iterator it = pid_list.begin();
    378        it != pid_list.end(); ++it) {
    379     std::string exectuable_name;
    380     if (GetExecutableNameForProcessID(*it, &exectuable_name)) {
    381       ProcInfoEntry proc_info = proc_info_entries_[*it];
    382       proc_info.command = exectuable_name;
    383     }
    384   }
    385 
    386   // Get memory information using top.
    387   bool memory_info_success = false;
    388   int32 major, minor, bugfix;
    389   base::SysInfo::OperatingSystemVersionNumbers(&major, &minor, &bugfix);
    390   if (major == 10 && minor == 5)
    391     memory_info_success = GetProcessMemoryInfoUsingTop_10_5(proc_info_entries_);
    392   else if ((major == 10 && minor >= 6) || major > 10)
    393     memory_info_success = GetProcessMemoryInfoUsingTop(proc_info_entries_);
    394 
    395   // If top didn't work then fall back to ps.
    396   if (!memory_info_success) {
    397     memory_info_success = GetProcessMemoryInfoUsingPS(pid_list,
    398                                                       proc_info_entries_);
    399   }
    400 
    401   return memory_info_success;
    402 }
    403 
    404 // Clear all the stored information.
    405 void ProcessInfoSnapshot::Reset() {
    406   proc_info_entries_.clear();
    407 }
    408 
    409 bool ProcessInfoSnapshot::GetProcInfo(int pid,
    410                                       ProcInfoEntry* proc_info) const {
    411   std::map<int,ProcInfoEntry>::const_iterator it = proc_info_entries_.find(pid);
    412   if (it == proc_info_entries_.end())
    413     return false;
    414 
    415   *proc_info = it->second;
    416   return true;
    417 }
    418 
    419 bool ProcessInfoSnapshot::GetCommittedKBytesOfPID(
    420     int pid,
    421     base::CommittedKBytes* usage) const {
    422   // Try to avoid crashing on a bug; stats aren't usually so crucial.
    423   if (!usage) {
    424     NOTREACHED();
    425     return false;
    426   }
    427 
    428   // Failure of |GetProcInfo()| is "normal", due to racing.
    429   ProcInfoEntry proc_info;
    430   if (!GetProcInfo(pid, &proc_info)) {
    431     usage->priv = 0;
    432     usage->mapped = 0;
    433     usage->image = 0;
    434     return false;
    435   }
    436 
    437   usage->priv = proc_info.vsize / 1024;
    438   usage->mapped = 0;
    439   usage->image = 0;
    440   return true;
    441 }
    442 
    443 bool ProcessInfoSnapshot::GetWorkingSetKBytesOfPID(
    444     int pid,
    445     base::WorkingSetKBytes* ws_usage) const {
    446   // Try to avoid crashing on a bug; stats aren't usually so crucial.
    447   if (!ws_usage) {
    448     NOTREACHED();
    449     return false;
    450   }
    451 
    452   // Failure of |GetProcInfo()| is "normal", due to racing.
    453   ProcInfoEntry proc_info;
    454   if (!GetProcInfo(pid, &proc_info)) {
    455     ws_usage->priv = 0;
    456     ws_usage->shareable = 0;
    457     ws_usage->shared = 0;
    458     return false;
    459   }
    460 
    461   ws_usage->priv = proc_info.rprvt / 1024;
    462   ws_usage->shareable = proc_info.rss / 1024;
    463   ws_usage->shared = proc_info.rshrd / 1024;
    464   return true;
    465 }
    466