summaryrefslogtreecommitdiffstats
path: root/chrome/browser/process_info_snapshot_mac.cc
diff options
context:
space:
mode:
authorKristian Monsen <kristianm@google.com>2011-05-31 20:30:28 +0100
committerKristian Monsen <kristianm@google.com>2011-06-14 20:31:41 -0700
commit72a454cd3513ac24fbdd0e0cb9ad70b86a99b801 (patch)
tree382278a54ce7a744d62fa510a9a80688cc12434b /chrome/browser/process_info_snapshot_mac.cc
parentc4becdd46e31d261b930e4b5a539cbc1d45c23a6 (diff)
downloadexternal_chromium-72a454cd3513ac24fbdd0e0cb9ad70b86a99b801.zip
external_chromium-72a454cd3513ac24fbdd0e0cb9ad70b86a99b801.tar.gz
external_chromium-72a454cd3513ac24fbdd0e0cb9ad70b86a99b801.tar.bz2
Merge Chromium.org at r11.0.672.0: Initial merge by git.
Change-Id: I8b4aaf611a2a405fe3fe10e8a94ea7658645c192
Diffstat (limited to 'chrome/browser/process_info_snapshot_mac.cc')
-rw-r--r--chrome/browser/process_info_snapshot_mac.cc367
1 files changed, 331 insertions, 36 deletions
diff --git a/chrome/browser/process_info_snapshot_mac.cc b/chrome/browser/process_info_snapshot_mac.cc
index 85adb19..98216e0 100644
--- a/chrome/browser/process_info_snapshot_mac.cc
+++ b/chrome/browser/process_info_snapshot_mac.cc
@@ -4,17 +4,17 @@
#include "chrome/browser/process_info_snapshot.h"
+#include <sys/sysctl.h>
+
#include <sstream>
#include "base/command_line.h"
#include "base/logging.h"
#include "base/string_number_conversions.h"
#include "base/string_util.h"
+#include "base/sys_info.h"
#include "base/threading/thread.h"
-// Implementation for the Mac; calls '/bin/ps' for information when
-// |Sample()| is called.
-
// Default constructor.
ProcessInfoSnapshot::ProcessInfoSnapshot() { }
@@ -25,30 +25,117 @@ ProcessInfoSnapshot::~ProcessInfoSnapshot() {
const size_t ProcessInfoSnapshot::kMaxPidListSize = 1000;
-// Capture the information by calling '/bin/ps'.
-// Note: we ignore the "tsiz" (text size) display option of ps because it's
-// always zero (tested on 10.5 and 10.6).
-bool ProcessInfoSnapshot::Sample(std::vector<base::ProcessId> pid_list) {
- const char* kPsPathName = "/bin/ps";
- Reset();
+static bool GetKInfoForProcessID(pid_t pid, kinfo_proc* kinfo) {
+ int mib[] = {CTL_KERN, KERN_PROC, KERN_PROC_PID, pid};
+ size_t len = sizeof(*kinfo);
+ if (sysctl(mib, arraysize(mib), kinfo, &len, NULL, 0) != 0) {
+ PLOG(ERROR) << "sysctl() for KERN_PROC";
+ return false;
+ }
- // Nothing to do if no PIDs given.
- if (pid_list.size() == 0)
- return true;
- if (pid_list.size() > kMaxPidListSize) {
- // The spec says |pid_list| *must* not have more than this many entries.
+ if (len == 0) {
+ // If the process isn't found then sysctl returns a length of 0.
+ return false;
+ }
+
+ return true;
+}
+
+static bool GetExecutableNameForProcessID(
+ pid_t pid,
+ std::string* executable_name) {
+ if (!executable_name) {
NOTREACHED();
return false;
}
+ static int s_arg_max = 0;
+ if (s_arg_max == 0) {
+ int mib[] = {CTL_KERN, KERN_ARGMAX};
+ size_t size = sizeof(s_arg_max);
+ if (sysctl(mib, arraysize(mib), &s_arg_max, &size, NULL, 0) != 0)
+ PLOG(ERROR) << "sysctl() for KERN_ARGMAX";
+ }
+
+ if (s_arg_max == 0)
+ return false;
+
+ int mib[] = {CTL_KERN, KERN_PROCARGS, pid};
+ size_t size = s_arg_max;
+ executable_name->resize(s_arg_max + 1);
+ if (sysctl(mib, arraysize(mib), &(*executable_name)[0],
+ &size, NULL, 0) != 0) {
+ // Don't log the error since it's normal for this to fail.
+ return false;
+ }
+
+ // KERN_PROCARGS returns multiple NULL terminated strings. Truncate
+ // executable_name to just the first string.
+ size_t end_pos = executable_name->find('\0');
+ if (end_pos == std::string::npos) {
+ return false;
+ }
+
+ executable_name->resize(end_pos);
+ return true;
+}
+
+// Converts a byte unit such as 'K' or 'M' into the scale for the unit.
+// The scale can then be used to calculate the number of bytes in a value.
+// The units are based on humanize_number(). See:
+// http://www.opensource.apple.com/source/libutil/libutil-21/humanize_number.c
+static bool ConvertByteUnitToScale(char unit, uint64_t* out_scale) {
+ int shift = 0;
+ switch (unit) {
+ case 'B':
+ shift = 0;
+ break;
+ case 'K':
+ case 'k':
+ shift = 1;
+ break;
+ case 'M':
+ shift = 2;
+ break;
+ case 'G':
+ shift = 3;
+ break;
+ case 'T':
+ shift = 4;
+ break;
+ case 'P':
+ shift = 5;
+ break;
+ case 'E':
+ shift = 6;
+ break;
+ default:
+ return false;
+ }
+
+ uint64_t scale = 1;
+ for (int i = 0; i < shift; i++)
+ scale *= 1024;
+ *out_scale = scale;
+
+ return true;
+}
+
+// Capture the information by calling '/bin/ps'.
+// Note: we ignore the "tsiz" (text size) display option of ps because it's
+// always zero (tested on 10.5 and 10.6).
+static bool GetProcessMemoryInfoUsingPS(
+ const std::vector<base::ProcessId>& pid_list,
+ std::map<int,ProcessInfoSnapshot::ProcInfoEntry>& proc_info_entries) {
+ const char kPsPathName[] = "/bin/ps";
std::vector<std::string> argv;
argv.push_back(kPsPathName);
- // Get PID, PPID, (real) UID, effective UID, resident set size, virtual memory
- // size, and command.
+
+ // Get resident set size, virtual memory size.
argv.push_back("-o");
- argv.push_back("pid=,ppid=,ruid=,uid=,rss=,vsz=,comm=");
+ argv.push_back("pid=,rss=,vsz=");
// Only display the specified PIDs.
- for (std::vector<base::ProcessId>::iterator it = pid_list.begin();
+ for (std::vector<base::ProcessId>::const_iterator it = pid_list.begin();
it != pid_list.end(); ++it) {
argv.push_back("-p");
argv.push_back(base::Int64ToString(static_cast<int64>(*it)));
@@ -67,20 +154,21 @@ bool ProcessInfoSnapshot::Sample(std::vector<base::ProcessId> pid_list) {
// Process lines until done.
while (true) {
- ProcInfoEntry proc_info;
-
// The format is as specified above to ps (see ps(1)):
- // "-o pid=,ppid=,ruid=,uid=,rss=,vsz=,comm=".
+ // "-o pid=,rss=,vsz=".
// Try to read the PID; if we get it, we should be able to get the rest of
// the line.
- in >> proc_info.pid;
+ pid_t pid;
+ in >> pid;
if (in.eof())
break;
- in >> proc_info.ppid;
- in >> proc_info.uid;
- in >> proc_info.euid;
+
+ ProcessInfoSnapshot::ProcInfoEntry proc_info = proc_info_entries[pid];
+ proc_info.pid = pid;
in >> proc_info.rss;
in >> proc_info.vsize;
+ proc_info.rss *= 1024; // Convert from kilobytes to bytes.
+ proc_info.vsize *= 1024;
in.ignore(1, ' '); // Eat the space.
std::getline(in, proc_info.command); // Get the rest of the line.
if (!in.good()) {
@@ -88,24 +176,231 @@ bool ProcessInfoSnapshot::Sample(std::vector<base::ProcessId> pid_list) {
return false;
}
- // Make sure the new PID isn't already in our list.
- if (proc_info_entries_.find(proc_info.pid) != proc_info_entries_.end()) {
- LOG(ERROR) << "Duplicate PID in output from " << kPsPathName << ".";
- return false;
- }
-
if (!proc_info.pid || ! proc_info.vsize) {
LOG(WARNING) << "Invalid data from " << kPsPathName << ".";
return false;
}
// Record the process information.
- proc_info_entries_[proc_info.pid] = proc_info;
+ proc_info_entries[proc_info.pid] = proc_info;
+ }
+
+ return true;
+}
+
+static bool GetProcessMemoryInfoUsingTop(
+ std::map<int,ProcessInfoSnapshot::ProcInfoEntry>& proc_info_entries) {
+ const char kTopPathName[] = "/usr/bin/top";
+ std::vector<std::string> argv;
+ argv.push_back(kTopPathName);
+
+ // -stats tells top to print just the given fields as ordered.
+ argv.push_back("-stats");
+ argv.push_back("pid," // Process ID
+ "rsize," // Resident memory
+ "rshrd," // Resident shared memory
+ "rprvt," // Resident private memory
+ "vsize"); // Total virtual memory
+ // Run top in logging (non-interactive) mode.
+ argv.push_back("-l");
+ argv.push_back("1");
+ // Set the delay between updates to 0.
+ argv.push_back("-s");
+ argv.push_back("0");
+
+ std::string output;
+ CommandLine command_line(argv);
+ // Limit output read to a megabyte for safety.
+ if (!base::GetAppOutputRestricted(command_line, &output, 1024 * 1024)) {
+ LOG(ERROR) << "Failure running " << kTopPathName << " to acquire data.";
+ return false;
+ }
+
+ // Process lines until done. Lines should look something like this:
+ // PID RSIZE RSHRD RPRVT VSIZE
+ // 58539 1276K+ 336K+ 740K+ 2378M+
+ // 58485 1888K+ 592K+ 1332K+ 2383M+
+ std::istringstream top_in(output, std::istringstream::in);
+ std::string line;
+ while (std::getline(top_in, line)) {
+ std::istringstream in(line, std::istringstream::in);
+
+ // Try to read the PID.
+ pid_t pid;
+ in >> pid;
+ if (in.fail())
+ continue;
+
+ // Make sure that caller is interested in this process.
+ if (proc_info_entries.find(pid) == proc_info_entries.end())
+ continue;
+
+ // Skip the - or + sign that top puts after the pid.
+ in.get();
+
+ uint64_t values[4];
+ size_t i;
+ for (i = 0; i < arraysize(values); i++) {
+ in >> values[i];
+ if (in.fail())
+ break;
+ std::string unit;
+ in >> unit;
+ if (in.fail())
+ break;
+
+ if (unit.size() == 0)
+ break;
+
+ uint64_t scale;
+ if (!ConvertByteUnitToScale(unit[0], &scale))
+ break;
+ values[i] *= scale;
+ }
+ if (i != arraysize(values))
+ continue;
+
+ ProcessInfoSnapshot::ProcInfoEntry proc_info = proc_info_entries[pid];
+ proc_info.rss = values[0];
+ proc_info.rshrd = values[1];
+ proc_info.rprvt = values[2];
+ proc_info.vsize = values[3];
+ // Record the process information.
+ proc_info_entries[proc_info.pid] = proc_info;
}
return true;
}
+static bool GetProcessMemoryInfoUsingTop_10_5(
+ std::map<int,ProcessInfoSnapshot::ProcInfoEntry>& proc_info_entries) {
+ const char kTopPathName[] = "/usr/bin/top";
+ std::vector<std::string> argv;
+ argv.push_back(kTopPathName);
+
+ // -p tells top to print just the given fields as ordered.
+ argv.push_back("-p");
+ argv.push_back("^aaaaaaaaaaaaaaaaaaaa " // Process ID (PID)
+ "^jjjjjjjjjjjjjjjjjjjj " // Resident memory (RSIZE)
+ "^iiiiiiiiiiiiiiiiiiii " // Resident shared memory (RSHRD)
+ "^hhhhhhhhhhhhhhhhhhhh " // Resident private memory (RPRVT)
+ "^llllllllllllllllllll"); // Total virtual memory (VSIZE)
+ // Run top in logging (non-interactive) mode.
+ argv.push_back("-l");
+ argv.push_back("1");
+ // Set the delay between updates to 0.
+ argv.push_back("-s");
+ argv.push_back("0");
+
+ std::string output;
+ CommandLine command_line(argv);
+ // Limit output read to a megabyte for safety.
+ if (!base::GetAppOutputRestricted(command_line, &output, 1024 * 1024)) {
+ LOG(ERROR) << "Failure running " << kTopPathName << " to acquire data.";
+ return false;
+ }
+
+ // Process lines until done. Lines should look something like this:
+ // PID RSIZE RSHRD RPRVT VSIZE
+ // 16943 815104 262144 290816 18489344
+ // 16922 954368 720896 278528 18976768
+ std::istringstream top_in(output, std::istringstream::in);
+ std::string line;
+ while (std::getline(top_in, line)) {
+ std::istringstream in(line, std::istringstream::in);
+
+ // Try to read the PID.
+ pid_t pid;
+ in >> pid;
+ if (in.fail())
+ continue;
+
+ // Make sure that caller is interested in this process.
+ if (proc_info_entries.find(pid) == proc_info_entries.end())
+ continue;
+
+ uint64_t values[4];
+ size_t i;
+ for (i = 0; i < arraysize(values); i++) {
+ in >> values[i];
+ if (in.fail())
+ break;
+ }
+ if (i != arraysize(values))
+ continue;
+
+ ProcessInfoSnapshot::ProcInfoEntry proc_info = proc_info_entries[pid];
+ proc_info.rss = values[0];
+ proc_info.rshrd = values[1];
+ proc_info.rprvt = values[2];
+ proc_info.vsize = values[3];
+ // Record the process information.
+ proc_info_entries[proc_info.pid] = proc_info;
+ }
+
+ return true;
+}
+
+
+bool ProcessInfoSnapshot::Sample(std::vector<base::ProcessId> pid_list) {
+ Reset();
+
+ // Nothing to do if no PIDs given.
+ if (pid_list.size() == 0)
+ return true;
+ if (pid_list.size() > kMaxPidListSize) {
+ // The spec says |pid_list| *must* not have more than this many entries.
+ NOTREACHED();
+ return false;
+ }
+
+ // Get basic process info from KERN_PROC.
+ for (std::vector<base::ProcessId>::iterator it = pid_list.begin();
+ it != pid_list.end(); ++it) {
+ ProcInfoEntry proc_info;
+ proc_info.pid = *it;
+
+ kinfo_proc kinfo;
+ if (!GetKInfoForProcessID(*it, &kinfo))
+ return false;
+
+ proc_info.ppid = kinfo.kp_eproc.e_ppid;
+ proc_info.uid = kinfo.kp_eproc.e_pcred.p_ruid;
+ proc_info.euid = kinfo.kp_eproc.e_ucred.cr_uid;
+ // Note, p_comm is truncated to 16 characters.
+ proc_info.command = kinfo.kp_proc.p_comm;
+ proc_info_entries_[*it] = proc_info;
+ }
+
+ // Use KERN_PROCARGS to get the full executable name. This may fail if this
+ // process doesn't have privileges to inspect the target process.
+ for (std::vector<base::ProcessId>::iterator it = pid_list.begin();
+ it != pid_list.end(); ++it) {
+ std::string exectuable_name;
+ if (GetExecutableNameForProcessID(*it, &exectuable_name)) {
+ ProcInfoEntry proc_info = proc_info_entries_[*it];
+ proc_info.command = exectuable_name;
+ }
+ }
+
+ // Get memory information using top.
+ bool memory_info_success = false;
+ int32 major, minor, bugfix;
+ base::SysInfo::OperatingSystemVersionNumbers(&major, &minor, &bugfix);
+ if (major == 10 && minor == 5)
+ memory_info_success = GetProcessMemoryInfoUsingTop_10_5(proc_info_entries_);
+ else if ((major == 10 && minor >= 6) || major > 10)
+ memory_info_success = GetProcessMemoryInfoUsingTop(proc_info_entries_);
+
+ // If top didn't work then fall back to ps.
+ if (!memory_info_success) {
+ memory_info_success = GetProcessMemoryInfoUsingPS(pid_list,
+ proc_info_entries_);
+ }
+
+ return memory_info_success;
+}
+
// Clear all the stored information.
void ProcessInfoSnapshot::Reset() {
proc_info_entries_.clear();
@@ -139,7 +434,7 @@ bool ProcessInfoSnapshot::GetCommittedKBytesOfPID(
return false;
}
- usage->priv = proc_info.vsize;
+ usage->priv = proc_info.vsize / 1024;
usage->mapped = 0;
usage->image = 0;
return true;
@@ -163,8 +458,8 @@ bool ProcessInfoSnapshot::GetWorkingSetKBytesOfPID(
return false;
}
- ws_usage->priv = 0;
- ws_usage->shareable = proc_info.rss;
- ws_usage->shared = 0;
+ ws_usage->priv = proc_info.rprvt / 1024;
+ ws_usage->shareable = proc_info.rss / 1024;
+ ws_usage->shared = proc_info.rshrd / 1024;
return true;
}