summaryrefslogtreecommitdiffstats
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
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
-rw-r--r--base/base.gypi12
-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
-rw-r--r--base/process_util.h207
-rw-r--r--base/process_util_freebsd.cc114
-rw-r--r--base/process_util_ios.mm37
-rw-r--r--base/process_util_linux.cc590
-rw-r--r--base/process_util_mac.mm307
-rw-r--r--base/process_util_openbsd.cc156
-rw-r--r--base/process_util_posix.cc10
-rw-r--r--base/process_util_win.cc303
19 files changed, 1922 insertions, 1705 deletions
diff --git a/base/base.gypi b/base/base.gypi
index acdd7ba..abfe1f5 100644
--- a/base/base.gypi
+++ b/base/base.gypi
@@ -376,6 +376,16 @@
'process_util_posix.cc',
'process_util_win.cc',
'process_win.cc',
+ 'process/process_metrics.h',
+ 'process/process_metrics_freebsd.cc',
+ 'process/process_metrics_ios.cc',
+ 'process/process_metrics_linux.cc',
+ 'process/process_metrics_mac.cc',
+ 'process/process_metrics_openbsd.cc',
+ 'process/process_metrics_posix.cc',
+ 'process/process_metrics_win.cc',
+ 'process/internal_linux.cc',
+ 'process/internal_linux.h',
'profiler/scoped_profile.cc',
'profiler/scoped_profile.h',
'profiler/alternate_timer.cc',
@@ -675,6 +685,8 @@
'sources/': [
['include', '^files/file_path_watcher_linux\\.cc$'],
['include', '^process_util_linux\\.cc$'],
+ ['include', '^process/internal_linux\\.cc$'],
+ ['include', '^process/process_metrics_linux\\.cc$'],
['include', '^posix/unix_domain_socket_linux\\.cc$'],
['include', '^strings/sys_string_conversions_posix\\.cc$'],
['include', '^sys_info_linux\\.cc$'],
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
diff --git a/base/process_util.h b/base/process_util.h
index 6efc70c..2036ca8 100644
--- a/base/process_util.h
+++ b/base/process_util.h
@@ -39,6 +39,7 @@ typedef struct _malloc_zone_t malloc_zone_t;
#include "base/base_export.h"
#include "base/files/file_path.h"
#include "base/process.h"
+#include "base/process/process_metrics.h"
#if defined(OS_POSIX)
#include "base/posix/file_descriptor_shuffle.h"
@@ -55,9 +56,6 @@ struct ProcessEntry : public PROCESSENTRY32 {
const wchar_t* exe_file() const { return szExeFile; }
};
-struct IoCounters : public IO_COUNTERS {
-};
-
// Process access masks. These constants provide platform-independent
// definitions for the standard Windows access masks.
// See http://msdn.microsoft.com/en-us/library/ms684880(VS.85).aspx for
@@ -98,15 +96,6 @@ struct BASE_EXPORT ProcessEntry {
std::vector<std::string> cmd_line_args_;
};
-struct IoCounters {
- uint64_t ReadOperationCount;
- uint64_t WriteOperationCount;
- uint64_t OtherOperationCount;
- uint64_t ReadTransferCount;
- uint64_t WriteTransferCount;
- uint64_t OtherTransferCount;
-};
-
// Process access masks. They are not used on Posix because access checking
// does not happen during handle creation.
const uint32 kProcessAccessTerminate = 0;
@@ -192,11 +181,6 @@ BASE_EXPORT FilePath GetProcessExecutablePath(ProcessHandle process);
#endif
#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);
-
// Get the number of threads of |process| as available in /proc/<pid>/stat.
// This should be used with care as no synchronization with running threads is
// done. This is mostly useful to guarantee being single-threaded.
@@ -644,195 +628,6 @@ class BASE_EXPORT NamedProcessIterator : public ProcessIterator {
DISALLOW_COPY_AND_ASSIGN(NamedProcessIterator);
};
-// 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);
-};
-
-#if defined(OS_LINUX) || defined(OS_ANDROID)
-// 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)
-
-// Returns the memory committed by the system in KBytes.
-// Returns 0 if it can't compute the commit charge.
-BASE_EXPORT size_t GetSystemCommitCharge();
-
// Enables low fragmentation heap (LFH) for every heaps of this process. This
// won't have any effect on heaps created after this function call. It will not
// modify data allocated in the heaps before calling this function. So it is
diff --git a/base/process_util_freebsd.cc b/base/process_util_freebsd.cc
index cb5a2db..bcf629f 100644
--- a/base/process_util_freebsd.cc
+++ b/base/process_util_freebsd.cc
@@ -162,120 +162,6 @@ bool NamedProcessIterator::IncludeEntry() {
return ProcessIterator::IncludeEntry();
}
-
-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;
-}
-
-bool ProcessMetrics::GetIOCounters(IoCounters* io_counters) const {
- return false;
-}
-
-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;
-}
-
-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);
-}
-
void EnableTerminationOnOutOfMemory() {
DLOG(WARNING) << "Not feasible.";
}
diff --git a/base/process_util_ios.mm b/base/process_util_ios.mm
index 7239ed2..4d95a7e 100644
--- a/base/process_util_ios.mm
+++ b/base/process_util_ios.mm
@@ -5,7 +5,6 @@
#include "base/process_util.h"
#import <Foundation/Foundation.h>
-#include <mach/task.h>
#include <stdio.h>
#include <sys/resource.h>
@@ -16,19 +15,6 @@
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
-
ProcessId GetCurrentProcId() {
return getpid();
}
@@ -66,27 +52,4 @@ size_t GetMaxFds() {
return static_cast<size_t>(max_fds);
}
-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_util_linux.cc b/base/process_util_linux.cc
index 898c172..4f510d7 100644
--- a/base/process_util_linux.cc
+++ b/base/process_util_linux.cc
@@ -12,10 +12,10 @@
#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"
@@ -23,143 +23,14 @@ namespace base {
namespace {
-enum ParsingState {
- KEY_NAME,
- KEY_VALUE
-};
-
-const char kProcDir[] = "/proc";
-const char kStatFile[] = "stat";
-
-// Returns a FilePath to "/proc/pid".
-FilePath GetProcPidDir(pid_t pid) {
- return FilePath(kProcDir).Append(IntToString(pid));
-}
-
-// 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 /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) {
- buffer->clear();
- // Synchronously reading files in /proc is safe.
- ThreadRestrictions::ScopedAllowIO allow_io;
-
- FilePath stat_file = GetProcPidDir(pid).Append(kStatFile);
- if (!file_util::ReadFileToString(stat_file, buffer)) {
- DLOG(WARNING) << "Failed to get process stats.";
- return false;
- }
- return !buffer->empty();
-}
-
-// 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) {
- // |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;
-}
-
-// 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) {
- 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;
-}
-
-// Same as GetProcStatsFieldAsInt(), but for size_t values.
-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;
-}
-
-// Convenience wrapper around GetProcStatsFieldAsInt(), ParseProcStats() and
-// ReadProcStats(). See GetProcStatsFieldAsInt() for details.
-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);
-}
-
-// Same as ReadProcStatsAndGetFieldAsInt() but for size_t values.
-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);
-}
-
// Reads the |field_num|th field from |proc_stats|.
// Returns an empty string on failure.
// This version only handles VM_COMM and VM_STATE, which are the only fields
// that are strings.
std::string GetProcStatsFieldAsString(
const std::vector<std::string>& proc_stats,
- ProcStatsFields field_num) {
- if (field_num < VM_COMM || field_num > VM_STATE) {
+ internal::ProcStatsFields field_num) {
+ if (field_num < internal::VM_COMM || field_num > internal::VM_STATE) {
NOTREACHED();
return std::string();
}
@@ -180,7 +51,7 @@ bool GetProcCmdline(pid_t pid, std::vector<std::string>* proc_cmd_line_args) {
// Synchronously reading files in /proc is safe.
ThreadRestrictions::ScopedAllowIO allow_io;
- FilePath cmd_line_file = GetProcPidDir(pid).Append("cmdline");
+ FilePath cmd_line_file = internal::GetProcPidDir(pid).Append("cmdline");
std::string cmd_line;
if (!file_util::ReadFileToString(cmd_line_file, &cmd_line))
return false;
@@ -190,113 +61,6 @@ bool GetProcCmdline(pid_t pid, std::vector<std::string>* proc_cmd_line_args) {
return true;
}
-// 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) {
- 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;
-}
-
-// 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 = 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 = 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(kStatFile);
- if (file_util::ReadFileToString(stat_path, &stat)) {
- int cpu = ParseProcStatCPU(stat);
- if (cpu > 0)
- total_cpu += cpu;
- }
- }
- closedir(dir);
-
- return total_cpu;
-}
-
-// 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 = 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;
-}
-
} // namespace
#if defined(USE_LINUX_BREAKPAD)
@@ -306,14 +70,15 @@ size_t g_oom_size = 0U;
const char kProcSelfExe[] = "/proc/self/exe";
ProcessId GetParentProcessId(ProcessHandle process) {
- ProcessId pid = ReadProcStatsAndGetFieldAsInt(process, VM_PPID);
+ ProcessId pid =
+ internal::ReadProcStatsAndGetFieldAsInt(process, internal::VM_PPID);
if (pid)
return pid;
return -1;
}
FilePath GetProcessExecutablePath(ProcessHandle process) {
- FilePath stat_file = GetProcPidDir(process).Append("exe");
+ FilePath stat_file = internal::GetProcPidDir(process).Append("exe");
FilePath exe_name;
if (!file_util::ReadSymbolicLink(stat_file, &exe_name)) {
// No such process. Happens frequently in e.g. TerminateAllChromeProcesses
@@ -324,7 +89,7 @@ FilePath GetProcessExecutablePath(ProcessHandle process) {
ProcessIterator::ProcessIterator(const ProcessFilter* filter)
: filter_(filter) {
- procfs_dir_ = opendir(kProcDir);
+ procfs_dir_ = opendir(internal::kProcDir);
}
ProcessIterator::~ProcessIterator() {
@@ -353,7 +118,7 @@ bool ProcessIterator::CheckForNextProcess() {
return false;
// If not a process, keep looking for one.
- pid = ProcDirSlotToPid(slot->d_name);
+ pid = internal::ProcDirSlotToPid(slot->d_name);
if (!pid) {
skipped++;
continue;
@@ -362,12 +127,13 @@ bool ProcessIterator::CheckForNextProcess() {
if (!GetProcCmdline(pid, &cmd_line_args))
continue;
- if (!ReadProcStats(pid, &stats_data))
+ if (!internal::ReadProcStats(pid, &stats_data))
continue;
- if (!ParseProcStats(stats_data, &proc_stats))
+ if (!internal::ParseProcStats(stats_data, &proc_stats))
continue;
- std::string runstate = GetProcStatsFieldAsString(proc_stats, VM_STATE);
+ std::string runstate =
+ GetProcStatsFieldAsString(proc_stats, internal::VM_STATE);
if (runstate.size() != 1) {
NOTREACHED();
continue;
@@ -388,8 +154,8 @@ bool ProcessIterator::CheckForNextProcess() {
}
entry_.pid_ = pid;
- entry_.ppid_ = GetProcStatsFieldAsInt(proc_stats, VM_PPID);
- entry_.gid_ = GetProcStatsFieldAsInt(proc_stats, VM_PGRP);
+ entry_.ppid_ = GetProcStatsFieldAsInt(proc_stats, internal::VM_PPID);
+ entry_.gid_ = GetProcStatsFieldAsInt(proc_stats, internal::VM_PGRP);
entry_.cmd_line_args_.assign(cmd_line_args.begin(), cmd_line_args.end());
entry_.exe_file_ = GetProcessExecutablePath(pid).BaseName().value();
return true;
@@ -402,329 +168,9 @@ bool NamedProcessIterator::IncludeEntry() {
}
-// static
-ProcessMetrics* ProcessMetrics::CreateProcessMetrics(ProcessHandle process) {
- return new ProcessMetrics(process);
-}
-
-// On linux, we return vsize.
-size_t ProcessMetrics::GetPagefileUsage() const {
- return ReadProcStatsAndGetFieldAsSizeT(process_, 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 ReadProcStatsAndGetFieldAsSizeT(process_, 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 = 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 = 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_ = SysInfo::NumberOfProcessors();
-}
-
-
-// Exposed for testing.
-int ParseProcStatCPU(const std::string& input) {
- std::vector<std::string> proc_stats;
- if (!ParseProcStats(input, &proc_stats))
- return -1;
-
- if (proc_stats.size() <= VM_STIME)
- return -1;
- int utime = GetProcStatsFieldAsInt(proc_stats, VM_UTIME);
- int stime = GetProcStatsFieldAsInt(proc_stats, VM_STIME);
- return utime + stime;
-}
-
int GetNumberOfThreads(ProcessHandle process) {
- return ReadProcStatsAndGetFieldAsInt(process, VM_NUMTHREADS);
-}
-
-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;
-}
-
-size_t GetSystemCommitCharge() {
- SystemMemoryInfoKB meminfo;
- if (!GetSystemMemoryInfo(&meminfo))
- return 0;
- return meminfo.total - meminfo.free - meminfo.buffers - meminfo.cached;
+ return internal::ReadProcStatsAndGetFieldAsInt(process,
+ internal::VM_NUMTHREADS);
}
namespace {
@@ -859,7 +305,7 @@ bool AdjustOOMScore(ProcessId process, int score) {
if (score < 0 || score > kMaxOomScore)
return false;
- FilePath oom_path(GetProcPidDir(process));
+ FilePath oom_path(internal::GetProcPidDir(process));
// Attempt to write the newer oom_score_adj file first.
FilePath oom_file = oom_path.AppendASCII("oom_score_adj");
diff --git a/base/process_util_mac.mm b/base/process_util_mac.mm
index e0174b4..c9f0ef7 100644
--- a/base/process_util_mac.mm
+++ b/base/process_util_mac.mm
@@ -184,313 +184,6 @@ bool NamedProcessIterator::IncludeEntry() {
}
-// ------------------------------------------------------------------------
-// NOTE: about ProcessMetrics
-//
-// 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.
-//
-
-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();
-}
-
-// static
-ProcessMetrics* ProcessMetrics::CreateProcessMetrics(
- ProcessHandle process,
- ProcessMetrics::PortProvider* port_provider) {
- return new ProcessMetrics(process, port_provider);
-}
-
-bool ProcessMetrics::GetIOCounters(IoCounters* io_counters) const {
- return false;
-}
-
-static 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;
-}
-
-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;
-}
-
-static 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;
-}
-
-static 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;
-}
-
-// 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;
-}
-
-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;
-}
-
// These are helpers for EnableTerminationOnHeapCorruption, which is a no-op
// on 64 bit Macs.
#if ARCH_CPU_32_BITS
diff --git a/base/process_util_openbsd.cc b/base/process_util_openbsd.cc
index 099c2b4..de16909 100644
--- a/base/process_util_openbsd.cc
+++ b/base/process_util_openbsd.cc
@@ -178,162 +178,6 @@ bool NamedProcessIterator::IncludeEntry() {
}
-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;
- 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;
-}
-
-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);
-}
-
void EnableTerminationOnOutOfMemory() {
}
diff --git a/base/process_util_posix.cc b/base/process_util_posix.cc
index 6f15130..9b05b98 100644
--- a/base/process_util_posix.cc
+++ b/base/process_util_posix.cc
@@ -771,8 +771,6 @@ bool LaunchProcess(const CommandLine& cmdline,
return LaunchProcess(cmdline.argv(), options, process_handle);
}
-ProcessMetrics::~ProcessMetrics() { }
-
void RaiseProcessToHighPriority() {
// On POSIX, we don't actually do anything here. We could try to nice() or
// setpriority() or sched_getscheduler, but these all require extra rights.
@@ -946,14 +944,6 @@ bool WaitForSingleProcess(ProcessHandle handle, base::TimeDelta wait) {
}
}
-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;
-}
-
// Return value used by GetAppOutputInternal to encapsulate the various exit
// scenarios from the function.
enum GetAppOutputInternalResult {
diff --git a/base/process_util_win.cc b/base/process_util_win.cc
index 0dd679b..1a30d0e 100644
--- a/base/process_util_win.cc
+++ b/base/process_util_win.cc
@@ -33,9 +33,6 @@ namespace base {
namespace {
-// System pagesize. This value remains constant on x86/64 architectures.
-const int PAGESIZE_KB = 4;
-
// Exit codes with special meanings on Windows.
const DWORD kNormalTerminationExitCode = 0;
const DWORD kDebuggerInactiveExitCode = 0xC0000354;
@@ -679,267 +676,6 @@ void EnsureProcessTerminated(ProcessHandle process) {
base::TimeDelta::FromMilliseconds(kWaitInterval));
}
-///////////////////////////////////////////////////////////////////////////////
-// ProcesMetrics
-
-ProcessMetrics::ProcessMetrics(ProcessHandle process)
- : process_(process),
- processor_count_(base::SysInfo::NumberOfProcessors()),
- last_time_(0),
- last_system_time_(0) {
-}
-
-// static
-ProcessMetrics* ProcessMetrics::CreateProcessMetrics(ProcessHandle process) {
- return new ProcessMetrics(process);
-}
-
-ProcessMetrics::~ProcessMetrics() { }
-
-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::GetIOCounters(IoCounters* io_counters) const {
- return GetProcessIoCounters(process_, io_counters) != FALSE;
-}
-
-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 EnableLowFragmentationHeap() {
HMODULE kernel32 = GetModuleHandle(L"kernel32.dll");
HeapSetFn heap_set = reinterpret_cast<HeapSetFn>(GetProcAddress(
@@ -988,43 +724,4 @@ void RaiseProcessToHighPriority() {
SetPriorityClass(GetCurrentProcess(), HIGH_PRIORITY_CLASS);
}
-// 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