summaryrefslogtreecommitdiffstats
path: root/base/process
diff options
context:
space:
mode:
authorrsesek@chromium.org <rsesek@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-05-23 12:59:54 +0000
committerrsesek@chromium.org <rsesek@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-05-23 12:59:54 +0000
commit32f5e9a0783c3d03ec2351401d8c4100c153a499 (patch)
tree2a0d58988e8c1429380500961d5f20ce08dc5c60 /base/process
parent2ac90b1f8eff5bf3788f258bd8fbbcaaa023df91 (diff)
downloadchromium_src-32f5e9a0783c3d03ec2351401d8c4100c153a499.zip
chromium_src-32f5e9a0783c3d03ec2351401d8c4100c153a499.tar.gz
chromium_src-32f5e9a0783c3d03ec2351401d8c4100c153a499.tar.bz2
Split ProcessMetrics out of base/process_util.h and into base/process/process_metrics.h.
BUG=242290 R=brettw@chromium.org Review URL: https://codereview.chromium.org/15564006 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@201775 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'base/process')
-rw-r--r--base/process/internal_linux.cc142
-rw-r--r--base/process/internal_linux.h78
-rw-r--r--base/process/process_metrics.h234
-rw-r--r--base/process/process_metrics_freebsd.cc122
-rw-r--r--base/process/process_metrics_ios.cc47
-rw-r--r--base/process/process_metrics_linux.cc439
-rw-r--r--base/process/process_metrics_mac.cc325
-rw-r--r--base/process/process_metrics_openbsd.cc168
-rw-r--r--base/process/process_metrics_posix.cc21
-rw-r--r--base/process/process_metrics_win.cc315
10 files changed, 1891 insertions, 0 deletions
diff --git a/base/process/internal_linux.cc b/base/process/internal_linux.cc
new file mode 100644
index 0000000..c1fcaef
--- /dev/null
+++ b/base/process/internal_linux.cc
@@ -0,0 +1,142 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/process/internal_linux.h"
+
+#include <unistd.h>
+
+#include <string>
+#include <vector>
+
+#include "base/file_util.h"
+#include "base/logging.h"
+#include "base/string_util.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/string_split.h"
+#include "base/threading/thread_restrictions.h"
+
+namespace base {
+namespace internal {
+
+const char kProcDir[] = "/proc";
+
+const char kStatFile[] = "stat";
+
+base::FilePath GetProcPidDir(pid_t pid) {
+ return base::FilePath(kProcDir).Append(IntToString(pid));
+}
+
+pid_t ProcDirSlotToPid(const char* d_name) {
+ int i;
+ for (i = 0; i < NAME_MAX && d_name[i]; ++i) {
+ if (!IsAsciiDigit(d_name[i])) {
+ return 0;
+ }
+ }
+ if (i == NAME_MAX)
+ return 0;
+
+ // Read the process's command line.
+ pid_t pid;
+ std::string pid_string(d_name);
+ if (!StringToInt(pid_string, &pid)) {
+ NOTREACHED();
+ return 0;
+ }
+ return pid;
+}
+
+bool ReadProcStats(pid_t pid, std::string* buffer) {
+ buffer->clear();
+ // Synchronously reading files in /proc is safe.
+ ThreadRestrictions::ScopedAllowIO allow_io;
+
+ FilePath stat_file = internal::GetProcPidDir(pid).Append(kStatFile);
+ if (!file_util::ReadFileToString(stat_file, buffer)) {
+ DLOG(WARNING) << "Failed to get process stats.";
+ return false;
+ }
+ return !buffer->empty();
+}
+
+bool ParseProcStats(const std::string& stats_data,
+ std::vector<std::string>* proc_stats) {
+ // |stats_data| may be empty if the process disappeared somehow.
+ // e.g. http://crbug.com/145811
+ if (stats_data.empty())
+ return false;
+
+ // The stat file is formatted as:
+ // pid (process name) data1 data2 .... dataN
+ // Look for the closing paren by scanning backwards, to avoid being fooled by
+ // processes with ')' in the name.
+ size_t open_parens_idx = stats_data.find(" (");
+ size_t close_parens_idx = stats_data.rfind(") ");
+ if (open_parens_idx == std::string::npos ||
+ close_parens_idx == std::string::npos ||
+ open_parens_idx > close_parens_idx) {
+ DLOG(WARNING) << "Failed to find matched parens in '" << stats_data << "'";
+ NOTREACHED();
+ return false;
+ }
+ open_parens_idx++;
+
+ proc_stats->clear();
+ // PID.
+ proc_stats->push_back(stats_data.substr(0, open_parens_idx));
+ // Process name without parentheses.
+ proc_stats->push_back(
+ stats_data.substr(open_parens_idx + 1,
+ close_parens_idx - (open_parens_idx + 1)));
+
+ // Split the rest.
+ std::vector<std::string> other_stats;
+ SplitString(stats_data.substr(close_parens_idx + 2), ' ', &other_stats);
+ for (size_t i = 0; i < other_stats.size(); ++i)
+ proc_stats->push_back(other_stats[i]);
+ return true;
+}
+
+int GetProcStatsFieldAsInt(const std::vector<std::string>& proc_stats,
+ ProcStatsFields field_num) {
+ DCHECK_GE(field_num, VM_PPID);
+ CHECK_LT(static_cast<size_t>(field_num), proc_stats.size());
+
+ int value;
+ return StringToInt(proc_stats[field_num], &value) ? value : 0;
+}
+
+size_t GetProcStatsFieldAsSizeT(const std::vector<std::string>& proc_stats,
+ ProcStatsFields field_num) {
+ DCHECK_GE(field_num, VM_PPID);
+ CHECK_LT(static_cast<size_t>(field_num), proc_stats.size());
+
+ size_t value;
+ return StringToSizeT(proc_stats[field_num], &value) ? value : 0;
+}
+
+int ReadProcStatsAndGetFieldAsInt(pid_t pid,
+ ProcStatsFields field_num) {
+ std::string stats_data;
+ if (!ReadProcStats(pid, &stats_data))
+ return 0;
+ std::vector<std::string> proc_stats;
+ if (!ParseProcStats(stats_data, &proc_stats))
+ return 0;
+ return GetProcStatsFieldAsInt(proc_stats, field_num);
+}
+
+size_t ReadProcStatsAndGetFieldAsSizeT(pid_t pid,
+ ProcStatsFields field_num) {
+ std::string stats_data;
+ if (!ReadProcStats(pid, &stats_data))
+ return 0;
+ std::vector<std::string> proc_stats;
+ if (!ParseProcStats(stats_data, &proc_stats))
+ return 0;
+ return GetProcStatsFieldAsSizeT(proc_stats, field_num);
+}
+
+} // namespace internal
+} // namespace base
diff --git a/base/process/internal_linux.h b/base/process/internal_linux.h
new file mode 100644
index 0000000..88d32ee
--- /dev/null
+++ b/base/process/internal_linux.h
@@ -0,0 +1,78 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// This file contains internal routines that are called by other files in
+// base/process/.
+
+#ifndef BASE_PROCESS_LINUX_INTERNAL_H_
+#define BASE_PROCESS_LINUX_INTERNAL_H_
+
+#include "base/files/file_path.h"
+
+namespace base {
+namespace internal {
+
+// "/proc"
+extern const char kProcDir[];
+
+// "stat"
+extern const char kStatFile[];
+
+// Returns a FilePath to "/proc/pid".
+base::FilePath GetProcPidDir(pid_t pid);
+
+// Take a /proc directory entry named |d_name|, and if it is the directory for
+// a process, convert it to a pid_t.
+// Returns 0 on failure.
+// e.g. /proc/self/ will return 0, whereas /proc/1234 will return 1234.
+pid_t ProcDirSlotToPid(const char* d_name);
+
+// Reads /proc/<pid>/stat into |buffer|. Returns true if the file can be read
+// and is non-empty.
+bool ReadProcStats(pid_t pid, std::string* buffer);
+
+// Takes |stats_data| and populates |proc_stats| with the values split by
+// spaces. Taking into account the 2nd field may, in itself, contain spaces.
+// Returns true if successful.
+bool ParseProcStats(const std::string& stats_data,
+ std::vector<std::string>* proc_stats);
+
+// Fields from /proc/<pid>/stat, 0-based. See man 5 proc.
+// If the ordering ever changes, carefully review functions that use these
+// values.
+enum ProcStatsFields {
+ VM_COMM = 1, // Filename of executable, without parentheses.
+ VM_STATE = 2, // Letter indicating the state of the process.
+ VM_PPID = 3, // PID of the parent.
+ VM_PGRP = 4, // Process group id.
+ VM_UTIME = 13, // Time scheduled in user mode in clock ticks.
+ VM_STIME = 14, // Time scheduled in kernel mode in clock ticks.
+ VM_NUMTHREADS = 19, // Number of threads.
+ VM_VSIZE = 22, // Virtual memory size in bytes.
+ VM_RSS = 23, // Resident Set Size in pages.
+};
+
+// Reads the |field_num|th field from |proc_stats|. Returns 0 on failure.
+// This version does not handle the first 3 values, since the first value is
+// simply |pid|, and the next two values are strings.
+int GetProcStatsFieldAsInt(const std::vector<std::string>& proc_stats,
+ ProcStatsFields field_num);
+
+// Same as GetProcStatsFieldAsInt(), but for size_t values.
+size_t GetProcStatsFieldAsSizeT(const std::vector<std::string>& proc_stats,
+ ProcStatsFields field_num);
+
+// Convenience wrapper around GetProcStatsFieldAsInt(), ParseProcStats() and
+// ReadProcStats(). See GetProcStatsFieldAsInt() for details.
+int ReadProcStatsAndGetFieldAsInt(pid_t pid,
+ ProcStatsFields field_num);
+
+// Same as ReadProcStatsAndGetFieldAsInt() but for size_t values.
+size_t ReadProcStatsAndGetFieldAsSizeT(pid_t pid,
+ ProcStatsFields field_num);
+
+} // namespace internal
+} // namespace base
+
+#endif // BASE_PROCESS_LINUX_INTERNAL_H_
diff --git a/base/process/process_metrics.h b/base/process/process_metrics.h
new file mode 100644
index 0000000..4acc354
--- /dev/null
+++ b/base/process/process_metrics.h
@@ -0,0 +1,234 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// This file contains routines for gathering resource statistics for processes
+// running on the system.
+
+#ifndef BASE_PROCESS_PROCESS_METRICS_H_
+#define BASE_PROCESS_PROCESS_METRICS_H_
+
+#include <string>
+
+#include "base/base_export.h"
+#include "base/basictypes.h"
+#include "base/process.h"
+#include "base/time.h"
+
+#if defined(OS_MACOSX)
+#include <mach/mach.h>
+#endif
+
+namespace base {
+
+#if defined(OS_WIN)
+struct IoCounters : public IO_COUNTERS {
+};
+#elif defined(OS_POSIX)
+struct IoCounters {
+ uint64_t ReadOperationCount;
+ uint64_t WriteOperationCount;
+ uint64_t OtherOperationCount;
+ uint64_t ReadTransferCount;
+ uint64_t WriteTransferCount;
+ uint64_t OtherTransferCount;
+};
+#endif
+
+// Working Set (resident) memory usage broken down by
+//
+// On Windows:
+// priv (private): These pages (kbytes) cannot be shared with any other process.
+// shareable: These pages (kbytes) can be shared with other processes under
+// the right circumstances.
+// shared : These pages (kbytes) are currently shared with at least one
+// other process.
+//
+// On Linux:
+// priv: Pages mapped only by this process
+// shared: PSS or 0 if the kernel doesn't support this
+// shareable: 0
+//
+// On OS X: TODO(thakis): Revise.
+// priv: Memory.
+// shared: 0
+// shareable: 0
+struct WorkingSetKBytes {
+ WorkingSetKBytes() : priv(0), shareable(0), shared(0) {}
+ size_t priv;
+ size_t shareable;
+ size_t shared;
+};
+
+// Committed (resident + paged) memory usage broken down by
+// private: These pages cannot be shared with any other process.
+// mapped: These pages are mapped into the view of a section (backed by
+// pagefile.sys)
+// image: These pages are mapped into the view of an image section (backed by
+// file system)
+struct CommittedKBytes {
+ CommittedKBytes() : priv(0), mapped(0), image(0) {}
+ size_t priv;
+ size_t mapped;
+ size_t image;
+};
+
+// Free memory (Megabytes marked as free) in the 2G process address space.
+// total : total amount in megabytes marked as free. Maximum value is 2048.
+// largest : size of the largest contiguous amount of memory found. It is
+// always smaller or equal to FreeMBytes::total.
+// largest_ptr: starting address of the largest memory block.
+struct FreeMBytes {
+ size_t total;
+ size_t largest;
+ void* largest_ptr;
+};
+
+// Convert a POSIX timeval to microseconds.
+BASE_EXPORT int64 TimeValToMicroseconds(const struct timeval& tv);
+
+// Provides performance metrics for a specified process (CPU usage, memory and
+// IO counters). To use it, invoke CreateProcessMetrics() to get an instance
+// for a specific process, then access the information with the different get
+// methods.
+class BASE_EXPORT ProcessMetrics {
+ public:
+ ~ProcessMetrics();
+
+ // Creates a ProcessMetrics for the specified process.
+ // The caller owns the returned object.
+#if !defined(OS_MACOSX) || defined(OS_IOS)
+ static ProcessMetrics* CreateProcessMetrics(ProcessHandle process);
+#else
+ class PortProvider {
+ public:
+ virtual ~PortProvider() {}
+
+ // Should return the mach task for |process| if possible, or else
+ // |MACH_PORT_NULL|. Only processes that this returns tasks for will have
+ // metrics on OS X (except for the current process, which always gets
+ // metrics).
+ virtual mach_port_t TaskForPid(ProcessHandle process) const = 0;
+ };
+
+ // The port provider needs to outlive the ProcessMetrics object returned by
+ // this function. If NULL is passed as provider, the returned object
+ // only returns valid metrics if |process| is the current process.
+ static ProcessMetrics* CreateProcessMetrics(ProcessHandle process,
+ PortProvider* port_provider);
+#endif // !defined(OS_MACOSX) || defined(OS_IOS)
+
+ // Returns the current space allocated for the pagefile, in bytes (these pages
+ // may or may not be in memory). On Linux, this returns the total virtual
+ // memory size.
+ size_t GetPagefileUsage() const;
+ // Returns the peak space allocated for the pagefile, in bytes.
+ size_t GetPeakPagefileUsage() const;
+ // Returns the current working set size, in bytes. On Linux, this returns
+ // the resident set size.
+ size_t GetWorkingSetSize() const;
+ // Returns the peak working set size, in bytes.
+ size_t GetPeakWorkingSetSize() const;
+ // Returns private and sharedusage, in bytes. Private bytes is the amount of
+ // memory currently allocated to a process that cannot be shared. Returns
+ // false on platform specific error conditions. Note: |private_bytes|
+ // returns 0 on unsupported OSes: prior to XP SP2.
+ bool GetMemoryBytes(size_t* private_bytes,
+ size_t* shared_bytes);
+ // Fills a CommittedKBytes with both resident and paged
+ // memory usage as per definition of CommittedBytes.
+ void GetCommittedKBytes(CommittedKBytes* usage) const;
+ // Fills a WorkingSetKBytes containing resident private and shared memory
+ // usage in bytes, as per definition of WorkingSetBytes.
+ bool GetWorkingSetKBytes(WorkingSetKBytes* ws_usage) const;
+
+ // Computes the current process available memory for allocation.
+ // It does a linear scan of the address space querying each memory region
+ // for its free (unallocated) status. It is useful for estimating the memory
+ // load and fragmentation.
+ bool CalculateFreeMemory(FreeMBytes* free) const;
+
+ // Returns the CPU usage in percent since the last time this method was
+ // called. The first time this method is called it returns 0 and will return
+ // the actual CPU info on subsequent calls.
+ // On Windows, the CPU usage value is for all CPUs. So if you have 2 CPUs and
+ // your process is using all the cycles of 1 CPU and not the other CPU, this
+ // method returns 50.
+ double GetCPUUsage();
+
+ // Retrieves accounting information for all I/O operations performed by the
+ // process.
+ // If IO information is retrieved successfully, the function returns true
+ // and fills in the IO_COUNTERS passed in. The function returns false
+ // otherwise.
+ bool GetIOCounters(IoCounters* io_counters) const;
+
+ private:
+#if !defined(OS_MACOSX) || defined(OS_IOS)
+ explicit ProcessMetrics(ProcessHandle process);
+#else
+ ProcessMetrics(ProcessHandle process, PortProvider* port_provider);
+#endif // !defined(OS_MACOSX) || defined(OS_IOS)
+
+ ProcessHandle process_;
+
+ int processor_count_;
+
+ // Used to store the previous times and CPU usage counts so we can
+ // compute the CPU usage between calls.
+ int64 last_time_;
+ int64 last_system_time_;
+
+#if !defined(OS_IOS)
+#if defined(OS_MACOSX)
+ // Queries the port provider if it's set.
+ mach_port_t TaskForPid(ProcessHandle process) const;
+
+ PortProvider* port_provider_;
+#elif defined(OS_POSIX)
+ // Jiffie count at the last_time_ we updated.
+ int last_cpu_;
+#endif // defined(OS_POSIX)
+#endif // !defined(OS_IOS)
+
+ DISALLOW_COPY_AND_ASSIGN(ProcessMetrics);
+};
+
+// Returns the memory committed by the system in KBytes.
+// Returns 0 if it can't compute the commit charge.
+BASE_EXPORT size_t GetSystemCommitCharge();
+
+#if defined(OS_LINUX) || defined(OS_ANDROID)
+// Parse the data found in /proc/<pid>/stat and return the sum of the
+// CPU-related ticks. Returns -1 on parse error.
+// Exposed for testing.
+BASE_EXPORT int ParseProcStatCPU(const std::string& input);
+
+// Data from /proc/meminfo about system-wide memory consumption.
+// Values are in KB.
+struct BASE_EXPORT SystemMemoryInfoKB {
+ SystemMemoryInfoKB();
+
+ int total;
+ int free;
+ int buffers;
+ int cached;
+ int active_anon;
+ int inactive_anon;
+ int active_file;
+ int inactive_file;
+ int shmem;
+
+ // Gem data will be -1 if not supported.
+ int gem_objects;
+ long long gem_size;
+};
+// Retrieves data from /proc/meminfo about system-wide memory consumption.
+// Fills in the provided |meminfo| structure. Returns true on success.
+// Exposed for memory debugging widget.
+BASE_EXPORT bool GetSystemMemoryInfo(SystemMemoryInfoKB* meminfo);
+#endif // defined(OS_LINUX) || defined(OS_ANDROID)
+
+} // namespace base
+
+#endif // BASE_PROCESS_PROCESS_METRICS_H_
diff --git a/base/process/process_metrics_freebsd.cc b/base/process/process_metrics_freebsd.cc
new file mode 100644
index 0000000..019454c
--- /dev/null
+++ b/base/process/process_metrics_freebsd.cc
@@ -0,0 +1,122 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/process/process_metrics.h"
+
+namespace base {
+
+ProcessMetrics::ProcessMetrics(ProcessHandle process)
+ : process_(process),
+ last_time_(0),
+ last_system_time_(0),
+ last_cpu_(0) {
+ processor_count_ = base::SysInfo::NumberOfProcessors();
+}
+
+// static
+ProcessMetrics* ProcessMetrics::CreateProcessMetrics(ProcessHandle process) {
+ return new ProcessMetrics(process);
+}
+
+size_t ProcessMetrics::GetPagefileUsage() const {
+ struct kinfo_proc info;
+ int mib[] = { CTL_KERN, KERN_PROC, KERN_PROC_PID, process_ };
+ size_t length = sizeof(info);
+
+ if (sysctl(mib, arraysize(mib), &info, &length, NULL, 0) < 0)
+ return 0;
+
+ return info.ki_size;
+}
+
+size_t ProcessMetrics::GetPeakPagefileUsage() const {
+ return 0;
+}
+
+size_t ProcessMetrics::GetWorkingSetSize() const {
+ struct kinfo_proc info;
+ int mib[] = { CTL_KERN, KERN_PROC, KERN_PROC_PID, process_ };
+ size_t length = sizeof(info);
+
+ if (sysctl(mib, arraysize(mib), &info, &length, NULL, 0) < 0)
+ return 0;
+
+ return info.ki_rssize * getpagesize();
+}
+
+size_t ProcessMetrics::GetPeakWorkingSetSize() const {
+ return 0;
+}
+
+bool ProcessMetrics::GetMemoryBytes(size_t* private_bytes,
+ size_t* shared_bytes) {
+ WorkingSetKBytes ws_usage;
+ if (!GetWorkingSetKBytes(&ws_usage))
+ return false;
+
+ if (private_bytes)
+ *private_bytes = ws_usage.priv << 10;
+
+ if (shared_bytes)
+ *shared_bytes = ws_usage.shared * 1024;
+
+ return true;
+}
+
+bool ProcessMetrics::GetWorkingSetKBytes(WorkingSetKBytes* ws_usage) const {
+// TODO(bapt) be sure we can't be precise
+ size_t priv = GetWorkingSetSize();
+ if (!priv)
+ return false;
+ ws_usage->priv = priv / 1024;
+ ws_usage->shareable = 0;
+ ws_usage->shared = 0;
+
+ return true;
+}
+
+double ProcessMetrics::GetCPUUsage() {
+ struct kinfo_proc info;
+ int mib[] = { CTL_KERN, KERN_PROC, KERN_PROC_PID, process_ };
+ size_t length = sizeof(info);
+
+ struct timeval now;
+ int retval = gettimeofday(&now, NULL);
+ if (retval)
+ return 0;
+
+ if (sysctl(mib, arraysize(mib), &info, &length, NULL, 0) < 0)
+ return 0;
+
+ return (info.ki_pctcpu / FSCALE) * 100.0;
+}
+
+bool ProcessMetrics::GetIOCounters(IoCounters* io_counters) const {
+ return false;
+}
+
+size_t GetSystemCommitCharge() {
+ int mib[2], pagesize;
+ unsigned long mem_total, mem_free, mem_inactive;
+ size_t length = sizeof(mem_total);
+
+ if (sysctl(mib, arraysize(mib), &mem_total, &length, NULL, 0) < 0)
+ return 0;
+
+ length = sizeof(mem_free);
+ if (sysctlbyname("vm.stats.vm.v_free_count", &mem_free, &length, NULL, 0) < 0)
+ return 0;
+
+ length = sizeof(mem_inactive);
+ if (sysctlbyname("vm.stats.vm.v_inactive_count", &mem_inactive, &length,
+ NULL, 0) < 0) {
+ return 0;
+ }
+
+ pagesize = getpagesize();
+
+ return mem_total - (mem_free*pagesize) - (mem_inactive*pagesize);
+}
+
+} // namespace base
diff --git a/base/process/process_metrics_ios.cc b/base/process/process_metrics_ios.cc
new file mode 100644
index 0000000..9f0e8c6b
--- /dev/null
+++ b/base/process/process_metrics_ios.cc
@@ -0,0 +1,47 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/process/process_metrics.h"
+
+#include <mach/task.h>
+
+namespace base {
+
+namespace {
+
+bool GetTaskInfo(task_basic_info_64* task_info_data) {
+ mach_msg_type_number_t count = TASK_BASIC_INFO_64_COUNT;
+ kern_return_t kr = task_info(mach_task_self(),
+ TASK_BASIC_INFO_64,
+ reinterpret_cast<task_info_t>(task_info_data),
+ &count);
+ return kr == KERN_SUCCESS;
+}
+
+} // namespace
+
+ProcessMetrics::ProcessMetrics(ProcessHandle process) {}
+
+ProcessMetrics::~ProcessMetrics() {}
+
+// static
+ProcessMetrics* ProcessMetrics::CreateProcessMetrics(ProcessHandle process) {
+ return new ProcessMetrics(process);
+}
+
+size_t ProcessMetrics::GetPagefileUsage() const {
+ task_basic_info_64 task_info_data;
+ if (!GetTaskInfo(&task_info_data))
+ return 0;
+ return task_info_data.virtual_size;
+}
+
+size_t ProcessMetrics::GetWorkingSetSize() const {
+ task_basic_info_64 task_info_data;
+ if (!GetTaskInfo(&task_info_data))
+ return 0;
+ return task_info_data.resident_size;
+}
+
+} // namespace base
diff --git a/base/process/process_metrics_linux.cc b/base/process/process_metrics_linux.cc
new file mode 100644
index 0000000..b52d356
--- /dev/null
+++ b/base/process/process_metrics_linux.cc
@@ -0,0 +1,439 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/process/process_metrics.h"
+
+#include <dirent.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include "base/file_util.h"
+#include "base/logging.h"
+#include "base/process/internal_linux.h"
+#include "base/string_util.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/string_split.h"
+#include "base/strings/string_tokenizer.h"
+#include "base/sys_info.h"
+#include "base/threading/thread_restrictions.h"
+
+namespace base {
+
+namespace {
+
+enum ParsingState {
+ KEY_NAME,
+ KEY_VALUE
+};
+
+// Read /proc/<pid>/status and returns the value for |field|, or 0 on failure.
+// Only works for fields in the form of "Field: value kB".
+size_t ReadProcStatusAndGetFieldAsSizeT(pid_t pid, const std::string& field) {
+ FilePath stat_file = internal::GetProcPidDir(pid).Append("status");
+ std::string status;
+ {
+ // Synchronously reading files in /proc is safe.
+ ThreadRestrictions::ScopedAllowIO allow_io;
+ if (!file_util::ReadFileToString(stat_file, &status))
+ return 0;
+ }
+
+ StringTokenizer tokenizer(status, ":\n");
+ ParsingState state = KEY_NAME;
+ StringPiece last_key_name;
+ while (tokenizer.GetNext()) {
+ switch (state) {
+ case KEY_NAME:
+ last_key_name = tokenizer.token_piece();
+ state = KEY_VALUE;
+ break;
+ case KEY_VALUE:
+ DCHECK(!last_key_name.empty());
+ if (last_key_name == field) {
+ std::string value_str;
+ tokenizer.token_piece().CopyToString(&value_str);
+ std::string value_str_trimmed;
+ TrimWhitespaceASCII(value_str, TRIM_ALL, &value_str_trimmed);
+ std::vector<std::string> split_value_str;
+ SplitString(value_str_trimmed, ' ', &split_value_str);
+ if (split_value_str.size() != 2 || split_value_str[1] != "kB") {
+ NOTREACHED();
+ return 0;
+ }
+ size_t value;
+ if (!StringToSizeT(split_value_str[0], &value)) {
+ NOTREACHED();
+ return 0;
+ }
+ return value;
+ }
+ state = KEY_NAME;
+ break;
+ }
+ }
+ NOTREACHED();
+ return 0;
+}
+
+// Get the total CPU of a single process. Return value is number of jiffies
+// on success or -1 on error.
+int GetProcessCPU(pid_t pid) {
+ // Use /proc/<pid>/task to find all threads and parse their /stat file.
+ FilePath task_path = internal::GetProcPidDir(pid).Append("task");
+
+ DIR* dir = opendir(task_path.value().c_str());
+ if (!dir) {
+ DPLOG(ERROR) << "opendir(" << task_path.value() << ")";
+ return -1;
+ }
+
+ int total_cpu = 0;
+ while (struct dirent* ent = readdir(dir)) {
+ pid_t tid = internal::ProcDirSlotToPid(ent->d_name);
+ if (!tid)
+ continue;
+
+ // Synchronously reading files in /proc is safe.
+ ThreadRestrictions::ScopedAllowIO allow_io;
+
+ std::string stat;
+ FilePath stat_path =
+ task_path.Append(ent->d_name).Append(internal::kStatFile);
+ if (file_util::ReadFileToString(stat_path, &stat)) {
+ int cpu = ParseProcStatCPU(stat);
+ if (cpu > 0)
+ total_cpu += cpu;
+ }
+ }
+ closedir(dir);
+
+ return total_cpu;
+}
+
+} // namespace
+
+// static
+ProcessMetrics* ProcessMetrics::CreateProcessMetrics(ProcessHandle process) {
+ return new ProcessMetrics(process);
+}
+
+// On linux, we return vsize.
+size_t ProcessMetrics::GetPagefileUsage() const {
+ return internal::ReadProcStatsAndGetFieldAsSizeT(process_,
+ internal::VM_VSIZE);
+}
+
+// On linux, we return the high water mark of vsize.
+size_t ProcessMetrics::GetPeakPagefileUsage() const {
+ return ReadProcStatusAndGetFieldAsSizeT(process_, "VmPeak") * 1024;
+}
+
+// On linux, we return RSS.
+size_t ProcessMetrics::GetWorkingSetSize() const {
+ return internal::ReadProcStatsAndGetFieldAsSizeT(process_, internal::VM_RSS) *
+ getpagesize();
+}
+
+// On linux, we return the high water mark of RSS.
+size_t ProcessMetrics::GetPeakWorkingSetSize() const {
+ return ReadProcStatusAndGetFieldAsSizeT(process_, "VmHWM") * 1024;
+}
+
+bool ProcessMetrics::GetMemoryBytes(size_t* private_bytes,
+ size_t* shared_bytes) {
+ WorkingSetKBytes ws_usage;
+ if (!GetWorkingSetKBytes(&ws_usage))
+ return false;
+
+ if (private_bytes)
+ *private_bytes = ws_usage.priv * 1024;
+
+ if (shared_bytes)
+ *shared_bytes = ws_usage.shared * 1024;
+
+ return true;
+}
+
+// Private and Shared working set sizes are obtained from /proc/<pid>/statm.
+bool ProcessMetrics::GetWorkingSetKBytes(WorkingSetKBytes* ws_usage) const {
+ // Use statm instead of smaps because smaps is:
+ // a) Large and slow to parse.
+ // b) Unavailable in the SUID sandbox.
+
+ // First we need to get the page size, since everything is measured in pages.
+ // For details, see: man 5 proc.
+ const int page_size_kb = getpagesize() / 1024;
+ if (page_size_kb <= 0)
+ return false;
+
+ std::string statm;
+ {
+ FilePath statm_file = internal::GetProcPidDir(process_).Append("statm");
+ // Synchronously reading files in /proc is safe.
+ ThreadRestrictions::ScopedAllowIO allow_io;
+ bool ret = file_util::ReadFileToString(statm_file, &statm);
+ if (!ret || statm.length() == 0)
+ return false;
+ }
+
+ std::vector<std::string> statm_vec;
+ SplitString(statm, ' ', &statm_vec);
+ if (statm_vec.size() != 7)
+ return false; // Not the format we expect.
+
+ int statm_rss, statm_shared;
+ StringToInt(statm_vec[1], &statm_rss);
+ StringToInt(statm_vec[2], &statm_shared);
+
+ ws_usage->priv = (statm_rss - statm_shared) * page_size_kb;
+ ws_usage->shared = statm_shared * page_size_kb;
+
+ // Sharable is not calculated, as it does not provide interesting data.
+ ws_usage->shareable = 0;
+
+ return true;
+}
+
+double ProcessMetrics::GetCPUUsage() {
+ // This queries the /proc-specific scaling factor which is
+ // conceptually the system hertz. To dump this value on another
+ // system, try
+ // od -t dL /proc/self/auxv
+ // and look for the number after 17 in the output; mine is
+ // 0000040 17 100 3 134512692
+ // which means the answer is 100.
+ // It may be the case that this value is always 100.
+ static const int kHertz = sysconf(_SC_CLK_TCK);
+
+ struct timeval now;
+ int retval = gettimeofday(&now, NULL);
+ if (retval)
+ return 0;
+ int64 time = TimeValToMicroseconds(now);
+
+ if (last_time_ == 0) {
+ // First call, just set the last values.
+ last_time_ = time;
+ last_cpu_ = GetProcessCPU(process_);
+ return 0;
+ }
+
+ int64 time_delta = time - last_time_;
+ DCHECK_NE(time_delta, 0);
+ if (time_delta == 0)
+ return 0;
+
+ int cpu = GetProcessCPU(process_);
+
+ // We have the number of jiffies in the time period. Convert to percentage.
+ // Note this means we will go *over* 100 in the case where multiple threads
+ // are together adding to more than one CPU's worth.
+ int percentage = 100 * (cpu - last_cpu_) /
+ (kHertz * TimeDelta::FromMicroseconds(time_delta).InSecondsF());
+
+ last_time_ = time;
+ last_cpu_ = cpu;
+
+ return percentage;
+}
+
+// To have /proc/self/io file you must enable CONFIG_TASK_IO_ACCOUNTING
+// in your kernel configuration.
+bool ProcessMetrics::GetIOCounters(IoCounters* io_counters) const {
+ // Synchronously reading files in /proc is safe.
+ ThreadRestrictions::ScopedAllowIO allow_io;
+
+ std::string proc_io_contents;
+ FilePath io_file = internal::GetProcPidDir(process_).Append("io");
+ if (!file_util::ReadFileToString(io_file, &proc_io_contents))
+ return false;
+
+ (*io_counters).OtherOperationCount = 0;
+ (*io_counters).OtherTransferCount = 0;
+
+ StringTokenizer tokenizer(proc_io_contents, ": \n");
+ ParsingState state = KEY_NAME;
+ StringPiece last_key_name;
+ while (tokenizer.GetNext()) {
+ switch (state) {
+ case KEY_NAME:
+ last_key_name = tokenizer.token_piece();
+ state = KEY_VALUE;
+ break;
+ case KEY_VALUE:
+ DCHECK(!last_key_name.empty());
+ if (last_key_name == "syscr") {
+ StringToInt64(tokenizer.token_piece(),
+ reinterpret_cast<int64*>(&(*io_counters).ReadOperationCount));
+ } else if (last_key_name == "syscw") {
+ StringToInt64(tokenizer.token_piece(),
+ reinterpret_cast<int64*>(&(*io_counters).WriteOperationCount));
+ } else if (last_key_name == "rchar") {
+ StringToInt64(tokenizer.token_piece(),
+ reinterpret_cast<int64*>(&(*io_counters).ReadTransferCount));
+ } else if (last_key_name == "wchar") {
+ StringToInt64(tokenizer.token_piece(),
+ reinterpret_cast<int64*>(&(*io_counters).WriteTransferCount));
+ }
+ state = KEY_NAME;
+ break;
+ }
+ }
+ return true;
+}
+
+ProcessMetrics::ProcessMetrics(ProcessHandle process)
+ : process_(process),
+ last_time_(0),
+ last_system_time_(0),
+ last_cpu_(0) {
+ processor_count_ = base::SysInfo::NumberOfProcessors();
+}
+
+size_t GetSystemCommitCharge() {
+ SystemMemoryInfoKB meminfo;
+ if (!GetSystemMemoryInfo(&meminfo))
+ return 0;
+ return meminfo.total - meminfo.free - meminfo.buffers - meminfo.cached;
+}
+
+// Exposed for testing.
+int ParseProcStatCPU(const std::string& input) {
+ std::vector<std::string> proc_stats;
+ if (!internal::ParseProcStats(input, &proc_stats))
+ return -1;
+
+ if (proc_stats.size() <= internal::VM_STIME)
+ return -1;
+ int utime = GetProcStatsFieldAsInt(proc_stats, internal::VM_UTIME);
+ int stime = GetProcStatsFieldAsInt(proc_stats, internal::VM_STIME);
+ return utime + stime;
+}
+
+namespace {
+
+// The format of /proc/meminfo is:
+//
+// MemTotal: 8235324 kB
+// MemFree: 1628304 kB
+// Buffers: 429596 kB
+// Cached: 4728232 kB
+// ...
+const size_t kMemTotalIndex = 1;
+const size_t kMemFreeIndex = 4;
+const size_t kMemBuffersIndex = 7;
+const size_t kMemCachedIndex = 10;
+const size_t kMemActiveAnonIndex = 22;
+const size_t kMemInactiveAnonIndex = 25;
+const size_t kMemActiveFileIndex = 28;
+const size_t kMemInactiveFileIndex = 31;
+
+} // namespace
+
+SystemMemoryInfoKB::SystemMemoryInfoKB()
+ : total(0),
+ free(0),
+ buffers(0),
+ cached(0),
+ active_anon(0),
+ inactive_anon(0),
+ active_file(0),
+ inactive_file(0),
+ shmem(0),
+ gem_objects(-1),
+ gem_size(-1) {
+}
+
+bool GetSystemMemoryInfo(SystemMemoryInfoKB* meminfo) {
+ // Synchronously reading files in /proc is safe.
+ ThreadRestrictions::ScopedAllowIO allow_io;
+
+ // Used memory is: total - free - buffers - caches
+ FilePath meminfo_file("/proc/meminfo");
+ std::string meminfo_data;
+ if (!file_util::ReadFileToString(meminfo_file, &meminfo_data)) {
+ DLOG(WARNING) << "Failed to open " << meminfo_file.value();
+ return false;
+ }
+ std::vector<std::string> meminfo_fields;
+ SplitStringAlongWhitespace(meminfo_data, &meminfo_fields);
+
+ if (meminfo_fields.size() < kMemCachedIndex) {
+ DLOG(WARNING) << "Failed to parse " << meminfo_file.value()
+ << ". Only found " << meminfo_fields.size() << " fields.";
+ return false;
+ }
+
+ DCHECK_EQ(meminfo_fields[kMemTotalIndex-1], "MemTotal:");
+ DCHECK_EQ(meminfo_fields[kMemFreeIndex-1], "MemFree:");
+ DCHECK_EQ(meminfo_fields[kMemBuffersIndex-1], "Buffers:");
+ DCHECK_EQ(meminfo_fields[kMemCachedIndex-1], "Cached:");
+ DCHECK_EQ(meminfo_fields[kMemActiveAnonIndex-1], "Active(anon):");
+ DCHECK_EQ(meminfo_fields[kMemInactiveAnonIndex-1], "Inactive(anon):");
+ DCHECK_EQ(meminfo_fields[kMemActiveFileIndex-1], "Active(file):");
+ DCHECK_EQ(meminfo_fields[kMemInactiveFileIndex-1], "Inactive(file):");
+
+ StringToInt(meminfo_fields[kMemTotalIndex], &meminfo->total);
+ StringToInt(meminfo_fields[kMemFreeIndex], &meminfo->free);
+ StringToInt(meminfo_fields[kMemBuffersIndex], &meminfo->buffers);
+ StringToInt(meminfo_fields[kMemCachedIndex], &meminfo->cached);
+ StringToInt(meminfo_fields[kMemActiveAnonIndex], &meminfo->active_anon);
+ StringToInt(meminfo_fields[kMemInactiveAnonIndex],
+ &meminfo->inactive_anon);
+ StringToInt(meminfo_fields[kMemActiveFileIndex], &meminfo->active_file);
+ StringToInt(meminfo_fields[kMemInactiveFileIndex],
+ &meminfo->inactive_file);
+#if defined(OS_CHROMEOS)
+ // Chrome OS has a tweaked kernel that allows us to query Shmem, which is
+ // usually video memory otherwise invisible to the OS. Unfortunately, the
+ // meminfo format varies on different hardware so we have to search for the
+ // string. It always appears after "Cached:".
+ for (size_t i = kMemCachedIndex+2; i < meminfo_fields.size(); i += 3) {
+ if (meminfo_fields[i] == "Shmem:") {
+ StringToInt(meminfo_fields[i+1], &meminfo->shmem);
+ break;
+ }
+ }
+
+ // Report on Chrome OS GEM object graphics memory. /var/run/debugfs_gpu is a
+ // bind mount into /sys/kernel/debug and synchronously reading the in-memory
+ // files in /sys is fast.
+#if defined(ARCH_CPU_ARM_FAMILY)
+ FilePath geminfo_file("/var/run/debugfs_gpu/exynos_gem_objects");
+#else
+ FilePath geminfo_file("/var/run/debugfs_gpu/i915_gem_objects");
+#endif
+ std::string geminfo_data;
+ meminfo->gem_objects = -1;
+ meminfo->gem_size = -1;
+ if (file_util::ReadFileToString(geminfo_file, &geminfo_data)) {
+ int gem_objects = -1;
+ long long gem_size = -1;
+ int num_res = sscanf(geminfo_data.c_str(),
+ "%d objects, %lld bytes",
+ &gem_objects, &gem_size);
+ if (num_res == 2) {
+ meminfo->gem_objects = gem_objects;
+ meminfo->gem_size = gem_size;
+ }
+ }
+
+#if defined(ARCH_CPU_ARM_FAMILY)
+ // Incorporate Mali graphics memory if present.
+ FilePath mali_memory_file("/sys/devices/platform/mali.0/memory");
+ std::string mali_memory_data;
+ if (file_util::ReadFileToString(mali_memory_file, &mali_memory_data)) {
+ long long mali_size = -1;
+ int num_res = sscanf(mali_memory_data.c_str(), "%lld bytes", &mali_size);
+ if (num_res == 1)
+ meminfo->gem_size += mali_size;
+ }
+#endif // defined(ARCH_CPU_ARM_FAMILY)
+#endif // defined(OS_CHROMEOS)
+
+ return true;
+}
+
+} // namespace base
diff --git a/base/process/process_metrics_mac.cc b/base/process/process_metrics_mac.cc
new file mode 100644
index 0000000..74da992
--- /dev/null
+++ b/base/process/process_metrics_mac.cc
@@ -0,0 +1,325 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/process/process_metrics.h"
+
+#include <mach/mach.h>
+#include <mach/mach_vm.h>
+#include <mach/shared_region.h>
+#include <sys/sysctl.h>
+
+#include "base/hash_tables.h"
+#include "base/logging.h"
+#include "base/mac/scoped_mach_port.h"
+#include "base/sys_info.h"
+
+namespace base {
+
+namespace {
+
+bool GetTaskInfo(mach_port_t task, task_basic_info_64* task_info_data) {
+ if (task == MACH_PORT_NULL)
+ return false;
+ mach_msg_type_number_t count = TASK_BASIC_INFO_64_COUNT;
+ kern_return_t kr = task_info(task,
+ TASK_BASIC_INFO_64,
+ reinterpret_cast<task_info_t>(task_info_data),
+ &count);
+ // Most likely cause for failure: |task| is a zombie.
+ return kr == KERN_SUCCESS;
+}
+
+bool GetCPUTypeForProcess(pid_t pid, cpu_type_t* cpu_type) {
+ size_t len = sizeof(*cpu_type);
+ int result = sysctlbyname("sysctl.proc_cputype",
+ cpu_type,
+ &len,
+ NULL,
+ 0);
+ if (result != 0) {
+ DPLOG(ERROR) << "sysctlbyname(""sysctl.proc_cputype"")";
+ return false;
+ }
+
+ return true;
+}
+
+bool IsAddressInSharedRegion(mach_vm_address_t addr, cpu_type_t type) {
+ if (type == CPU_TYPE_I386) {
+ return addr >= SHARED_REGION_BASE_I386 &&
+ addr < (SHARED_REGION_BASE_I386 + SHARED_REGION_SIZE_I386);
+ } else if (type == CPU_TYPE_X86_64) {
+ return addr >= SHARED_REGION_BASE_X86_64 &&
+ addr < (SHARED_REGION_BASE_X86_64 + SHARED_REGION_SIZE_X86_64);
+ } else {
+ return false;
+ }
+}
+
+} // namespace
+
+// Getting a mach task from a pid for another process requires permissions in
+// general, so there doesn't really seem to be a way to do these (and spinning
+// up ps to fetch each stats seems dangerous to put in a base api for anyone to
+// call). Child processes ipc their port, so return something if available,
+// otherwise return 0.
+
+// static
+ProcessMetrics* ProcessMetrics::CreateProcessMetrics(
+ ProcessHandle process,
+ ProcessMetrics::PortProvider* port_provider) {
+ return new ProcessMetrics(process, port_provider);
+}
+
+size_t ProcessMetrics::GetPagefileUsage() const {
+ task_basic_info_64 task_info_data;
+ if (!GetTaskInfo(TaskForPid(process_), &task_info_data))
+ return 0;
+ return task_info_data.virtual_size;
+}
+
+size_t ProcessMetrics::GetPeakPagefileUsage() const {
+ return 0;
+}
+
+size_t ProcessMetrics::GetWorkingSetSize() const {
+ task_basic_info_64 task_info_data;
+ if (!GetTaskInfo(TaskForPid(process_), &task_info_data))
+ return 0;
+ return task_info_data.resident_size;
+}
+
+size_t ProcessMetrics::GetPeakWorkingSetSize() const {
+ return 0;
+}
+
+// This is a rough approximation of the algorithm that libtop uses.
+// private_bytes is the size of private resident memory.
+// shared_bytes is the size of shared resident memory.
+bool ProcessMetrics::GetMemoryBytes(size_t* private_bytes,
+ size_t* shared_bytes) {
+ kern_return_t kr;
+ size_t private_pages_count = 0;
+ size_t shared_pages_count = 0;
+
+ if (!private_bytes && !shared_bytes)
+ return true;
+
+ mach_port_t task = TaskForPid(process_);
+ if (task == MACH_PORT_NULL) {
+ DLOG(ERROR) << "Invalid process";
+ return false;
+ }
+
+ cpu_type_t cpu_type;
+ if (!GetCPUTypeForProcess(process_, &cpu_type))
+ return false;
+
+ // The same region can be referenced multiple times. To avoid double counting
+ // we need to keep track of which regions we've already counted.
+ base::hash_set<int> seen_objects;
+
+ // We iterate through each VM region in the task's address map. For shared
+ // memory we add up all the pages that are marked as shared. Like libtop we
+ // try to avoid counting pages that are also referenced by other tasks. Since
+ // we don't have access to the VM regions of other tasks the only hint we have
+ // is if the address is in the shared region area.
+ //
+ // Private memory is much simpler. We simply count the pages that are marked
+ // as private or copy on write (COW).
+ //
+ // See libtop_update_vm_regions in
+ // http://www.opensource.apple.com/source/top/top-67/libtop.c
+ mach_vm_size_t size = 0;
+ for (mach_vm_address_t address = MACH_VM_MIN_ADDRESS;; address += size) {
+ vm_region_top_info_data_t info;
+ mach_msg_type_number_t info_count = VM_REGION_TOP_INFO_COUNT;
+ mach_port_t object_name;
+ kr = mach_vm_region(task,
+ &address,
+ &size,
+ VM_REGION_TOP_INFO,
+ (vm_region_info_t)&info,
+ &info_count,
+ &object_name);
+ if (kr == KERN_INVALID_ADDRESS) {
+ // We're at the end of the address space.
+ break;
+ } else if (kr != KERN_SUCCESS) {
+ DLOG(ERROR) << "Calling mach_vm_region failed with error: "
+ << mach_error_string(kr);
+ return false;
+ }
+
+ if (IsAddressInSharedRegion(address, cpu_type) &&
+ info.share_mode != SM_PRIVATE)
+ continue;
+
+ if (info.share_mode == SM_COW && info.ref_count == 1)
+ info.share_mode = SM_PRIVATE;
+
+ switch (info.share_mode) {
+ case SM_PRIVATE:
+ private_pages_count += info.private_pages_resident;
+ private_pages_count += info.shared_pages_resident;
+ break;
+ case SM_COW:
+ private_pages_count += info.private_pages_resident;
+ // Fall through
+ case SM_SHARED:
+ if (seen_objects.count(info.obj_id) == 0) {
+ // Only count the first reference to this region.
+ seen_objects.insert(info.obj_id);
+ shared_pages_count += info.shared_pages_resident;
+ }
+ break;
+ default:
+ break;
+ }
+ }
+
+ vm_size_t page_size;
+ kr = host_page_size(task, &page_size);
+ if (kr != KERN_SUCCESS) {
+ DLOG(ERROR) << "Failed to fetch host page size, error: "
+ << mach_error_string(kr);
+ return false;
+ }
+
+ if (private_bytes)
+ *private_bytes = private_pages_count * page_size;
+ if (shared_bytes)
+ *shared_bytes = shared_pages_count * page_size;
+
+ return true;
+}
+
+void ProcessMetrics::GetCommittedKBytes(CommittedKBytes* usage) const {
+}
+
+bool ProcessMetrics::GetWorkingSetKBytes(WorkingSetKBytes* ws_usage) const {
+ size_t priv = GetWorkingSetSize();
+ if (!priv)
+ return false;
+ ws_usage->priv = priv / 1024;
+ ws_usage->shareable = 0;
+ ws_usage->shared = 0;
+ return true;
+}
+
+#define TIME_VALUE_TO_TIMEVAL(a, r) do { \
+ (r)->tv_sec = (a)->seconds; \
+ (r)->tv_usec = (a)->microseconds; \
+} while (0)
+
+double ProcessMetrics::GetCPUUsage() {
+ mach_port_t task = TaskForPid(process_);
+ if (task == MACH_PORT_NULL)
+ return 0;
+
+ kern_return_t kr;
+
+ // Libtop explicitly loops over the threads (libtop_pinfo_update_cpu_usage()
+ // in libtop.c), but this is more concise and gives the same results:
+ task_thread_times_info thread_info_data;
+ mach_msg_type_number_t thread_info_count = TASK_THREAD_TIMES_INFO_COUNT;
+ kr = task_info(task,
+ TASK_THREAD_TIMES_INFO,
+ reinterpret_cast<task_info_t>(&thread_info_data),
+ &thread_info_count);
+ if (kr != KERN_SUCCESS) {
+ // Most likely cause: |task| is a zombie.
+ return 0;
+ }
+
+ task_basic_info_64 task_info_data;
+ if (!GetTaskInfo(task, &task_info_data))
+ return 0;
+
+ /* Set total_time. */
+ // thread info contains live time...
+ struct timeval user_timeval, system_timeval, task_timeval;
+ TIME_VALUE_TO_TIMEVAL(&thread_info_data.user_time, &user_timeval);
+ TIME_VALUE_TO_TIMEVAL(&thread_info_data.system_time, &system_timeval);
+ timeradd(&user_timeval, &system_timeval, &task_timeval);
+
+ // ... task info contains terminated time.
+ TIME_VALUE_TO_TIMEVAL(&task_info_data.user_time, &user_timeval);
+ TIME_VALUE_TO_TIMEVAL(&task_info_data.system_time, &system_timeval);
+ timeradd(&user_timeval, &task_timeval, &task_timeval);
+ timeradd(&system_timeval, &task_timeval, &task_timeval);
+
+ struct timeval now;
+ int retval = gettimeofday(&now, NULL);
+ if (retval)
+ return 0;
+
+ int64 time = TimeValToMicroseconds(now);
+ int64 task_time = TimeValToMicroseconds(task_timeval);
+
+ if ((last_system_time_ == 0) || (last_time_ == 0)) {
+ // First call, just set the last values.
+ last_system_time_ = task_time;
+ last_time_ = time;
+ return 0;
+ }
+
+ int64 system_time_delta = task_time - last_system_time_;
+ int64 time_delta = time - last_time_;
+ DCHECK_NE(0U, time_delta);
+ if (time_delta == 0)
+ return 0;
+
+ last_system_time_ = task_time;
+ last_time_ = time;
+
+ return static_cast<double>(system_time_delta * 100.0) / time_delta;
+}
+
+bool ProcessMetrics::GetIOCounters(IoCounters* io_counters) const {
+ return false;
+}
+
+ProcessMetrics::ProcessMetrics(ProcessHandle process,
+ ProcessMetrics::PortProvider* port_provider)
+ : process_(process),
+ last_time_(0),
+ last_system_time_(0),
+ port_provider_(port_provider) {
+ processor_count_ = SysInfo::NumberOfProcessors();
+}
+
+mach_port_t ProcessMetrics::TaskForPid(ProcessHandle process) const {
+ mach_port_t task = MACH_PORT_NULL;
+ if (port_provider_)
+ task = port_provider_->TaskForPid(process_);
+ if (task == MACH_PORT_NULL && process_ == getpid())
+ task = mach_task_self();
+ return task;
+}
+
+// Bytes committed by the system.
+size_t GetSystemCommitCharge() {
+ base::mac::ScopedMachPort host(mach_host_self());
+ mach_msg_type_number_t count = HOST_VM_INFO_COUNT;
+ vm_statistics_data_t data;
+ kern_return_t kr = host_statistics(host, HOST_VM_INFO,
+ reinterpret_cast<host_info_t>(&data),
+ &count);
+ if (kr) {
+ DLOG(WARNING) << "Failed to fetch host statistics.";
+ return 0;
+ }
+
+ vm_size_t page_size;
+ kr = host_page_size(host, &page_size);
+ if (kr) {
+ DLOG(ERROR) << "Failed to fetch host page size.";
+ return 0;
+ }
+
+ return (data.active_count * page_size) / 1024;
+}
+
+} // namespace base
diff --git a/base/process/process_metrics_openbsd.cc b/base/process/process_metrics_openbsd.cc
new file mode 100644
index 0000000..36f607c
--- /dev/null
+++ b/base/process/process_metrics_openbsd.cc
@@ -0,0 +1,168 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/process/process_metrics.h"
+
+#include <sys/param.h>
+#include <sys/sysctl.h>
+
+namespace base {
+
+// static
+ProcessMetrics* ProcessMetrics::CreateProcessMetrics(ProcessHandle process) {
+ return new ProcessMetrics(process);
+}
+
+size_t ProcessMetrics::GetPagefileUsage() const {
+ struct kinfo_proc info;
+ size_t length;
+ int mib[] = { CTL_KERN, KERN_PROC, KERN_PROC_PID, process_,
+ sizeof(struct kinfo_proc), 0 };
+
+ if (sysctl(mib, arraysize(mib), NULL, &length, NULL, 0) < 0)
+ return -1;
+
+ mib[5] = (length / sizeof(struct kinfo_proc));
+
+ if (sysctl(mib, arraysize(mib), &info, &length, NULL, 0) < 0)
+ return -1;
+
+ return (info.p_vm_tsize + info.p_vm_dsize + info.p_vm_ssize);
+}
+
+size_t ProcessMetrics::GetPeakPagefileUsage() const {
+ return 0;
+}
+
+size_t ProcessMetrics::GetWorkingSetSize() const {
+ struct kinfo_proc info;
+ size_t length;
+ int mib[] = { CTL_KERN, KERN_PROC, KERN_PROC_PID, process_,
+ sizeof(struct kinfo_proc), 0 };
+
+ if (sysctl(mib, arraysize(mib), NULL, &length, NULL, 0) < 0)
+ return -1;
+
+ mib[5] = (length / sizeof(struct kinfo_proc));
+
+ if (sysctl(mib, arraysize(mib), &info, &length, NULL, 0) < 0)
+ return -1;
+
+ return info.p_vm_rssize * getpagesize();
+}
+
+size_t ProcessMetrics::GetPeakWorkingSetSize() const {
+ return 0;
+}
+
+bool ProcessMetrics::GetMemoryBytes(size_t* private_bytes,
+ size_t* shared_bytes) {
+ WorkingSetKBytes ws_usage;
+
+ if (!GetWorkingSetKBytes(&ws_usage))
+ return false;
+
+ if (private_bytes)
+ *private_bytes = ws_usage.priv << 10;
+
+ if (shared_bytes)
+ *shared_bytes = ws_usage.shared * 1024;
+
+ return true;
+}
+
+bool ProcessMetrics::GetWorkingSetKBytes(WorkingSetKBytes* ws_usage) const {
+ // TODO(bapt): be sure we can't be precise
+ size_t priv = GetWorkingSetSize();
+ if (!priv)
+ return false;
+ ws_usage->priv = priv / 1024;
+ ws_usage->shareable = 0;
+ ws_usage->shared = 0;
+
+ return true;
+}
+
+bool ProcessMetrics::GetIOCounters(IoCounters* io_counters) const {
+ return false;
+}
+
+static int GetProcessCPU(pid_t pid) {
+ struct kinfo_proc info;
+ size_t length;
+ int mib[] = { CTL_KERN, KERN_PROC, KERN_PROC_PID, pid,
+ sizeof(struct kinfo_proc), 0 };
+
+ if (sysctl(mib, arraysize(mib), NULL, &length, NULL, 0) < 0)
+ return -1;
+
+ mib[5] = (length / sizeof(struct kinfo_proc));
+
+ if (sysctl(mib, arraysize(mib), &info, &length, NULL, 0) < 0)
+ return 0;
+
+ return info.p_pctcpu;
+}
+
+double ProcessMetrics::GetCPUUsage() {
+ struct timeval now;
+
+ int retval = gettimeofday(&now, NULL);
+ if (retval)
+ return 0;
+
+ int64 time = TimeValToMicroseconds(now);
+
+ if (last_time_ == 0) {
+ // First call, just set the last values.
+ last_time_ = time;
+ last_cpu_ = GetProcessCPU(process_);
+ return 0;
+ }
+
+ int64 time_delta = time - last_time_;
+ DCHECK_NE(time_delta, 0);
+
+ if (time_delta == 0)
+ return 0;
+
+ int cpu = GetProcessCPU(process_);
+
+ last_time_ = time;
+ last_cpu_ = cpu;
+
+ double percentage = static_cast<double>((cpu * 100.0) / FSCALE);
+
+ return percentage;
+}
+
+ProcessMetrics::ProcessMetrics(ProcessHandle process)
+ : process_(process),
+ last_time_(0),
+ last_system_time_(0),
+ last_cpu_(0) {
+
+ processor_count_ = base::SysInfo::NumberOfProcessors();
+}
+
+size_t GetSystemCommitCharge() {
+ int mib[] = { CTL_VM, VM_METER };
+ int pagesize;
+ struct vmtotal vmtotal;
+ unsigned long mem_total, mem_free, mem_inactive;
+ size_t len = sizeof(vmtotal);
+
+ if (sysctl(mib, arraysize(mib), &vmtotal, &len, NULL, 0) < 0)
+ return 0;
+
+ mem_total = vmtotal.t_vm;
+ mem_free = vmtotal.t_free;
+ mem_inactive = vmtotal.t_vm - vmtotal.t_avm;
+
+ pagesize = getpagesize();
+
+ return mem_total - (mem_free*pagesize) - (mem_inactive*pagesize);
+}
+
+} // namespace base
diff --git a/base/process/process_metrics_posix.cc b/base/process/process_metrics_posix.cc
new file mode 100644
index 0000000..3422a73
--- /dev/null
+++ b/base/process/process_metrics_posix.cc
@@ -0,0 +1,21 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/process/process_metrics.h"
+
+#include <sys/time.h>
+
+namespace base {
+
+int64 TimeValToMicroseconds(const struct timeval& tv) {
+ static const int kMicrosecondsPerSecond = 1000000;
+ int64 ret = tv.tv_sec; // Avoid (int * int) integer overflow.
+ ret *= kMicrosecondsPerSecond;
+ ret += tv.tv_usec;
+ return ret;
+}
+
+ProcessMetrics::~ProcessMetrics() { }
+
+} // namespace base
diff --git a/base/process/process_metrics_win.cc b/base/process/process_metrics_win.cc
new file mode 100644
index 0000000..f42ea86
--- /dev/null
+++ b/base/process/process_metrics_win.cc
@@ -0,0 +1,315 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/process/process_metrics.h"
+
+#include <windows.h>
+#include <psapi.h>
+
+#include "base/logging.h"
+#include "base/sys_info.h"
+
+namespace base {
+
+// System pagesize. This value remains constant on x86/64 architectures.
+const int PAGESIZE_KB = 4;
+
+ProcessMetrics::~ProcessMetrics() { }
+
+// static
+ProcessMetrics* ProcessMetrics::CreateProcessMetrics(ProcessHandle process) {
+ return new ProcessMetrics(process);
+}
+
+size_t ProcessMetrics::GetPagefileUsage() const {
+ PROCESS_MEMORY_COUNTERS pmc;
+ if (GetProcessMemoryInfo(process_, &pmc, sizeof(pmc))) {
+ return pmc.PagefileUsage;
+ }
+ return 0;
+}
+
+// Returns the peak space allocated for the pagefile, in bytes.
+size_t ProcessMetrics::GetPeakPagefileUsage() const {
+ PROCESS_MEMORY_COUNTERS pmc;
+ if (GetProcessMemoryInfo(process_, &pmc, sizeof(pmc))) {
+ return pmc.PeakPagefileUsage;
+ }
+ return 0;
+}
+
+// Returns the current working set size, in bytes.
+size_t ProcessMetrics::GetWorkingSetSize() const {
+ PROCESS_MEMORY_COUNTERS pmc;
+ if (GetProcessMemoryInfo(process_, &pmc, sizeof(pmc))) {
+ return pmc.WorkingSetSize;
+ }
+ return 0;
+}
+
+// Returns the peak working set size, in bytes.
+size_t ProcessMetrics::GetPeakWorkingSetSize() const {
+ PROCESS_MEMORY_COUNTERS pmc;
+ if (GetProcessMemoryInfo(process_, &pmc, sizeof(pmc))) {
+ return pmc.PeakWorkingSetSize;
+ }
+ return 0;
+}
+
+bool ProcessMetrics::GetMemoryBytes(size_t* private_bytes,
+ size_t* shared_bytes) {
+ // PROCESS_MEMORY_COUNTERS_EX is not supported until XP SP2.
+ // GetProcessMemoryInfo() will simply fail on prior OS. So the requested
+ // information is simply not available. Hence, we will return 0 on unsupported
+ // OSes. Unlike most Win32 API, we don't need to initialize the "cb" member.
+ PROCESS_MEMORY_COUNTERS_EX pmcx;
+ if (private_bytes &&
+ GetProcessMemoryInfo(process_,
+ reinterpret_cast<PROCESS_MEMORY_COUNTERS*>(&pmcx),
+ sizeof(pmcx))) {
+ *private_bytes = pmcx.PrivateUsage;
+ }
+
+ if (shared_bytes) {
+ WorkingSetKBytes ws_usage;
+ if (!GetWorkingSetKBytes(&ws_usage))
+ return false;
+
+ *shared_bytes = ws_usage.shared * 1024;
+ }
+
+ return true;
+}
+
+void ProcessMetrics::GetCommittedKBytes(CommittedKBytes* usage) const {
+ MEMORY_BASIC_INFORMATION mbi = {0};
+ size_t committed_private = 0;
+ size_t committed_mapped = 0;
+ size_t committed_image = 0;
+ void* base_address = NULL;
+ while (VirtualQueryEx(process_, base_address, &mbi, sizeof(mbi)) ==
+ sizeof(mbi)) {
+ if (mbi.State == MEM_COMMIT) {
+ if (mbi.Type == MEM_PRIVATE) {
+ committed_private += mbi.RegionSize;
+ } else if (mbi.Type == MEM_MAPPED) {
+ committed_mapped += mbi.RegionSize;
+ } else if (mbi.Type == MEM_IMAGE) {
+ committed_image += mbi.RegionSize;
+ } else {
+ NOTREACHED();
+ }
+ }
+ void* new_base = (static_cast<BYTE*>(mbi.BaseAddress)) + mbi.RegionSize;
+ // Avoid infinite loop by weird MEMORY_BASIC_INFORMATION.
+ // If we query 64bit processes in a 32bit process, VirtualQueryEx()
+ // returns such data.
+ if (new_base <= base_address) {
+ usage->image = 0;
+ usage->mapped = 0;
+ usage->priv = 0;
+ return;
+ }
+ base_address = new_base;
+ }
+ usage->image = committed_image / 1024;
+ usage->mapped = committed_mapped / 1024;
+ usage->priv = committed_private / 1024;
+}
+
+bool ProcessMetrics::GetWorkingSetKBytes(WorkingSetKBytes* ws_usage) const {
+ size_t ws_private = 0;
+ size_t ws_shareable = 0;
+ size_t ws_shared = 0;
+
+ DCHECK(ws_usage);
+ memset(ws_usage, 0, sizeof(*ws_usage));
+
+ DWORD number_of_entries = 4096; // Just a guess.
+ PSAPI_WORKING_SET_INFORMATION* buffer = NULL;
+ int retries = 5;
+ for (;;) {
+ DWORD buffer_size = sizeof(PSAPI_WORKING_SET_INFORMATION) +
+ (number_of_entries * sizeof(PSAPI_WORKING_SET_BLOCK));
+
+ // if we can't expand the buffer, don't leak the previous
+ // contents or pass a NULL pointer to QueryWorkingSet
+ PSAPI_WORKING_SET_INFORMATION* new_buffer =
+ reinterpret_cast<PSAPI_WORKING_SET_INFORMATION*>(
+ realloc(buffer, buffer_size));
+ if (!new_buffer) {
+ free(buffer);
+ return false;
+ }
+ buffer = new_buffer;
+
+ // Call the function once to get number of items
+ if (QueryWorkingSet(process_, buffer, buffer_size))
+ break; // Success
+
+ if (GetLastError() != ERROR_BAD_LENGTH) {
+ free(buffer);
+ return false;
+ }
+
+ number_of_entries = static_cast<DWORD>(buffer->NumberOfEntries);
+
+ // Maybe some entries are being added right now. Increase the buffer to
+ // take that into account.
+ number_of_entries = static_cast<DWORD>(number_of_entries * 1.25);
+
+ if (--retries == 0) {
+ free(buffer); // If we're looping, eventually fail.
+ return false;
+ }
+ }
+
+ // On windows 2000 the function returns 1 even when the buffer is too small.
+ // The number of entries that we are going to parse is the minimum between the
+ // size we allocated and the real number of entries.
+ number_of_entries =
+ std::min(number_of_entries, static_cast<DWORD>(buffer->NumberOfEntries));
+ for (unsigned int i = 0; i < number_of_entries; i++) {
+ if (buffer->WorkingSetInfo[i].Shared) {
+ ws_shareable++;
+ if (buffer->WorkingSetInfo[i].ShareCount > 1)
+ ws_shared++;
+ } else {
+ ws_private++;
+ }
+ }
+
+ ws_usage->priv = ws_private * PAGESIZE_KB;
+ ws_usage->shareable = ws_shareable * PAGESIZE_KB;
+ ws_usage->shared = ws_shared * PAGESIZE_KB;
+ free(buffer);
+ return true;
+}
+
+static uint64 FileTimeToUTC(const FILETIME& ftime) {
+ LARGE_INTEGER li;
+ li.LowPart = ftime.dwLowDateTime;
+ li.HighPart = ftime.dwHighDateTime;
+ return li.QuadPart;
+}
+
+double ProcessMetrics::GetCPUUsage() {
+ FILETIME now;
+ FILETIME creation_time;
+ FILETIME exit_time;
+ FILETIME kernel_time;
+ FILETIME user_time;
+
+ GetSystemTimeAsFileTime(&now);
+
+ if (!GetProcessTimes(process_, &creation_time, &exit_time,
+ &kernel_time, &user_time)) {
+ // We don't assert here because in some cases (such as in the Task Manager)
+ // we may call this function on a process that has just exited but we have
+ // not yet received the notification.
+ return 0;
+ }
+ int64 system_time = (FileTimeToUTC(kernel_time) + FileTimeToUTC(user_time)) /
+ processor_count_;
+ int64 time = FileTimeToUTC(now);
+
+ if ((last_system_time_ == 0) || (last_time_ == 0)) {
+ // First call, just set the last values.
+ last_system_time_ = system_time;
+ last_time_ = time;
+ return 0;
+ }
+
+ int64 system_time_delta = system_time - last_system_time_;
+ int64 time_delta = time - last_time_;
+ DCHECK_NE(0U, time_delta);
+ if (time_delta == 0)
+ return 0;
+
+ // We add time_delta / 2 so the result is rounded.
+ int cpu = static_cast<int>((system_time_delta * 100 + time_delta / 2) /
+ time_delta);
+
+ last_system_time_ = system_time;
+ last_time_ = time;
+
+ return cpu;
+}
+
+bool ProcessMetrics::CalculateFreeMemory(FreeMBytes* free) const {
+ const SIZE_T kTopAddress = 0x7F000000;
+ const SIZE_T kMegabyte = 1024 * 1024;
+ SIZE_T accumulated = 0;
+
+ MEMORY_BASIC_INFORMATION largest = {0};
+ UINT_PTR scan = 0;
+ while (scan < kTopAddress) {
+ MEMORY_BASIC_INFORMATION info;
+ if (!::VirtualQueryEx(process_, reinterpret_cast<void*>(scan),
+ &info, sizeof(info)))
+ return false;
+ if (info.State == MEM_FREE) {
+ accumulated += info.RegionSize;
+ if (info.RegionSize > largest.RegionSize)
+ largest = info;
+ }
+ scan += info.RegionSize;
+ }
+ free->largest = largest.RegionSize / kMegabyte;
+ free->largest_ptr = largest.BaseAddress;
+ free->total = accumulated / kMegabyte;
+ return true;
+}
+
+bool ProcessMetrics::GetIOCounters(IoCounters* io_counters) const {
+ return GetProcessIoCounters(process_, io_counters) != FALSE;
+}
+
+ProcessMetrics::ProcessMetrics(ProcessHandle process)
+ : process_(process),
+ processor_count_(base::SysInfo::NumberOfProcessors()),
+ last_time_(0),
+ last_system_time_(0) {
+}
+
+// GetPerformanceInfo is not available on WIN2K. So we'll
+// load it on-the-fly.
+const wchar_t kPsapiDllName[] = L"psapi.dll";
+typedef BOOL (WINAPI *GetPerformanceInfoFunction) (
+ PPERFORMANCE_INFORMATION pPerformanceInformation,
+ DWORD cb);
+
+// Beware of races if called concurrently from multiple threads.
+static BOOL InternalGetPerformanceInfo(
+ PPERFORMANCE_INFORMATION pPerformanceInformation, DWORD cb) {
+ static GetPerformanceInfoFunction GetPerformanceInfo_func = NULL;
+ if (!GetPerformanceInfo_func) {
+ HMODULE psapi_dll = ::GetModuleHandle(kPsapiDllName);
+ if (psapi_dll)
+ GetPerformanceInfo_func = reinterpret_cast<GetPerformanceInfoFunction>(
+ GetProcAddress(psapi_dll, "GetPerformanceInfo"));
+
+ if (!GetPerformanceInfo_func) {
+ // The function could be loaded!
+ memset(pPerformanceInformation, 0, cb);
+ return FALSE;
+ }
+ }
+ return GetPerformanceInfo_func(pPerformanceInformation, cb);
+}
+
+size_t GetSystemCommitCharge() {
+ // Get the System Page Size.
+ SYSTEM_INFO system_info;
+ GetSystemInfo(&system_info);
+
+ PERFORMANCE_INFORMATION info;
+ if (!InternalGetPerformanceInfo(&info, sizeof(info))) {
+ DLOG(ERROR) << "Failed to fetch internal performance info.";
+ return 0;
+ }
+ return (info.CommitTotal * system_info.dwPageSize) / 1024;
+}
+
+} // namespace base