Commit 18403b22 authored by thestig@chromium.org's avatar thestig@chromium.org

Linux: Stop using /proc/pid/smaps to calculate memory usage. It's too slow.

BUG=62854
TEST=See bug.

Review URL: http://codereview.chromium.org/9568046

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@124727 0039d316-1c4b-4281-b951-d872f2087c98
parent c0edde29
...@@ -17,7 +17,6 @@ ...@@ -17,7 +17,6 @@
#include "base/file_util.h" #include "base/file_util.h"
#include "base/logging.h" #include "base/logging.h"
#include "base/stringprintf.h"
#include "base/string_number_conversions.h" #include "base/string_number_conversions.h"
#include "base/string_split.h" #include "base/string_split.h"
#include "base/string_tokenizer.h" #include "base/string_tokenizer.h"
...@@ -27,24 +26,25 @@ ...@@ -27,24 +26,25 @@
namespace { namespace {
// Max score for the old oom_adj range. Used for conversion of new
// values to old values.
const int kMaxOldOomScore = 15;
enum ParsingState { enum ParsingState {
KEY_NAME, KEY_NAME,
KEY_VALUE KEY_VALUE
}; };
const char kProcDir[] = "/proc";
// Returns a FilePath to "/proc/pid".
FilePath GetProcPidDir(pid_t pid) {
return FilePath(kProcDir).Append(base::Int64ToString(pid));
}
// Reads /proc/<pid>/stat and populates |proc_stats| with the values split by // Reads /proc/<pid>/stat and populates |proc_stats| with the values split by
// spaces. Returns true if successful. // spaces. Returns true if successful.
bool GetProcStats(pid_t pid, std::vector<std::string>* proc_stats) { bool GetProcStats(pid_t pid, std::vector<std::string>* proc_stats) {
// Synchronously reading files in /proc is safe. // Synchronously reading files in /proc is safe.
base::ThreadRestrictions::ScopedAllowIO allow_io; base::ThreadRestrictions::ScopedAllowIO allow_io;
FilePath stat_file("/proc"); FilePath stat_file = GetProcPidDir(pid).Append("stat");
stat_file = stat_file.Append(base::IntToString(pid));
stat_file = stat_file.Append("stat");
std::string mem_stats; std::string mem_stats;
if (!file_util::ReadFileToString(stat_file, &mem_stats)) if (!file_util::ReadFileToString(stat_file, &mem_stats))
return false; return false;
...@@ -61,9 +61,7 @@ bool GetProcCmdline(pid_t pid, std::vector<std::string>* proc_cmd_line_args) { ...@@ -61,9 +61,7 @@ bool GetProcCmdline(pid_t pid, std::vector<std::string>* proc_cmd_line_args) {
// Synchronously reading files in /proc is safe. // Synchronously reading files in /proc is safe.
base::ThreadRestrictions::ScopedAllowIO allow_io; base::ThreadRestrictions::ScopedAllowIO allow_io;
FilePath cmd_line_file("/proc"); FilePath cmd_line_file = GetProcPidDir(pid).Append("cmdline");
cmd_line_file = cmd_line_file.Append(base::IntToString(pid));
cmd_line_file = cmd_line_file.Append("cmdline");
std::string cmd_line; std::string cmd_line;
if (!file_util::ReadFileToString(cmd_line_file, &cmd_line)) if (!file_util::ReadFileToString(cmd_line_file, &cmd_line))
return false; return false;
...@@ -80,7 +78,7 @@ int GetProcessCPU(pid_t pid) { ...@@ -80,7 +78,7 @@ int GetProcessCPU(pid_t pid) {
base::ThreadRestrictions::ScopedAllowIO allow_io; base::ThreadRestrictions::ScopedAllowIO allow_io;
// Use /proc/<pid>/task to find all threads and parse their /stat file. // Use /proc/<pid>/task to find all threads and parse their /stat file.
FilePath path = FilePath(base::StringPrintf("/proc/%d/task/", pid)); FilePath path = GetProcPidDir(pid).Append("task");
DIR* dir = opendir(path.value().c_str()); DIR* dir = opendir(path.value().c_str());
if (!dir) { if (!dir) {
...@@ -114,9 +112,7 @@ ProcessId GetParentProcessId(ProcessHandle process) { ...@@ -114,9 +112,7 @@ ProcessId GetParentProcessId(ProcessHandle process) {
// Synchronously reading files in /proc is safe. // Synchronously reading files in /proc is safe.
base::ThreadRestrictions::ScopedAllowIO allow_io; base::ThreadRestrictions::ScopedAllowIO allow_io;
FilePath stat_file("/proc"); FilePath stat_file = GetProcPidDir(process).Append("status");
stat_file = stat_file.Append(base::IntToString(process));
stat_file = stat_file.Append("status");
std::string status; std::string status;
if (!file_util::ReadFileToString(stat_file, &status)) if (!file_util::ReadFileToString(stat_file, &status))
return -1; return -1;
...@@ -146,9 +142,7 @@ ProcessId GetParentProcessId(ProcessHandle process) { ...@@ -146,9 +142,7 @@ ProcessId GetParentProcessId(ProcessHandle process) {
} }
FilePath GetProcessExecutablePath(ProcessHandle process) { FilePath GetProcessExecutablePath(ProcessHandle process) {
FilePath stat_file("/proc"); FilePath stat_file = GetProcPidDir(process).Append("exe");
stat_file = stat_file.Append(base::IntToString(process));
stat_file = stat_file.Append("exe");
FilePath exe_name; FilePath exe_name;
if (!file_util::ReadSymbolicLink(stat_file, &exe_name)) { if (!file_util::ReadSymbolicLink(stat_file, &exe_name)) {
// No such process. Happens frequently in e.g. TerminateAllChromeProcesses // No such process. Happens frequently in e.g. TerminateAllChromeProcesses
...@@ -159,7 +153,7 @@ FilePath GetProcessExecutablePath(ProcessHandle process) { ...@@ -159,7 +153,7 @@ FilePath GetProcessExecutablePath(ProcessHandle process) {
ProcessIterator::ProcessIterator(const ProcessFilter* filter) ProcessIterator::ProcessIterator(const ProcessFilter* filter)
: filter_(filter) { : filter_(filter) {
procfs_dir_ = opendir("/proc"); procfs_dir_ = opendir(kProcDir);
} }
ProcessIterator::~ProcessIterator() { ProcessIterator::~ProcessIterator() {
...@@ -210,7 +204,7 @@ bool ProcessIterator::CheckForNextProcess() { ...@@ -210,7 +204,7 @@ bool ProcessIterator::CheckForNextProcess() {
// Read the process's status. // Read the process's status.
char buf[NAME_MAX + 12]; char buf[NAME_MAX + 12];
sprintf(buf, "/proc/%s/stat", slot->d_name); sprintf(buf, "/proc/%s/stat", slot->d_name);
FILE *fp = fopen(buf, "r"); FILE* fp = fopen(buf, "r");
if (!fp) if (!fp)
continue; continue;
const char* result = fgets(buf, sizeof(buf), fp); const char* result = fgets(buf, sizeof(buf), fp);
...@@ -340,92 +334,43 @@ bool ProcessMetrics::GetMemoryBytes(size_t* private_bytes, ...@@ -340,92 +334,43 @@ bool ProcessMetrics::GetMemoryBytes(size_t* private_bytes,
return true; return true;
} }
// Private and Shared working set sizes are obtained from /proc/<pid>/smaps. // Private and Shared working set sizes are obtained from /proc/<pid>/statm.
// When that's not available, use the values from /proc<pid>/statm as a
// close approximation.
// See http://www.pixelbeat.org/scripts/ps_mem.py
bool ProcessMetrics::GetWorkingSetKBytes(WorkingSetKBytes* ws_usage) const { bool ProcessMetrics::GetWorkingSetKBytes(WorkingSetKBytes* ws_usage) const {
// Synchronously reading files in /proc is safe. // Use statm instead of smaps because smaps is:
base::ThreadRestrictions::ScopedAllowIO allow_io; // a) Large and slow to parse.
// b) Unavailable in the SUID sandbox.
FilePath proc_dir = FilePath("/proc").Append(base::IntToString(process_));
std::string smaps; // First we need to get the page size, since everything is measured in pages.
int private_kb = 0; // For details, see: man 5 proc.
int pss_kb = 0; const int page_size_kb = sysconf(_SC_PAGE_SIZE) / 1024;
bool have_pss = false; if (page_size_kb <= 0)
bool ret; return false;
std::string statm;
{ {
FilePath smaps_file = proc_dir.Append("smaps"); FilePath statm_file = GetProcPidDir(process_).Append("statm");
// Synchronously reading files in /proc is safe. // Synchronously reading files in /proc is safe.
base::ThreadRestrictions::ScopedAllowIO allow_io; base::ThreadRestrictions::ScopedAllowIO allow_io;
ret = file_util::ReadFileToString(smaps_file, &smaps); bool ret = file_util::ReadFileToString(statm_file, &statm);
}
if (ret && smaps.length() > 0) {
const std::string private_prefix = "Private_";
const std::string pss_prefix = "Pss";
StringTokenizer tokenizer(smaps, ":\n");
StringPiece last_key_name;
ParsingState state = KEY_NAME;
while (tokenizer.GetNext()) {
switch (state) {
case KEY_NAME:
last_key_name = tokenizer.token_piece();
state = KEY_VALUE;
break;
case KEY_VALUE:
if (last_key_name.empty()) {
NOTREACHED();
return false;
}
if (last_key_name.starts_with(private_prefix)) {
int cur;
base::StringToInt(tokenizer.token_piece(), &cur);
private_kb += cur;
} else if (last_key_name.starts_with(pss_prefix)) {
have_pss = true;
int cur;
base::StringToInt(tokenizer.token_piece(), &cur);
pss_kb += cur;
}
state = KEY_NAME;
break;
}
}
} else {
// Try statm if smaps is empty because of the SUID sandbox.
// First we need to get the page size though.
int page_size_kb = sysconf(_SC_PAGE_SIZE) / 1024;
if (page_size_kb <= 0)
return false;
std::string statm;
{
FilePath statm_file = proc_dir.Append("statm");
// Synchronously reading files in /proc is safe.
base::ThreadRestrictions::ScopedAllowIO allow_io;
ret = file_util::ReadFileToString(statm_file, &statm);
}
if (!ret || statm.length() == 0) if (!ret || statm.length() == 0)
return false; return false;
}
std::vector<std::string> statm_vec; std::vector<std::string> statm_vec;
base::SplitString(statm, ' ', &statm_vec); base::SplitString(statm, ' ', &statm_vec);
if (statm_vec.size() != 7) if (statm_vec.size() != 7)
return false; // Not the format we expect. return false; // Not the format we expect.
int statm_rss, statm_shared;
base::StringToInt(statm_vec[1], &statm_rss);
base::StringToInt(statm_vec[2], &statm_shared);
ws_usage->priv = (statm_rss - statm_shared) * page_size_kb;
ws_usage->shared = statm_shared * page_size_kb;
int statm1, statm2;
base::StringToInt(statm_vec[1], &statm1);
base::StringToInt(statm_vec[2], &statm2);
private_kb = (statm1 - statm2) * page_size_kb;
}
ws_usage->priv = private_kb;
// Sharable is not calculated, as it does not provide interesting data. // Sharable is not calculated, as it does not provide interesting data.
ws_usage->shareable = 0; ws_usage->shareable = 0;
ws_usage->shared = 0;
if (have_pss)
ws_usage->shared = pss_kb;
return true; return true;
} }
...@@ -479,9 +424,7 @@ bool ProcessMetrics::GetIOCounters(IoCounters* io_counters) const { ...@@ -479,9 +424,7 @@ bool ProcessMetrics::GetIOCounters(IoCounters* io_counters) const {
base::ThreadRestrictions::ScopedAllowIO allow_io; base::ThreadRestrictions::ScopedAllowIO allow_io;
std::string proc_io_contents; std::string proc_io_contents;
FilePath io_file("/proc"); FilePath io_file = GetProcPidDir(process_).Append("io");
io_file = io_file.Append(base::IntToString(process_));
io_file = io_file.Append("io");
if (!file_util::ReadFileToString(io_file, &proc_io_contents)) if (!file_util::ReadFileToString(io_file, &proc_io_contents))
return false; return false;
...@@ -745,8 +688,7 @@ bool AdjustOOMScore(ProcessId process, int score) { ...@@ -745,8 +688,7 @@ bool AdjustOOMScore(ProcessId process, int score) {
if (score < 0 || score > kMaxOomScore) if (score < 0 || score > kMaxOomScore)
return false; return false;
FilePath oom_path("/proc"); FilePath oom_path(GetProcPidDir(process));
oom_path = oom_path.Append(base::Int64ToString(process));
// Attempt to write the newer oom_score_adj file first. // Attempt to write the newer oom_score_adj file first.
FilePath oom_file = oom_path.AppendASCII("oom_score_adj"); FilePath oom_file = oom_path.AppendASCII("oom_score_adj");
...@@ -764,8 +706,12 @@ bool AdjustOOMScore(ProcessId process, int score) { ...@@ -764,8 +706,12 @@ bool AdjustOOMScore(ProcessId process, int score) {
// style file and translate the oom_adj score to the range 0-15. // style file and translate the oom_adj score to the range 0-15.
oom_file = oom_path.AppendASCII("oom_adj"); oom_file = oom_path.AppendASCII("oom_adj");
if (file_util::PathExists(oom_file)) { if (file_util::PathExists(oom_file)) {
std::string score_str = base::IntToString( // Max score for the old oom_adj range. Used for conversion of new
score * kMaxOldOomScore / kMaxOomScore); // values to old values.
const int kMaxOldOomScore = 15;
int converted_score = score * kMaxOldOomScore / kMaxOomScore;
std::string score_str = base::IntToString(converted_score);
DVLOG(1) << "Adjusting oom_adj of " << process << " to " << score_str; DVLOG(1) << "Adjusting oom_adj of " << process << " to " << score_str;
int score_len = static_cast<int>(score_str.length()); int score_len = static_cast<int>(score_str.length());
return (score_len == file_util::WriteFile(oom_file, return (score_len == file_util::WriteFile(oom_file,
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment