This source file includes following definitions.
- GetKInfoForProcessID
- GetExecutableNameForProcessID
- ConvertByteUnitToScale
- GetProcessMemoryInfoUsingPS
- GetProcessMemoryInfoUsingTop
- Sample
- Reset
- vsize
- GetProcInfo
- GetCommittedKBytesOfPID
- GetWorkingSetKBytesOfPID
#include "chrome/browser/process_info_snapshot.h"
#include <sys/sysctl.h>
#include <sstream>
#include "base/command_line.h"
#include "base/files/file_path.h"
#include "base/logging.h"
#include "base/process/launch.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_util.h"
#include "base/threading/thread.h"
ProcessInfoSnapshot::ProcessInfoSnapshot() { }
ProcessInfoSnapshot::~ProcessInfoSnapshot() {
Reset();
}
const size_t ProcessInfoSnapshot::kMaxPidListSize = 1000;
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;
}
if (len == 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) {
return false;
}
size_t end_pos = executable_name->find('\0');
if (end_pos == std::string::npos) {
return false;
}
executable_name->resize(end_pos);
return true;
}
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;
}
static bool GetProcessMemoryInfoUsingPS(
const std::vector<base::ProcessId>& pid_list,
std::map<int,ProcessInfoSnapshot::ProcInfoEntry>& proc_info_entries) {
const base::FilePath kProgram("/bin/ps");
CommandLine command_line(kProgram);
command_line.AppendArg("-o");
command_line.AppendArg("pid=,rss=,vsz=");
for (std::vector<base::ProcessId>::const_iterator it = pid_list.begin();
it != pid_list.end(); ++it) {
command_line.AppendArg("-p");
command_line.AppendArg(base::Int64ToString(static_cast<int64>(*it)));
}
std::string output;
if (!base::GetAppOutputRestricted(command_line, &output, 1024 * 1024)) {
LOG(ERROR) << "Failure running " << kProgram.value() << " to acquire data.";
return false;
}
std::istringstream in(output, std::istringstream::in);
while (true) {
pid_t pid;
in >> pid;
if (in.eof())
break;
ProcessInfoSnapshot::ProcInfoEntry proc_info = proc_info_entries[pid];
proc_info.pid = pid;
in >> proc_info.rss;
in >> proc_info.vsize;
proc_info.rss *= 1024;
proc_info.vsize *= 1024;
in.ignore(1, ' ');
std::getline(in, proc_info.command);
if (!in.good()) {
LOG(ERROR) << "Error parsing output from " << kProgram.value() << ".";
return false;
}
if (!proc_info.pid || ! proc_info.vsize) {
LOG(WARNING) << "Invalid data from " << kProgram.value() << ".";
return false;
}
proc_info_entries[proc_info.pid] = proc_info;
}
return true;
}
static bool GetProcessMemoryInfoUsingTop(
std::map<int,ProcessInfoSnapshot::ProcInfoEntry>& proc_info_entries) {
const base::FilePath kProgram("/usr/bin/top");
CommandLine command_line(kProgram);
command_line.AppendArg("-stats");
command_line.AppendArg("pid,"
"rsize,"
"rshrd,"
"rprvt,"
"vsize");
command_line.AppendArg("-l");
command_line.AppendArg("1");
command_line.AppendArg("-s");
command_line.AppendArg("0");
std::string output;
if (!base::GetAppOutputRestricted(command_line, &output, 1024 * 1024)) {
LOG(ERROR) << "Failure running " << kProgram.value() << " to acquire data.";
return false;
}
std::istringstream top_in(output, std::istringstream::in);
std::string line;
while (std::getline(top_in, line)) {
std::istringstream in(line, std::istringstream::in);
pid_t pid;
in >> pid;
if (in.fail())
continue;
if (proc_info_entries.find(pid) == proc_info_entries.end())
continue;
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.empty())
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];
proc_info_entries[proc_info.pid] = proc_info;
}
return true;
}
bool ProcessInfoSnapshot::Sample(std::vector<base::ProcessId> pid_list) {
Reset();
if (pid_list.empty())
return true;
if (pid_list.size() > kMaxPidListSize) {
NOTREACHED();
return false;
}
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;
proc_info.command = kinfo.kp_proc.p_comm;
proc_info_entries_[*it] = proc_info;
}
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;
}
}
bool memory_info_success = GetProcessMemoryInfoUsingTop(proc_info_entries_);
if (!memory_info_success) {
memory_info_success = GetProcessMemoryInfoUsingPS(pid_list,
proc_info_entries_);
}
return memory_info_success;
}
void ProcessInfoSnapshot::Reset() {
proc_info_entries_.clear();
}
ProcessInfoSnapshot::ProcInfoEntry::ProcInfoEntry()
: pid(0),
ppid(0),
uid(0),
euid(0),
rss(0),
rshrd(0),
rprvt(0),
vsize(0) {
}
bool ProcessInfoSnapshot::GetProcInfo(int pid,
ProcInfoEntry* proc_info) const {
std::map<int,ProcInfoEntry>::const_iterator it = proc_info_entries_.find(pid);
if (it == proc_info_entries_.end())
return false;
*proc_info = it->second;
return true;
}
bool ProcessInfoSnapshot::GetCommittedKBytesOfPID(
int pid,
base::CommittedKBytes* usage) const {
if (!usage) {
NOTREACHED();
return false;
}
ProcInfoEntry proc_info;
if (!GetProcInfo(pid, &proc_info)) {
usage->priv = 0;
usage->mapped = 0;
usage->image = 0;
return false;
}
usage->priv = proc_info.vsize / 1024;
usage->mapped = 0;
usage->image = 0;
return true;
}
bool ProcessInfoSnapshot::GetWorkingSetKBytesOfPID(
int pid,
base::WorkingSetKBytes* ws_usage) const {
if (!ws_usage) {
NOTREACHED();
return false;
}
ProcInfoEntry proc_info;
if (!GetProcInfo(pid, &proc_info)) {
ws_usage->priv = 0;
ws_usage->shareable = 0;
ws_usage->shared = 0;
return false;
}
ws_usage->priv = proc_info.rprvt / 1024;
ws_usage->shareable = proc_info.rss / 1024;
ws_usage->shared = proc_info.rshrd / 1024;
return true;
}