summaryrefslogtreecommitdiffstats
path: root/chromium/base/process
diff options
context:
space:
mode:
authorZeno Albisser <zeno.albisser@digia.com>2013-08-15 21:46:11 +0200
committerZeno Albisser <zeno.albisser@digia.com>2013-08-15 21:46:11 +0200
commit679147eead574d186ebf3069647b4c23e8ccace6 (patch)
treefc247a0ac8ff119f7c8550879ebb6d3dd8d1ff69 /chromium/base/process
Initial import.
Diffstat (limited to 'chromium/base/process')
-rw-r--r--chromium/base/process/internal_linux.cc190
-rw-r--r--chromium/base/process/internal_linux.h89
-rw-r--r--chromium/base/process/kill.cc26
-rw-r--r--chromium/base/process/kill.h144
-rw-r--r--chromium/base/process/kill_mac.cc173
-rw-r--r--chromium/base/process/kill_posix.cc492
-rw-r--r--chromium/base/process/kill_win.cc255
-rw-r--r--chromium/base/process/launch.h258
-rw-r--r--chromium/base/process/launch_ios.cc13
-rw-r--r--chromium/base/process/launch_mac.cc28
-rw-r--r--chromium/base/process/launch_posix.cc760
-rw-r--r--chromium/base/process/launch_win.cc283
-rw-r--r--chromium/base/process/memory.h69
-rw-r--r--chromium/base/process/memory_linux.cc183
-rw-r--r--chromium/base/process/memory_mac.mm704
-rw-r--r--chromium/base/process/memory_stubs.cc19
-rw-r--r--chromium/base/process/memory_unittest.cc379
-rw-r--r--chromium/base/process/memory_unittest_mac.h32
-rw-r--r--chromium/base/process/memory_unittest_mac.mm59
-rw-r--r--chromium/base/process/memory_win.cc85
-rw-r--r--chromium/base/process/process.h70
-rw-r--r--chromium/base/process/process_handle.h96
-rw-r--r--chromium/base/process/process_handle_freebsd.cc39
-rw-r--r--chromium/base/process/process_handle_linux.cc30
-rw-r--r--chromium/base/process/process_handle_mac.cc27
-rw-r--r--chromium/base/process/process_handle_openbsd.cc49
-rw-r--r--chromium/base/process/process_handle_posix.cc49
-rw-r--r--chromium/base/process/process_handle_win.cc126
-rw-r--r--chromium/base/process/process_info.h25
-rw-r--r--chromium/base/process/process_info_linux.cc27
-rw-r--r--chromium/base/process/process_info_mac.cc31
-rw-r--r--chromium/base/process/process_info_win.cc25
-rw-r--r--chromium/base/process/process_iterator.cc65
-rw-r--r--chromium/base/process/process_iterator.h183
-rw-r--r--chromium/base/process/process_iterator_freebsd.cc124
-rw-r--r--chromium/base/process/process_iterator_linux.cc137
-rw-r--r--chromium/base/process/process_iterator_mac.cc135
-rw-r--r--chromium/base/process/process_iterator_openbsd.cc128
-rw-r--r--chromium/base/process/process_iterator_win.cc41
-rw-r--r--chromium/base/process/process_linux.cc137
-rw-r--r--chromium/base/process/process_metrics.h269
-rw-r--r--chromium/base/process/process_metrics_freebsd.cc122
-rw-r--r--chromium/base/process/process_metrics_ios.cc64
-rw-r--r--chromium/base/process/process_metrics_linux.cc516
-rw-r--r--chromium/base/process/process_metrics_mac.cc325
-rw-r--r--chromium/base/process/process_metrics_openbsd.cc168
-rw-r--r--chromium/base/process/process_metrics_posix.cc55
-rw-r--r--chromium/base/process/process_metrics_win.cc315
-rw-r--r--chromium/base/process/process_posix.cc73
-rw-r--r--chromium/base/process/process_util_unittest.cc961
-rw-r--r--chromium/base/process/process_util_unittest_ios.cc15
-rw-r--r--chromium/base/process/process_win.cc92
52 files changed, 8760 insertions, 0 deletions
diff --git a/chromium/base/process/internal_linux.cc b/chromium/base/process/internal_linux.cc
new file mode 100644
index 00000000000..ee1107c1792
--- /dev/null
+++ b/chromium/base/process/internal_linux.cc
@@ -0,0 +1,190 @@
+// 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 <map>
+#include <string>
+#include <vector>
+
+#include "base/file_util.h"
+#include "base/logging.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/string_split.h"
+#include "base/strings/string_util.h"
+#include "base/threading/thread_restrictions.h"
+#include "base/time/time.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 ReadProcFile(const FilePath& file, std::string* buffer) {
+ buffer->clear();
+ // Synchronously reading files in /proc is safe.
+ ThreadRestrictions::ScopedAllowIO allow_io;
+
+ if (!file_util::ReadFileToString(file, buffer)) {
+ DLOG(WARNING) << "Failed to read " << file.MaybeAsASCII();
+ return false;
+ }
+ return !buffer->empty();
+}
+
+bool ReadProcStats(pid_t pid, std::string* buffer) {
+ FilePath stat_file = internal::GetProcPidDir(pid).Append(kStatFile);
+ return ReadProcFile(stat_file, buffer);
+}
+
+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;
+}
+
+typedef std::map<std::string, std::string> ProcStatMap;
+void ParseProcStat(const std::string& contents, ProcStatMap* output) {
+ typedef std::pair<std::string, std::string> StringPair;
+ std::vector<StringPair> key_value_pairs;
+ SplitStringIntoKeyValuePairs(contents, ' ', '\n', &key_value_pairs);
+ for (size_t i = 0; i < key_value_pairs.size(); ++i) {
+ const StringPair& key_value_pair = key_value_pairs[i];
+ output->insert(key_value_pair);
+ }
+}
+
+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);
+}
+
+Time GetBootTime() {
+ FilePath path("/proc/stat");
+ std::string contents;
+ if (!ReadProcFile(path, &contents))
+ return Time();
+ ProcStatMap proc_stat;
+ ParseProcStat(contents, &proc_stat);
+ ProcStatMap::const_iterator btime_it = proc_stat.find("btime");
+ if (btime_it == proc_stat.end())
+ return Time();
+ int btime;
+ if (!StringToInt(btime_it->second, &btime))
+ return Time();
+ return Time::FromTimeT(btime);
+}
+
+TimeDelta ClockTicksToTimeDelta(int clock_ticks) {
+ // 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);
+
+ return TimeDelta::FromMicroseconds(
+ Time::kMicrosecondsPerSecond * clock_ticks / kHertz);
+}
+
+} // namespace internal
+} // namespace base
diff --git a/chromium/base/process/internal_linux.h b/chromium/base/process/internal_linux.h
new file mode 100644
index 00000000000..a10cee36e56
--- /dev/null
+++ b/chromium/base/process/internal_linux.h
@@ -0,0 +1,89 @@
+// 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 {
+
+class Time;
+class TimeDelta;
+
+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_STARTTIME = 21, // The time the process started in clock ticks.
+ 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);
+
+// Returns the time that the OS started. Clock ticks are relative to this.
+Time GetBootTime();
+
+// Converts Linux clock ticks to a wall time delta.
+TimeDelta ClockTicksToTimeDelta(int clock_ticks);
+
+} // namespace internal
+} // namespace base
+
+#endif // BASE_PROCESS_LINUX_INTERNAL_H_
diff --git a/chromium/base/process/kill.cc b/chromium/base/process/kill.cc
new file mode 100644
index 00000000000..caca3484a11
--- /dev/null
+++ b/chromium/base/process/kill.cc
@@ -0,0 +1,26 @@
+// 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/kill.h"
+
+#include "base/process/process_iterator.h"
+
+namespace base {
+
+bool KillProcesses(const FilePath::StringType& executable_name,
+ int exit_code,
+ const ProcessFilter* filter) {
+ bool result = true;
+ NamedProcessIterator iter(executable_name, filter);
+ while (const ProcessEntry* entry = iter.NextProcessEntry()) {
+#if defined(OS_WIN)
+ result &= KillProcessById(entry->pid(), exit_code, true);
+#else
+ result &= KillProcess(entry->pid(), exit_code, true);
+#endif
+ }
+ return result;
+}
+
+} // namespace base
diff --git a/chromium/base/process/kill.h b/chromium/base/process/kill.h
new file mode 100644
index 00000000000..f81ea9090da
--- /dev/null
+++ b/chromium/base/process/kill.h
@@ -0,0 +1,144 @@
+// 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 to kill processes and get the exit code and
+// termination status.
+
+#ifndef BASE_PROCESS_KILL_H_
+#define BASE_PROCESS_KILL_H_
+
+#include "base/files/file_path.h"
+#include "base/process/process_handle.h"
+#include "base/time/time.h"
+
+namespace base {
+
+class ProcessFilter;
+
+// Return status values from GetTerminationStatus. Don't use these as
+// exit code arguments to KillProcess*(), use platform/application
+// specific values instead.
+enum TerminationStatus {
+ TERMINATION_STATUS_NORMAL_TERMINATION, // zero exit status
+ TERMINATION_STATUS_ABNORMAL_TERMINATION, // non-zero exit status
+ TERMINATION_STATUS_PROCESS_WAS_KILLED, // e.g. SIGKILL or task manager kill
+ TERMINATION_STATUS_PROCESS_CRASHED, // e.g. Segmentation fault
+ TERMINATION_STATUS_STILL_RUNNING, // child hasn't exited yet
+ TERMINATION_STATUS_MAX_ENUM
+};
+
+// Attempts to kill all the processes on the current machine that were launched
+// from the given executable name, ending them with the given exit code. If
+// filter is non-null, then only processes selected by the filter are killed.
+// Returns true if all processes were able to be killed off, false if at least
+// one couldn't be killed.
+BASE_EXPORT bool KillProcesses(const FilePath::StringType& executable_name,
+ int exit_code,
+ const ProcessFilter* filter);
+
+// Attempts to kill the process identified by the given process
+// entry structure, giving it the specified exit code. If |wait| is true, wait
+// for the process to be actually terminated before returning.
+// Returns true if this is successful, false otherwise.
+BASE_EXPORT bool KillProcess(ProcessHandle process, int exit_code, bool wait);
+
+#if defined(OS_POSIX)
+// Attempts to kill the process group identified by |process_group_id|. Returns
+// true on success.
+BASE_EXPORT bool KillProcessGroup(ProcessHandle process_group_id);
+#endif // defined(OS_POSIX)
+
+#if defined(OS_WIN)
+BASE_EXPORT bool KillProcessById(ProcessId process_id,
+ int exit_code,
+ bool wait);
+#endif // defined(OS_WIN)
+
+// Get the termination status of the process by interpreting the
+// circumstances of the child process' death. |exit_code| is set to
+// the status returned by waitpid() on POSIX, and from
+// GetExitCodeProcess() on Windows. |exit_code| may be NULL if the
+// caller is not interested in it. Note that on Linux, this function
+// will only return a useful result the first time it is called after
+// the child exits (because it will reap the child and the information
+// will no longer be available).
+BASE_EXPORT TerminationStatus GetTerminationStatus(ProcessHandle handle,
+ int* exit_code);
+
+#if defined(OS_POSIX)
+// Wait for the process to exit and get the termination status. See
+// GetTerminationStatus for more information. On POSIX systems, we can't call
+// WaitForExitCode and then GetTerminationStatus as the child will be reaped
+// when WaitForExitCode return and this information will be lost.
+BASE_EXPORT TerminationStatus WaitForTerminationStatus(ProcessHandle handle,
+ int* exit_code);
+#endif // defined(OS_POSIX)
+
+// Waits for process to exit. On POSIX systems, if the process hasn't been
+// signaled then puts the exit code in |exit_code|; otherwise it's considered
+// a failure. On Windows |exit_code| is always filled. Returns true on success,
+// and closes |handle| in any case.
+BASE_EXPORT bool WaitForExitCode(ProcessHandle handle, int* exit_code);
+
+// Waits for process to exit. If it did exit within |timeout_milliseconds|,
+// then puts the exit code in |exit_code|, and returns true.
+// In POSIX systems, if the process has been signaled then |exit_code| is set
+// to -1. Returns false on failure (the caller is then responsible for closing
+// |handle|).
+// The caller is always responsible for closing the |handle|.
+BASE_EXPORT bool WaitForExitCodeWithTimeout(ProcessHandle handle,
+ int* exit_code,
+ base::TimeDelta timeout);
+
+// Wait for all the processes based on the named executable to exit. If filter
+// is non-null, then only processes selected by the filter are waited on.
+// Returns after all processes have exited or wait_milliseconds have expired.
+// Returns true if all the processes exited, false otherwise.
+BASE_EXPORT bool WaitForProcessesToExit(
+ const FilePath::StringType& executable_name,
+ base::TimeDelta wait,
+ const ProcessFilter* filter);
+
+// Wait for a single process to exit. Return true if it exited cleanly within
+// the given time limit. On Linux |handle| must be a child process, however
+// on Mac and Windows it can be any process.
+BASE_EXPORT bool WaitForSingleProcess(ProcessHandle handle,
+ base::TimeDelta wait);
+
+// Waits a certain amount of time (can be 0) for all the processes with a given
+// executable name to exit, then kills off any of them that are still around.
+// If filter is non-null, then only processes selected by the filter are waited
+// on. Killed processes are ended with the given exit code. Returns false if
+// any processes needed to be killed, true if they all exited cleanly within
+// the wait_milliseconds delay.
+BASE_EXPORT bool CleanupProcesses(const FilePath::StringType& executable_name,
+ base::TimeDelta wait,
+ int exit_code,
+ const ProcessFilter* filter);
+
+// This method ensures that the specified process eventually terminates, and
+// then it closes the given process handle.
+//
+// It assumes that the process has already been signalled to exit, and it
+// begins by waiting a small amount of time for it to exit. If the process
+// does not appear to have exited, then this function starts to become
+// aggressive about ensuring that the process terminates.
+//
+// On Linux this method does not block the calling thread.
+// On OS X this method may block for up to 2 seconds.
+//
+// NOTE: The process handle must have been opened with the PROCESS_TERMINATE
+// and SYNCHRONIZE permissions.
+//
+BASE_EXPORT void EnsureProcessTerminated(ProcessHandle process_handle);
+
+#if defined(OS_POSIX) && !defined(OS_MACOSX)
+// The nicer version of EnsureProcessTerminated() that is patient and will
+// wait for |process_handle| to finish and then reap it.
+BASE_EXPORT void EnsureProcessGetsReaped(ProcessHandle process_handle);
+#endif
+
+} // namespace base
+
+#endif // BASE_PROCESS_KILL_H_
diff --git a/chromium/base/process/kill_mac.cc b/chromium/base/process/kill_mac.cc
new file mode 100644
index 00000000000..5ebca5d0076
--- /dev/null
+++ b/chromium/base/process/kill_mac.cc
@@ -0,0 +1,173 @@
+// 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/kill.h"
+
+#include <signal.h>
+#include <sys/event.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+
+#include "base/file_util.h"
+#include "base/logging.h"
+#include "base/posix/eintr_wrapper.h"
+
+namespace base {
+
+namespace {
+
+const int kWaitBeforeKillSeconds = 2;
+
+// Reap |child| process. This call blocks until completion.
+void BlockingReap(pid_t child) {
+ const pid_t result = HANDLE_EINTR(waitpid(child, NULL, 0));
+ if (result == -1) {
+ DPLOG(ERROR) << "waitpid(" << child << ", NULL, 0)";
+ }
+}
+
+// Waits for |timeout| seconds for the given |child| to exit and reap it. If
+// the child doesn't exit within the time specified, kills it.
+//
+// This function takes two approaches: first, it tries to use kqueue to
+// observe when the process exits. kevent can monitor a kqueue with a
+// timeout, so this method is preferred to wait for a specified period of
+// time. Once the kqueue indicates the process has exited, waitpid will reap
+// the exited child. If the kqueue doesn't provide an exit event notification,
+// before the timeout expires, or if the kqueue fails or misbehaves, the
+// process will be mercilessly killed and reaped.
+//
+// A child process passed to this function may be in one of several states:
+// running, terminated and not yet reaped, and (apparently, and unfortunately)
+// terminated and already reaped. Normally, a process will at least have been
+// asked to exit before this function is called, but this is not required.
+// If a process is terminating and unreaped, there may be a window between the
+// time that kqueue will no longer recognize it and when it becomes an actual
+// zombie that a non-blocking (WNOHANG) waitpid can reap. This condition is
+// detected when kqueue indicates that the process is not running and a
+// non-blocking waitpid fails to reap the process but indicates that it is
+// still running. In this event, a blocking attempt to reap the process
+// collects the known-dying child, preventing zombies from congregating.
+//
+// In the event that the kqueue misbehaves entirely, as it might under a
+// EMFILE condition ("too many open files", or out of file descriptors), this
+// function will forcibly kill and reap the child without delay. This
+// eliminates another potential zombie vector. (If you're out of file
+// descriptors, you're probably deep into something else, but that doesn't
+// mean that zombies be allowed to kick you while you're down.)
+//
+// The fact that this function seemingly can be called to wait on a child
+// that's not only already terminated but already reaped is a bit of a
+// problem: a reaped child's pid can be reclaimed and may refer to a distinct
+// process in that case. The fact that this function can seemingly be called
+// to wait on a process that's not even a child is also a problem: kqueue will
+// work in that case, but waitpid won't, and killing a non-child might not be
+// the best approach.
+void WaitForChildToDie(pid_t child, int timeout) {
+ DCHECK(child > 0);
+ DCHECK(timeout > 0);
+
+ // DON'T ADD ANY EARLY RETURNS TO THIS FUNCTION without ensuring that
+ // |child| has been reaped. Specifically, even if a kqueue, kevent, or other
+ // call fails, this function should fall back to the last resort of trying
+ // to kill and reap the process. Not observing this rule will resurrect
+ // zombies.
+
+ int result;
+
+ int kq = HANDLE_EINTR(kqueue());
+ if (kq == -1) {
+ DPLOG(ERROR) << "kqueue()";
+ } else {
+ file_util::ScopedFD auto_close_kq(&kq);
+
+ struct kevent change = {0};
+ EV_SET(&change, child, EVFILT_PROC, EV_ADD, NOTE_EXIT, 0, NULL);
+ result = HANDLE_EINTR(kevent(kq, &change, 1, NULL, 0, NULL));
+
+ if (result == -1) {
+ if (errno != ESRCH) {
+ DPLOG(ERROR) << "kevent (setup " << child << ")";
+ } else {
+ // At this point, one of the following has occurred:
+ // 1. The process has died but has not yet been reaped.
+ // 2. The process has died and has already been reaped.
+ // 3. The process is in the process of dying. It's no longer
+ // kqueueable, but it may not be waitable yet either. Mark calls
+ // this case the "zombie death race".
+
+ result = HANDLE_EINTR(waitpid(child, NULL, WNOHANG));
+
+ if (result != 0) {
+ // A positive result indicates case 1. waitpid succeeded and reaped
+ // the child. A result of -1 indicates case 2. The child has already
+ // been reaped. In both of these cases, no further action is
+ // necessary.
+ return;
+ }
+
+ // |result| is 0, indicating case 3. The process will be waitable in
+ // short order. Fall back out of the kqueue code to kill it (for good
+ // measure) and reap it.
+ }
+ } else {
+ // Keep track of the elapsed time to be able to restart kevent if it's
+ // interrupted.
+ TimeDelta remaining_delta = TimeDelta::FromSeconds(timeout);
+ TimeTicks deadline = TimeTicks::Now() + remaining_delta;
+ result = -1;
+ struct kevent event = {0};
+ while (remaining_delta.InMilliseconds() > 0) {
+ const struct timespec remaining_timespec = remaining_delta.ToTimeSpec();
+ result = kevent(kq, NULL, 0, &event, 1, &remaining_timespec);
+ if (result == -1 && errno == EINTR) {
+ remaining_delta = deadline - TimeTicks::Now();
+ result = 0;
+ } else {
+ break;
+ }
+ }
+
+ if (result == -1) {
+ DPLOG(ERROR) << "kevent (wait " << child << ")";
+ } else if (result > 1) {
+ DLOG(ERROR) << "kevent (wait " << child << "): unexpected result "
+ << result;
+ } else if (result == 1) {
+ if ((event.fflags & NOTE_EXIT) &&
+ (event.ident == static_cast<uintptr_t>(child))) {
+ // The process is dead or dying. This won't block for long, if at
+ // all.
+ BlockingReap(child);
+ return;
+ } else {
+ DLOG(ERROR) << "kevent (wait " << child
+ << "): unexpected event: fflags=" << event.fflags
+ << ", ident=" << event.ident;
+ }
+ }
+ }
+ }
+
+ // The child is still alive, or is very freshly dead. Be sure by sending it
+ // a signal. This is safe even if it's freshly dead, because it will be a
+ // zombie (or on the way to zombiedom) and kill will return 0 even if the
+ // signal is not delivered to a live process.
+ result = kill(child, SIGKILL);
+ if (result == -1) {
+ DPLOG(ERROR) << "kill(" << child << ", SIGKILL)";
+ } else {
+ // The child is definitely on the way out now. BlockingReap won't need to
+ // wait for long, if at all.
+ BlockingReap(child);
+ }
+}
+
+} // namespace
+
+void EnsureProcessTerminated(ProcessHandle process) {
+ WaitForChildToDie(process, kWaitBeforeKillSeconds);
+}
+
+} // namespace base
diff --git a/chromium/base/process/kill_posix.cc b/chromium/base/process/kill_posix.cc
new file mode 100644
index 00000000000..5938fa53233
--- /dev/null
+++ b/chromium/base/process/kill_posix.cc
@@ -0,0 +1,492 @@
+// 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/kill.h"
+
+#include <signal.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include "base/file_util.h"
+#include "base/logging.h"
+#include "base/posix/eintr_wrapper.h"
+#include "base/process/process_iterator.h"
+#include "base/synchronization/waitable_event.h"
+#include "base/third_party/dynamic_annotations/dynamic_annotations.h"
+#include "base/threading/platform_thread.h"
+
+namespace base {
+
+namespace {
+
+int WaitpidWithTimeout(ProcessHandle handle,
+ int64 wait_milliseconds,
+ bool* success) {
+ // This POSIX version of this function only guarantees that we wait no less
+ // than |wait_milliseconds| for the process to exit. The child process may
+ // exit sometime before the timeout has ended but we may still block for up
+ // to 256 milliseconds after the fact.
+ //
+ // waitpid() has no direct support on POSIX for specifying a timeout, you can
+ // either ask it to block indefinitely or return immediately (WNOHANG).
+ // When a child process terminates a SIGCHLD signal is sent to the parent.
+ // Catching this signal would involve installing a signal handler which may
+ // affect other parts of the application and would be difficult to debug.
+ //
+ // Our strategy is to call waitpid() once up front to check if the process
+ // has already exited, otherwise to loop for wait_milliseconds, sleeping for
+ // at most 256 milliseconds each time using usleep() and then calling
+ // waitpid(). The amount of time we sleep starts out at 1 milliseconds, and
+ // we double it every 4 sleep cycles.
+ //
+ // usleep() is speced to exit if a signal is received for which a handler
+ // has been installed. This means that when a SIGCHLD is sent, it will exit
+ // depending on behavior external to this function.
+ //
+ // This function is used primarily for unit tests, if we want to use it in
+ // the application itself it would probably be best to examine other routes.
+ int status = -1;
+ pid_t ret_pid = HANDLE_EINTR(waitpid(handle, &status, WNOHANG));
+ static const int64 kMaxSleepInMicroseconds = 1 << 18; // ~256 milliseconds.
+ int64 max_sleep_time_usecs = 1 << 10; // ~1 milliseconds.
+ int64 double_sleep_time = 0;
+
+ // If the process hasn't exited yet, then sleep and try again.
+ TimeTicks wakeup_time = TimeTicks::Now() +
+ TimeDelta::FromMilliseconds(wait_milliseconds);
+ while (ret_pid == 0) {
+ TimeTicks now = TimeTicks::Now();
+ if (now > wakeup_time)
+ break;
+ // Guaranteed to be non-negative!
+ int64 sleep_time_usecs = (wakeup_time - now).InMicroseconds();
+ // Sleep for a bit while we wait for the process to finish.
+ if (sleep_time_usecs > max_sleep_time_usecs)
+ sleep_time_usecs = max_sleep_time_usecs;
+
+ // usleep() will return 0 and set errno to EINTR on receipt of a signal
+ // such as SIGCHLD.
+ usleep(sleep_time_usecs);
+ ret_pid = HANDLE_EINTR(waitpid(handle, &status, WNOHANG));
+
+ if ((max_sleep_time_usecs < kMaxSleepInMicroseconds) &&
+ (double_sleep_time++ % 4 == 0)) {
+ max_sleep_time_usecs *= 2;
+ }
+ }
+
+ if (success)
+ *success = (ret_pid != -1);
+
+ return status;
+}
+
+TerminationStatus GetTerminationStatusImpl(ProcessHandle handle,
+ bool can_block,
+ int* exit_code) {
+ int status = 0;
+ const pid_t result = HANDLE_EINTR(waitpid(handle, &status,
+ can_block ? 0 : WNOHANG));
+ if (result == -1) {
+ DPLOG(ERROR) << "waitpid(" << handle << ")";
+ if (exit_code)
+ *exit_code = 0;
+ return TERMINATION_STATUS_NORMAL_TERMINATION;
+ } else if (result == 0) {
+ // the child hasn't exited yet.
+ if (exit_code)
+ *exit_code = 0;
+ return TERMINATION_STATUS_STILL_RUNNING;
+ }
+
+ if (exit_code)
+ *exit_code = status;
+
+ if (WIFSIGNALED(status)) {
+ switch (WTERMSIG(status)) {
+ case SIGABRT:
+ case SIGBUS:
+ case SIGFPE:
+ case SIGILL:
+ case SIGSEGV:
+ return TERMINATION_STATUS_PROCESS_CRASHED;
+ case SIGINT:
+ case SIGKILL:
+ case SIGTERM:
+ return TERMINATION_STATUS_PROCESS_WAS_KILLED;
+ default:
+ break;
+ }
+ }
+
+ if (WIFEXITED(status) && WEXITSTATUS(status) != 0)
+ return TERMINATION_STATUS_ABNORMAL_TERMINATION;
+
+ return TERMINATION_STATUS_NORMAL_TERMINATION;
+}
+
+} // namespace
+
+// Attempts to kill the process identified by the given process
+// entry structure. Ignores specified exit_code; posix can't force that.
+// Returns true if this is successful, false otherwise.
+bool KillProcess(ProcessHandle process_id, int exit_code, bool wait) {
+ DCHECK_GT(process_id, 1) << " tried to kill invalid process_id";
+ if (process_id <= 1)
+ return false;
+ bool result = kill(process_id, SIGTERM) == 0;
+ if (result && wait) {
+ int tries = 60;
+
+ if (RunningOnValgrind()) {
+ // Wait for some extra time when running under Valgrind since the child
+ // processes may take some time doing leak checking.
+ tries *= 2;
+ }
+
+ unsigned sleep_ms = 4;
+
+ // The process may not end immediately due to pending I/O
+ bool exited = false;
+ while (tries-- > 0) {
+ pid_t pid = HANDLE_EINTR(waitpid(process_id, NULL, WNOHANG));
+ if (pid == process_id) {
+ exited = true;
+ break;
+ }
+ if (pid == -1) {
+ if (errno == ECHILD) {
+ // The wait may fail with ECHILD if another process also waited for
+ // the same pid, causing the process state to get cleaned up.
+ exited = true;
+ break;
+ }
+ DPLOG(ERROR) << "Error waiting for process " << process_id;
+ }
+
+ usleep(sleep_ms * 1000);
+ const unsigned kMaxSleepMs = 1000;
+ if (sleep_ms < kMaxSleepMs)
+ sleep_ms *= 2;
+ }
+
+ // If we're waiting and the child hasn't died by now, force it
+ // with a SIGKILL.
+ if (!exited)
+ result = kill(process_id, SIGKILL) == 0;
+ }
+
+ if (!result)
+ DPLOG(ERROR) << "Unable to terminate process " << process_id;
+
+ return result;
+}
+
+bool KillProcessGroup(ProcessHandle process_group_id) {
+ bool result = kill(-1 * process_group_id, SIGKILL) == 0;
+ if (!result)
+ DPLOG(ERROR) << "Unable to terminate process group " << process_group_id;
+ return result;
+}
+
+TerminationStatus GetTerminationStatus(ProcessHandle handle, int* exit_code) {
+ return GetTerminationStatusImpl(handle, false /* can_block */, exit_code);
+}
+
+TerminationStatus WaitForTerminationStatus(ProcessHandle handle,
+ int* exit_code) {
+ return GetTerminationStatusImpl(handle, true /* can_block */, exit_code);
+}
+
+bool WaitForExitCode(ProcessHandle handle, int* exit_code) {
+ int status;
+ if (HANDLE_EINTR(waitpid(handle, &status, 0)) == -1) {
+ NOTREACHED();
+ return false;
+ }
+
+ if (WIFEXITED(status)) {
+ *exit_code = WEXITSTATUS(status);
+ return true;
+ }
+
+ // If it didn't exit cleanly, it must have been signaled.
+ DCHECK(WIFSIGNALED(status));
+ return false;
+}
+
+bool WaitForExitCodeWithTimeout(ProcessHandle handle,
+ int* exit_code,
+ base::TimeDelta timeout) {
+ bool waitpid_success = false;
+ int status = WaitpidWithTimeout(handle, timeout.InMilliseconds(),
+ &waitpid_success);
+ if (status == -1)
+ return false;
+ if (!waitpid_success)
+ return false;
+ if (WIFSIGNALED(status)) {
+ *exit_code = -1;
+ return true;
+ }
+ if (WIFEXITED(status)) {
+ *exit_code = WEXITSTATUS(status);
+ return true;
+ }
+ return false;
+}
+
+bool WaitForProcessesToExit(const FilePath::StringType& executable_name,
+ base::TimeDelta wait,
+ const ProcessFilter* filter) {
+ bool result = false;
+
+ // TODO(port): This is inefficient, but works if there are multiple procs.
+ // TODO(port): use waitpid to avoid leaving zombies around
+
+ base::TimeTicks end_time = base::TimeTicks::Now() + wait;
+ do {
+ NamedProcessIterator iter(executable_name, filter);
+ if (!iter.NextProcessEntry()) {
+ result = true;
+ break;
+ }
+ base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(100));
+ } while ((end_time - base::TimeTicks::Now()) > base::TimeDelta());
+
+ return result;
+}
+
+#if defined(OS_MACOSX)
+// Using kqueue on Mac so that we can wait on non-child processes.
+// We can't use kqueues on child processes because we need to reap
+// our own children using wait.
+static bool WaitForSingleNonChildProcess(ProcessHandle handle,
+ base::TimeDelta wait) {
+ DCHECK_GT(handle, 0);
+ DCHECK(wait.InMilliseconds() == base::kNoTimeout || wait > base::TimeDelta());
+
+ int kq = kqueue();
+ if (kq == -1) {
+ DPLOG(ERROR) << "kqueue";
+ return false;
+ }
+ file_util::ScopedFD kq_closer(&kq);
+
+ struct kevent change = {0};
+ EV_SET(&change, handle, EVFILT_PROC, EV_ADD, NOTE_EXIT, 0, NULL);
+ int result = HANDLE_EINTR(kevent(kq, &change, 1, NULL, 0, NULL));
+ if (result == -1) {
+ if (errno == ESRCH) {
+ // If the process wasn't found, it must be dead.
+ return true;
+ }
+
+ DPLOG(ERROR) << "kevent (setup " << handle << ")";
+ return false;
+ }
+
+ // Keep track of the elapsed time to be able to restart kevent if it's
+ // interrupted.
+ bool wait_forever = wait.InMilliseconds() == base::kNoTimeout;
+ base::TimeDelta remaining_delta;
+ base::TimeTicks deadline;
+ if (!wait_forever) {
+ remaining_delta = wait;
+ deadline = base::TimeTicks::Now() + remaining_delta;
+ }
+
+ result = -1;
+ struct kevent event = {0};
+
+ while (wait_forever || remaining_delta > base::TimeDelta()) {
+ struct timespec remaining_timespec;
+ struct timespec* remaining_timespec_ptr;
+ if (wait_forever) {
+ remaining_timespec_ptr = NULL;
+ } else {
+ remaining_timespec = remaining_delta.ToTimeSpec();
+ remaining_timespec_ptr = &remaining_timespec;
+ }
+
+ result = kevent(kq, NULL, 0, &event, 1, remaining_timespec_ptr);
+
+ if (result == -1 && errno == EINTR) {
+ if (!wait_forever) {
+ remaining_delta = deadline - base::TimeTicks::Now();
+ }
+ result = 0;
+ } else {
+ break;
+ }
+ }
+
+ if (result < 0) {
+ DPLOG(ERROR) << "kevent (wait " << handle << ")";
+ return false;
+ } else if (result > 1) {
+ DLOG(ERROR) << "kevent (wait " << handle << "): unexpected result "
+ << result;
+ return false;
+ } else if (result == 0) {
+ // Timed out.
+ return false;
+ }
+
+ DCHECK_EQ(result, 1);
+
+ if (event.filter != EVFILT_PROC ||
+ (event.fflags & NOTE_EXIT) == 0 ||
+ event.ident != static_cast<uintptr_t>(handle)) {
+ DLOG(ERROR) << "kevent (wait " << handle
+ << "): unexpected event: filter=" << event.filter
+ << ", fflags=" << event.fflags
+ << ", ident=" << event.ident;
+ return false;
+ }
+
+ return true;
+}
+#endif // OS_MACOSX
+
+bool WaitForSingleProcess(ProcessHandle handle, base::TimeDelta wait) {
+ ProcessHandle parent_pid = GetParentProcessId(handle);
+ ProcessHandle our_pid = Process::Current().handle();
+ if (parent_pid != our_pid) {
+#if defined(OS_MACOSX)
+ // On Mac we can wait on non child processes.
+ return WaitForSingleNonChildProcess(handle, wait);
+#else
+ // Currently on Linux we can't handle non child processes.
+ NOTIMPLEMENTED();
+#endif // OS_MACOSX
+ }
+
+ bool waitpid_success;
+ int status = -1;
+ if (wait.InMilliseconds() == base::kNoTimeout) {
+ waitpid_success = (HANDLE_EINTR(waitpid(handle, &status, 0)) != -1);
+ } else {
+ status = WaitpidWithTimeout(
+ handle, wait.InMilliseconds(), &waitpid_success);
+ }
+
+ if (status != -1) {
+ DCHECK(waitpid_success);
+ return WIFEXITED(status);
+ } else {
+ return false;
+ }
+}
+
+bool CleanupProcesses(const FilePath::StringType& executable_name,
+ base::TimeDelta wait,
+ int exit_code,
+ const ProcessFilter* filter) {
+ bool exited_cleanly = WaitForProcessesToExit(executable_name, wait, filter);
+ if (!exited_cleanly)
+ KillProcesses(executable_name, exit_code, filter);
+ return exited_cleanly;
+}
+
+#if !defined(OS_MACOSX)
+
+namespace {
+
+// Return true if the given child is dead. This will also reap the process.
+// Doesn't block.
+static bool IsChildDead(pid_t child) {
+ const pid_t result = HANDLE_EINTR(waitpid(child, NULL, WNOHANG));
+ if (result == -1) {
+ DPLOG(ERROR) << "waitpid(" << child << ")";
+ NOTREACHED();
+ } else if (result > 0) {
+ // The child has died.
+ return true;
+ }
+
+ return false;
+}
+
+// A thread class which waits for the given child to exit and reaps it.
+// If the child doesn't exit within a couple of seconds, kill it.
+class BackgroundReaper : public PlatformThread::Delegate {
+ public:
+ BackgroundReaper(pid_t child, unsigned timeout)
+ : child_(child),
+ timeout_(timeout) {
+ }
+
+ // Overridden from PlatformThread::Delegate:
+ virtual void ThreadMain() OVERRIDE {
+ WaitForChildToDie();
+ delete this;
+ }
+
+ void WaitForChildToDie() {
+ // Wait forever case.
+ if (timeout_ == 0) {
+ pid_t r = HANDLE_EINTR(waitpid(child_, NULL, 0));
+ if (r != child_) {
+ DPLOG(ERROR) << "While waiting for " << child_
+ << " to terminate, we got the following result: " << r;
+ }
+ return;
+ }
+
+ // There's no good way to wait for a specific child to exit in a timed
+ // fashion. (No kqueue on Linux), so we just loop and sleep.
+
+ // Wait for 2 * timeout_ 500 milliseconds intervals.
+ for (unsigned i = 0; i < 2 * timeout_; ++i) {
+ PlatformThread::Sleep(TimeDelta::FromMilliseconds(500));
+ if (IsChildDead(child_))
+ return;
+ }
+
+ if (kill(child_, SIGKILL) == 0) {
+ // SIGKILL is uncatchable. Since the signal was delivered, we can
+ // just wait for the process to die now in a blocking manner.
+ if (HANDLE_EINTR(waitpid(child_, NULL, 0)) < 0)
+ DPLOG(WARNING) << "waitpid";
+ } else {
+ DLOG(ERROR) << "While waiting for " << child_ << " to terminate we"
+ << " failed to deliver a SIGKILL signal (" << errno << ").";
+ }
+ }
+
+ private:
+ const pid_t child_;
+ // Number of seconds to wait, if 0 then wait forever and do not attempt to
+ // kill |child_|.
+ const unsigned timeout_;
+
+ DISALLOW_COPY_AND_ASSIGN(BackgroundReaper);
+};
+
+} // namespace
+
+void EnsureProcessTerminated(ProcessHandle process) {
+ // If the child is already dead, then there's nothing to do.
+ if (IsChildDead(process))
+ return;
+
+ const unsigned timeout = 2; // seconds
+ BackgroundReaper* reaper = new BackgroundReaper(process, timeout);
+ PlatformThread::CreateNonJoinable(0, reaper);
+}
+
+void EnsureProcessGetsReaped(ProcessHandle process) {
+ // If the child is already dead, then there's nothing to do.
+ if (IsChildDead(process))
+ return;
+
+ BackgroundReaper* reaper = new BackgroundReaper(process, 0);
+ PlatformThread::CreateNonJoinable(0, reaper);
+}
+
+#endif // !defined(OS_MACOSX)
+
+} // namespace base
diff --git a/chromium/base/process/kill_win.cc b/chromium/base/process/kill_win.cc
new file mode 100644
index 00000000000..99a7c661851
--- /dev/null
+++ b/chromium/base/process/kill_win.cc
@@ -0,0 +1,255 @@
+// 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/kill.h"
+
+#include <io.h>
+#include <windows.h>
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/logging.h"
+#include "base/message_loop/message_loop.h"
+#include "base/process/process_iterator.h"
+#include "base/win/object_watcher.h"
+
+namespace base {
+
+namespace {
+
+// Exit codes with special meanings on Windows.
+const DWORD kNormalTerminationExitCode = 0;
+const DWORD kDebuggerInactiveExitCode = 0xC0000354;
+const DWORD kKeyboardInterruptExitCode = 0xC000013A;
+const DWORD kDebuggerTerminatedExitCode = 0x40010004;
+
+// This exit code is used by the Windows task manager when it kills a
+// process. It's value is obviously not that unique, and it's
+// surprising to me that the task manager uses this value, but it
+// seems to be common practice on Windows to test for it as an
+// indication that the task manager has killed something if the
+// process goes away.
+const DWORD kProcessKilledExitCode = 1;
+
+// Maximum amount of time (in milliseconds) to wait for the process to exit.
+static const int kWaitInterval = 2000;
+
+class TimerExpiredTask : public win::ObjectWatcher::Delegate {
+ public:
+ explicit TimerExpiredTask(ProcessHandle process);
+ ~TimerExpiredTask();
+
+ void TimedOut();
+
+ // MessageLoop::Watcher -----------------------------------------------------
+ virtual void OnObjectSignaled(HANDLE object);
+
+ private:
+ void KillProcess();
+
+ // The process that we are watching.
+ ProcessHandle process_;
+
+ win::ObjectWatcher watcher_;
+
+ DISALLOW_COPY_AND_ASSIGN(TimerExpiredTask);
+};
+
+TimerExpiredTask::TimerExpiredTask(ProcessHandle process) : process_(process) {
+ watcher_.StartWatching(process_, this);
+}
+
+TimerExpiredTask::~TimerExpiredTask() {
+ TimedOut();
+ DCHECK(!process_) << "Make sure to close the handle.";
+}
+
+void TimerExpiredTask::TimedOut() {
+ if (process_)
+ KillProcess();
+}
+
+void TimerExpiredTask::OnObjectSignaled(HANDLE object) {
+ CloseHandle(process_);
+ process_ = NULL;
+}
+
+void TimerExpiredTask::KillProcess() {
+ // Stop watching the process handle since we're killing it.
+ watcher_.StopWatching();
+
+ // OK, time to get frisky. We don't actually care when the process
+ // terminates. We just care that it eventually terminates, and that's what
+ // TerminateProcess should do for us. Don't check for the result code since
+ // it fails quite often. This should be investigated eventually.
+ base::KillProcess(process_, kProcessKilledExitCode, false);
+
+ // Now, just cleanup as if the process exited normally.
+ OnObjectSignaled(process_);
+}
+
+} // namespace
+
+bool KillProcess(ProcessHandle process, int exit_code, bool wait) {
+ bool result = (TerminateProcess(process, exit_code) != FALSE);
+ if (result && wait) {
+ // The process may not end immediately due to pending I/O
+ if (WAIT_OBJECT_0 != WaitForSingleObject(process, 60 * 1000))
+ DLOG_GETLASTERROR(ERROR) << "Error waiting for process exit";
+ } else if (!result) {
+ DLOG_GETLASTERROR(ERROR) << "Unable to terminate process";
+ }
+ return result;
+}
+
+// Attempts to kill the process identified by the given process
+// entry structure, giving it the specified exit code.
+// Returns true if this is successful, false otherwise.
+bool KillProcessById(ProcessId process_id, int exit_code, bool wait) {
+ HANDLE process = OpenProcess(PROCESS_TERMINATE | SYNCHRONIZE,
+ FALSE, // Don't inherit handle
+ process_id);
+ if (!process) {
+ DLOG_GETLASTERROR(ERROR) << "Unable to open process " << process_id;
+ return false;
+ }
+ bool ret = KillProcess(process, exit_code, wait);
+ CloseHandle(process);
+ return ret;
+}
+
+TerminationStatus GetTerminationStatus(ProcessHandle handle, int* exit_code) {
+ DWORD tmp_exit_code = 0;
+
+ if (!::GetExitCodeProcess(handle, &tmp_exit_code)) {
+ DLOG_GETLASTERROR(FATAL) << "GetExitCodeProcess() failed";
+ if (exit_code) {
+ // This really is a random number. We haven't received any
+ // information about the exit code, presumably because this
+ // process doesn't have permission to get the exit code, or
+ // because of some other cause for GetExitCodeProcess to fail
+ // (MSDN docs don't give the possible failure error codes for
+ // this function, so it could be anything). But we don't want
+ // to leave exit_code uninitialized, since that could cause
+ // random interpretations of the exit code. So we assume it
+ // terminated "normally" in this case.
+ *exit_code = kNormalTerminationExitCode;
+ }
+ // Assume the child has exited normally if we can't get the exit
+ // code.
+ return TERMINATION_STATUS_NORMAL_TERMINATION;
+ }
+ if (tmp_exit_code == STILL_ACTIVE) {
+ DWORD wait_result = WaitForSingleObject(handle, 0);
+ if (wait_result == WAIT_TIMEOUT) {
+ if (exit_code)
+ *exit_code = wait_result;
+ return TERMINATION_STATUS_STILL_RUNNING;
+ }
+
+ if (wait_result == WAIT_FAILED) {
+ DLOG_GETLASTERROR(ERROR) << "WaitForSingleObject() failed";
+ } else {
+ DCHECK_EQ(WAIT_OBJECT_0, wait_result);
+
+ // Strange, the process used 0x103 (STILL_ACTIVE) as exit code.
+ NOTREACHED();
+ }
+
+ return TERMINATION_STATUS_ABNORMAL_TERMINATION;
+ }
+
+ if (exit_code)
+ *exit_code = tmp_exit_code;
+
+ switch (tmp_exit_code) {
+ case kNormalTerminationExitCode:
+ return TERMINATION_STATUS_NORMAL_TERMINATION;
+ case kDebuggerInactiveExitCode: // STATUS_DEBUGGER_INACTIVE.
+ case kKeyboardInterruptExitCode: // Control-C/end session.
+ case kDebuggerTerminatedExitCode: // Debugger terminated process.
+ case kProcessKilledExitCode: // Task manager kill.
+ return TERMINATION_STATUS_PROCESS_WAS_KILLED;
+ default:
+ // All other exit codes indicate crashes.
+ return TERMINATION_STATUS_PROCESS_CRASHED;
+ }
+}
+
+bool WaitForExitCode(ProcessHandle handle, int* exit_code) {
+ bool success = WaitForExitCodeWithTimeout(
+ handle, exit_code, base::TimeDelta::FromMilliseconds(INFINITE));
+ CloseProcessHandle(handle);
+ return success;
+}
+
+bool WaitForExitCodeWithTimeout(ProcessHandle handle,
+ int* exit_code,
+ base::TimeDelta timeout) {
+ if (::WaitForSingleObject(handle, timeout.InMilliseconds()) != WAIT_OBJECT_0)
+ return false;
+ DWORD temp_code; // Don't clobber out-parameters in case of failure.
+ if (!::GetExitCodeProcess(handle, &temp_code))
+ return false;
+
+ *exit_code = temp_code;
+ return true;
+}
+
+bool WaitForProcessesToExit(const FilePath::StringType& executable_name,
+ base::TimeDelta wait,
+ const ProcessFilter* filter) {
+ const ProcessEntry* entry;
+ bool result = true;
+ DWORD start_time = GetTickCount();
+
+ NamedProcessIterator iter(executable_name, filter);
+ while ((entry = iter.NextProcessEntry())) {
+ DWORD remaining_wait = std::max<int64>(
+ 0, wait.InMilliseconds() - (GetTickCount() - start_time));
+ HANDLE process = OpenProcess(SYNCHRONIZE,
+ FALSE,
+ entry->th32ProcessID);
+ DWORD wait_result = WaitForSingleObject(process, remaining_wait);
+ CloseHandle(process);
+ result = result && (wait_result == WAIT_OBJECT_0);
+ }
+
+ return result;
+}
+
+bool WaitForSingleProcess(ProcessHandle handle, base::TimeDelta wait) {
+ int exit_code;
+ if (!WaitForExitCodeWithTimeout(handle, &exit_code, wait))
+ return false;
+ return exit_code == 0;
+}
+
+bool CleanupProcesses(const FilePath::StringType& executable_name,
+ base::TimeDelta wait,
+ int exit_code,
+ const ProcessFilter* filter) {
+ bool exited_cleanly = WaitForProcessesToExit(executable_name, wait, filter);
+ if (!exited_cleanly)
+ KillProcesses(executable_name, exit_code, filter);
+ return exited_cleanly;
+}
+
+void EnsureProcessTerminated(ProcessHandle process) {
+ DCHECK(process != GetCurrentProcess());
+
+ // If already signaled, then we are done!
+ if (WaitForSingleObject(process, 0) == WAIT_OBJECT_0) {
+ CloseHandle(process);
+ return;
+ }
+
+ MessageLoop::current()->PostDelayedTask(
+ FROM_HERE,
+ base::Bind(&TimerExpiredTask::TimedOut,
+ base::Owned(new TimerExpiredTask(process))),
+ base::TimeDelta::FromMilliseconds(kWaitInterval));
+}
+
+} // namespace base
diff --git a/chromium/base/process/launch.h b/chromium/base/process/launch.h
new file mode 100644
index 00000000000..45b10537b84
--- /dev/null
+++ b/chromium/base/process/launch.h
@@ -0,0 +1,258 @@
+// 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 functions for launching subprocesses.
+
+#ifndef BASE_PROCESS_LAUNCH_H_
+#define BASE_PROCESS_LAUNCH_H_
+
+#include <set>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "base/base_export.h"
+#include "base/basictypes.h"
+#include "base/process/process_handle.h"
+
+#if defined(OS_POSIX)
+#include "base/posix/file_descriptor_shuffle.h"
+#elif defined(OS_WIN)
+#include <windows.h>
+#endif
+
+class CommandLine;
+
+namespace base {
+
+typedef std::vector<std::pair<std::string, std::string> > EnvironmentVector;
+typedef std::vector<std::pair<int, int> > FileHandleMappingVector;
+
+// Options for launching a subprocess that are passed to LaunchProcess().
+// The default constructor constructs the object with default options.
+struct LaunchOptions {
+ LaunchOptions()
+ : wait(false),
+#if defined(OS_WIN)
+ start_hidden(false),
+ inherit_handles(false),
+ as_user(NULL),
+ empty_desktop_name(false),
+ job_handle(NULL),
+ stdin_handle(NULL),
+ stdout_handle(NULL),
+ stderr_handle(NULL),
+ force_breakaway_from_job_(false)
+#else
+ environ(NULL),
+ fds_to_remap(NULL),
+ maximize_rlimits(NULL),
+ new_process_group(false)
+#if defined(OS_LINUX)
+ , clone_flags(0)
+#endif // OS_LINUX
+#if defined(OS_CHROMEOS)
+ , ctrl_terminal_fd(-1)
+#endif // OS_CHROMEOS
+#endif // !defined(OS_WIN)
+ {}
+
+ // If true, wait for the process to complete.
+ bool wait;
+
+#if defined(OS_WIN)
+ bool start_hidden;
+
+ // If true, the new process inherits handles from the parent. In production
+ // code this flag should be used only when running short-lived, trusted
+ // binaries, because open handles from other libraries and subsystems will
+ // leak to the child process, causing errors such as open socket hangs.
+ bool inherit_handles;
+
+ // If non-NULL, runs as if the user represented by the token had launched it.
+ // Whether the application is visible on the interactive desktop depends on
+ // the token belonging to an interactive logon session.
+ //
+ // To avoid hard to diagnose problems, when specified this loads the
+ // environment variables associated with the user and if this operation fails
+ // the entire call fails as well.
+ UserTokenHandle as_user;
+
+ // If true, use an empty string for the desktop name.
+ bool empty_desktop_name;
+
+ // If non-NULL, launches the application in that job object. The process will
+ // be terminated immediately and LaunchProcess() will fail if assignment to
+ // the job object fails.
+ HANDLE job_handle;
+
+ // Handles for the redirection of stdin, stdout and stderr. The handles must
+ // be inheritable. Caller should either set all three of them or none (i.e.
+ // there is no way to redirect stderr without redirecting stdin). The
+ // |inherit_handles| flag must be set to true when redirecting stdio stream.
+ HANDLE stdin_handle;
+ HANDLE stdout_handle;
+ HANDLE stderr_handle;
+
+ // If set to true, ensures that the child process is launched with the
+ // CREATE_BREAKAWAY_FROM_JOB flag which allows it to breakout of the parent
+ // job if any.
+ bool force_breakaway_from_job_;
+#else
+ // If non-NULL, set/unset environment variables.
+ // See documentation of AlterEnvironment().
+ // This pointer is owned by the caller and must live through the
+ // call to LaunchProcess().
+ const EnvironmentVector* environ;
+
+ // If non-NULL, remap file descriptors according to the mapping of
+ // src fd->dest fd to propagate FDs into the child process.
+ // This pointer is owned by the caller and must live through the
+ // call to LaunchProcess().
+ const FileHandleMappingVector* fds_to_remap;
+
+ // Each element is an RLIMIT_* constant that should be raised to its
+ // rlim_max. This pointer is owned by the caller and must live through
+ // the call to LaunchProcess().
+ const std::set<int>* maximize_rlimits;
+
+ // If true, start the process in a new process group, instead of
+ // inheriting the parent's process group. The pgid of the child process
+ // will be the same as its pid.
+ bool new_process_group;
+
+#if defined(OS_LINUX)
+ // If non-zero, start the process using clone(), using flags as provided.
+ int clone_flags;
+#endif // defined(OS_LINUX)
+
+#if defined(OS_CHROMEOS)
+ // If non-negative, the specified file descriptor will be set as the launched
+ // process' controlling terminal.
+ int ctrl_terminal_fd;
+#endif // defined(OS_CHROMEOS)
+
+#endif // !defined(OS_WIN)
+};
+
+// Launch a process via the command line |cmdline|.
+// See the documentation of LaunchOptions for details on |options|.
+//
+// Returns true upon success.
+//
+// Upon success, if |process_handle| is non-NULL, it will be filled in with the
+// handle of the launched process. NOTE: In this case, the caller is
+// responsible for closing the handle so that it doesn't leak!
+// Otherwise, the process handle will be implicitly closed.
+//
+// Unix-specific notes:
+// - All file descriptors open in the parent process will be closed in the
+// child process except for any preserved by options::fds_to_remap, and
+// stdin, stdout, and stderr. If not remapped by options::fds_to_remap,
+// stdin is reopened as /dev/null, and the child is allowed to inherit its
+// parent's stdout and stderr.
+// - If the first argument on the command line does not contain a slash,
+// PATH will be searched. (See man execvp.)
+BASE_EXPORT bool LaunchProcess(const CommandLine& cmdline,
+ const LaunchOptions& options,
+ ProcessHandle* process_handle);
+
+#if defined(OS_WIN)
+// Windows-specific LaunchProcess that takes the command line as a
+// string. Useful for situations where you need to control the
+// command line arguments directly, but prefer the CommandLine version
+// if launching Chrome itself.
+//
+// The first command line argument should be the path to the process,
+// and don't forget to quote it.
+//
+// Example (including literal quotes)
+// cmdline = "c:\windows\explorer.exe" -foo "c:\bar\"
+BASE_EXPORT bool LaunchProcess(const string16& cmdline,
+ const LaunchOptions& options,
+ ProcessHandle* process_handle);
+
+#elif defined(OS_POSIX)
+// A POSIX-specific version of LaunchProcess that takes an argv array
+// instead of a CommandLine. Useful for situations where you need to
+// control the command line arguments directly, but prefer the
+// CommandLine version if launching Chrome itself.
+BASE_EXPORT bool LaunchProcess(const std::vector<std::string>& argv,
+ const LaunchOptions& options,
+ ProcessHandle* process_handle);
+
+// AlterEnvironment returns a modified environment vector, constructed from the
+// given environment and the list of changes given in |changes|. Each key in
+// the environment is matched against the first element of the pairs. In the
+// event of a match, the value is replaced by the second of the pair, unless
+// the second is empty, in which case the key-value is removed.
+//
+// The returned array is allocated using new[] and must be freed by the caller.
+BASE_EXPORT char** AlterEnvironment(const EnvironmentVector& changes,
+ const char* const* const env);
+
+// Close all file descriptors, except those which are a destination in the
+// given multimap. Only call this function in a child process where you know
+// that there aren't any other threads.
+BASE_EXPORT void CloseSuperfluousFds(const InjectiveMultimap& saved_map);
+#endif // defined(OS_POSIX)
+
+#if defined(OS_WIN)
+// Set JOBOBJECT_EXTENDED_LIMIT_INFORMATION to JobObject |job_object|.
+// As its limit_info.BasicLimitInformation.LimitFlags has
+// JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE.
+// When the provide JobObject |job_object| is closed, the binded process will
+// be terminated.
+BASE_EXPORT bool SetJobObjectAsKillOnJobClose(HANDLE job_object);
+
+// Output multi-process printf, cout, cerr, etc to the cmd.exe console that ran
+// chrome. This is not thread-safe: only call from main thread.
+BASE_EXPORT void RouteStdioToConsole();
+#endif // defined(OS_WIN)
+
+// Executes the application specified by |cl| and wait for it to exit. Stores
+// the output (stdout) in |output|. Redirects stderr to /dev/null. Returns true
+// on success (application launched and exited cleanly, with exit code
+// indicating success).
+BASE_EXPORT bool GetAppOutput(const CommandLine& cl, std::string* output);
+
+#if defined(OS_POSIX)
+// A POSIX-specific version of GetAppOutput that takes an argv array
+// instead of a CommandLine. Useful for situations where you need to
+// control the command line arguments directly.
+BASE_EXPORT bool GetAppOutput(const std::vector<std::string>& argv,
+ std::string* output);
+
+// A restricted version of |GetAppOutput()| which (a) clears the environment,
+// and (b) stores at most |max_output| bytes; also, it doesn't search the path
+// for the command.
+BASE_EXPORT bool GetAppOutputRestricted(const CommandLine& cl,
+ std::string* output, size_t max_output);
+
+// A version of |GetAppOutput()| which also returns the exit code of the
+// executed command. Returns true if the application runs and exits cleanly. If
+// this is the case the exit code of the application is available in
+// |*exit_code|.
+BASE_EXPORT bool GetAppOutputWithExitCode(const CommandLine& cl,
+ std::string* output, int* exit_code);
+#endif // defined(OS_POSIX)
+
+// If supported on the platform, and the user has sufficent rights, increase
+// the current process's scheduling priority to a high priority.
+BASE_EXPORT void RaiseProcessToHighPriority();
+
+#if defined(OS_MACOSX)
+// Restore the default exception handler, setting it to Apple Crash Reporter
+// (ReportCrash). When forking and execing a new process, the child will
+// inherit the parent's exception ports, which may be set to the Breakpad
+// instance running inside the parent. The parent's Breakpad instance should
+// not handle the child's exceptions. Calling RestoreDefaultExceptionHandler
+// in the child after forking will restore the standard exception handler.
+// See http://crbug.com/20371/ for more details.
+void RestoreDefaultExceptionHandler();
+#endif // defined(OS_MACOSX)
+
+} // namespace base
+
+#endif // BASE_PROCESS_LAUNCH_H_
diff --git a/chromium/base/process/launch_ios.cc b/chromium/base/process/launch_ios.cc
new file mode 100644
index 00000000000..3c700f8f0a3
--- /dev/null
+++ b/chromium/base/process/launch_ios.cc
@@ -0,0 +1,13 @@
+// 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/launch.h"
+
+namespace base {
+
+void RaiseProcessToHighPriority() {
+ // Impossible on iOS. Do nothing.
+}
+
+} // namespace base
diff --git a/chromium/base/process/launch_mac.cc b/chromium/base/process/launch_mac.cc
new file mode 100644
index 00000000000..176edca72ea
--- /dev/null
+++ b/chromium/base/process/launch_mac.cc
@@ -0,0 +1,28 @@
+// 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/launch.h"
+
+#include <mach/mach.h>
+
+namespace base {
+
+void RestoreDefaultExceptionHandler() {
+ // This function is tailored to remove the Breakpad exception handler.
+ // exception_mask matches s_exception_mask in
+ // breakpad/src/client/mac/handler/exception_handler.cc
+ const exception_mask_t exception_mask = EXC_MASK_BAD_ACCESS |
+ EXC_MASK_BAD_INSTRUCTION |
+ EXC_MASK_ARITHMETIC |
+ EXC_MASK_BREAKPOINT;
+
+ // Setting the exception port to MACH_PORT_NULL may not be entirely
+ // kosher to restore the default exception handler, but in practice,
+ // it results in the exception port being set to Apple Crash Reporter,
+ // the desired behavior.
+ task_set_exception_ports(mach_task_self(), exception_mask, MACH_PORT_NULL,
+ EXCEPTION_DEFAULT, THREAD_STATE_NONE);
+}
+
+} // namespace base
diff --git a/chromium/base/process/launch_posix.cc b/chromium/base/process/launch_posix.cc
new file mode 100644
index 00000000000..52e149cd8df
--- /dev/null
+++ b/chromium/base/process/launch_posix.cc
@@ -0,0 +1,760 @@
+// 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/launch.h"
+
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <sys/resource.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include <iterator>
+#include <limits>
+#include <set>
+
+#include "base/allocator/type_profiler_control.h"
+#include "base/command_line.h"
+#include "base/compiler_specific.h"
+#include "base/debug/debugger.h"
+#include "base/debug/stack_trace.h"
+#include "base/file_util.h"
+#include "base/files/dir_reader_posix.h"
+#include "base/logging.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/posix/eintr_wrapper.h"
+#include "base/process/kill.h"
+#include "base/process/process_metrics.h"
+#include "base/strings/stringprintf.h"
+#include "base/synchronization/waitable_event.h"
+#include "base/third_party/dynamic_annotations/dynamic_annotations.h"
+#include "base/threading/platform_thread.h"
+#include "base/threading/thread_restrictions.h"
+
+#if defined(OS_CHROMEOS)
+#include <sys/ioctl.h>
+#endif
+
+#if defined(OS_FREEBSD)
+#include <sys/event.h>
+#include <sys/ucontext.h>
+#endif
+
+#if defined(OS_MACOSX)
+#include <crt_externs.h>
+#include <sys/event.h>
+#else
+extern char** environ;
+#endif
+
+namespace base {
+
+namespace {
+
+// Get the process's "environment" (i.e. the thing that setenv/getenv
+// work with).
+char** GetEnvironment() {
+#if defined(OS_MACOSX)
+ return *_NSGetEnviron();
+#else
+ return environ;
+#endif
+}
+
+// Set the process's "environment" (i.e. the thing that setenv/getenv
+// work with).
+void SetEnvironment(char** env) {
+#if defined(OS_MACOSX)
+ *_NSGetEnviron() = env;
+#else
+ environ = env;
+#endif
+}
+
+// Set the calling thread's signal mask to new_sigmask and return
+// the previous signal mask.
+sigset_t SetSignalMask(const sigset_t& new_sigmask) {
+ sigset_t old_sigmask;
+#if defined(OS_ANDROID)
+ // POSIX says pthread_sigmask() must be used in multi-threaded processes,
+ // but Android's pthread_sigmask() was broken until 4.1:
+ // https://code.google.com/p/android/issues/detail?id=15337
+ // http://stackoverflow.com/questions/13777109/pthread-sigmask-on-android-not-working
+ RAW_CHECK(sigprocmask(SIG_SETMASK, &new_sigmask, &old_sigmask) == 0);
+#else
+ RAW_CHECK(pthread_sigmask(SIG_SETMASK, &new_sigmask, &old_sigmask) == 0);
+#endif
+ return old_sigmask;
+}
+
+#if !defined(OS_LINUX) || \
+ (!defined(__i386__) && !defined(__x86_64__) && !defined(__arm__))
+void ResetChildSignalHandlersToDefaults() {
+ // The previous signal handlers are likely to be meaningless in the child's
+ // context so we reset them to the defaults for now. http://crbug.com/44953
+ // These signal handlers are set up at least in browser_main_posix.cc:
+ // BrowserMainPartsPosix::PreEarlyInitialization and stack_trace_posix.cc:
+ // EnableInProcessStackDumping.
+ signal(SIGHUP, SIG_DFL);
+ signal(SIGINT, SIG_DFL);
+ signal(SIGILL, SIG_DFL);
+ signal(SIGABRT, SIG_DFL);
+ signal(SIGFPE, SIG_DFL);
+ signal(SIGBUS, SIG_DFL);
+ signal(SIGSEGV, SIG_DFL);
+ signal(SIGSYS, SIG_DFL);
+ signal(SIGTERM, SIG_DFL);
+}
+
+#else
+
+// TODO(jln): remove the Linux special case once kernels are fixed.
+
+// Internally the kernel makes sigset_t an array of long large enough to have
+// one bit per signal.
+typedef uint64_t kernel_sigset_t;
+
+// This is what struct sigaction looks like to the kernel at least on X86 and
+// ARM. MIPS, for instance, is very different.
+struct kernel_sigaction {
+ void* k_sa_handler; // For this usage it only needs to be a generic pointer.
+ unsigned long k_sa_flags;
+ void* k_sa_restorer; // For this usage it only needs to be a generic pointer.
+ kernel_sigset_t k_sa_mask;
+};
+
+// glibc's sigaction() will prevent access to sa_restorer, so we need to roll
+// our own.
+int sys_rt_sigaction(int sig, const struct kernel_sigaction* act,
+ struct kernel_sigaction* oact) {
+ return syscall(SYS_rt_sigaction, sig, act, oact, sizeof(kernel_sigset_t));
+}
+
+// This function is intended to be used in between fork() and execve() and will
+// reset all signal handlers to the default.
+// The motivation for going through all of them is that sa_restorer can leak
+// from parents and help defeat ASLR on buggy kernels. We reset it to NULL.
+// See crbug.com/177956.
+void ResetChildSignalHandlersToDefaults(void) {
+ for (int signum = 1; ; ++signum) {
+ struct kernel_sigaction act = {0};
+ int sigaction_get_ret = sys_rt_sigaction(signum, NULL, &act);
+ if (sigaction_get_ret && errno == EINVAL) {
+#if !defined(NDEBUG)
+ // Linux supports 32 real-time signals from 33 to 64.
+ // If the number of signals in the Linux kernel changes, someone should
+ // look at this code.
+ const int kNumberOfSignals = 64;
+ RAW_CHECK(signum == kNumberOfSignals + 1);
+#endif // !defined(NDEBUG)
+ break;
+ }
+ // All other failures are fatal.
+ if (sigaction_get_ret) {
+ RAW_LOG(FATAL, "sigaction (get) failed.");
+ }
+
+ // The kernel won't allow to re-set SIGKILL or SIGSTOP.
+ if (signum != SIGSTOP && signum != SIGKILL) {
+ act.k_sa_handler = reinterpret_cast<void*>(SIG_DFL);
+ act.k_sa_restorer = NULL;
+ if (sys_rt_sigaction(signum, &act, NULL)) {
+ RAW_LOG(FATAL, "sigaction (set) failed.");
+ }
+ }
+#if !defined(NDEBUG)
+ // Now ask the kernel again and check that no restorer will leak.
+ if (sys_rt_sigaction(signum, NULL, &act) || act.k_sa_restorer) {
+ RAW_LOG(FATAL, "Cound not fix sa_restorer.");
+ }
+#endif // !defined(NDEBUG)
+ }
+}
+#endif // !defined(OS_LINUX) ||
+ // (!defined(__i386__) && !defined(__x86_64__) && !defined(__arm__))
+
+} // anonymous namespace
+
+// A class to handle auto-closing of DIR*'s.
+class ScopedDIRClose {
+ public:
+ inline void operator()(DIR* x) const {
+ if (x) {
+ closedir(x);
+ }
+ }
+};
+typedef scoped_ptr_malloc<DIR, ScopedDIRClose> ScopedDIR;
+
+#if defined(OS_LINUX)
+static const char kFDDir[] = "/proc/self/fd";
+#elif defined(OS_MACOSX)
+static const char kFDDir[] = "/dev/fd";
+#elif defined(OS_SOLARIS)
+static const char kFDDir[] = "/dev/fd";
+#elif defined(OS_FREEBSD)
+static const char kFDDir[] = "/dev/fd";
+#elif defined(OS_OPENBSD)
+static const char kFDDir[] = "/dev/fd";
+#elif defined(OS_ANDROID)
+static const char kFDDir[] = "/proc/self/fd";
+#endif
+
+void CloseSuperfluousFds(const base::InjectiveMultimap& saved_mapping) {
+ // DANGER: no calls to malloc are allowed from now on:
+ // http://crbug.com/36678
+
+ // Get the maximum number of FDs possible.
+ size_t max_fds = GetMaxFds();
+
+ DirReaderPosix fd_dir(kFDDir);
+ if (!fd_dir.IsValid()) {
+ // Fallback case: Try every possible fd.
+ for (size_t i = 0; i < max_fds; ++i) {
+ const int fd = static_cast<int>(i);
+ if (fd == STDIN_FILENO || fd == STDOUT_FILENO || fd == STDERR_FILENO)
+ continue;
+ InjectiveMultimap::const_iterator j;
+ for (j = saved_mapping.begin(); j != saved_mapping.end(); j++) {
+ if (fd == j->dest)
+ break;
+ }
+ if (j != saved_mapping.end())
+ continue;
+
+ // Since we're just trying to close anything we can find,
+ // ignore any error return values of close().
+ ignore_result(HANDLE_EINTR(close(fd)));
+ }
+ return;
+ }
+
+ const int dir_fd = fd_dir.fd();
+
+ for ( ; fd_dir.Next(); ) {
+ // Skip . and .. entries.
+ if (fd_dir.name()[0] == '.')
+ continue;
+
+ char *endptr;
+ errno = 0;
+ const long int fd = strtol(fd_dir.name(), &endptr, 10);
+ if (fd_dir.name()[0] == 0 || *endptr || fd < 0 || errno)
+ continue;
+ if (fd == STDIN_FILENO || fd == STDOUT_FILENO || fd == STDERR_FILENO)
+ continue;
+ InjectiveMultimap::const_iterator i;
+ for (i = saved_mapping.begin(); i != saved_mapping.end(); i++) {
+ if (fd == i->dest)
+ break;
+ }
+ if (i != saved_mapping.end())
+ continue;
+ if (fd == dir_fd)
+ continue;
+
+ // When running under Valgrind, Valgrind opens several FDs for its
+ // own use and will complain if we try to close them. All of
+ // these FDs are >= |max_fds|, so we can check against that here
+ // before closing. See https://bugs.kde.org/show_bug.cgi?id=191758
+ if (fd < static_cast<int>(max_fds)) {
+ int ret = HANDLE_EINTR(close(fd));
+ DPCHECK(ret == 0);
+ }
+ }
+}
+
+char** AlterEnvironment(const EnvironmentVector& changes,
+ const char* const* const env) {
+ unsigned count = 0;
+ unsigned size = 0;
+
+ // First assume that all of the current environment will be included.
+ for (unsigned i = 0; env[i]; i++) {
+ const char *const pair = env[i];
+ count++;
+ size += strlen(pair) + 1 /* terminating NUL */;
+ }
+
+ for (EnvironmentVector::const_iterator j = changes.begin();
+ j != changes.end();
+ ++j) {
+ bool found = false;
+ const char *pair;
+
+ for (unsigned i = 0; env[i]; i++) {
+ pair = env[i];
+ const char *const equals = strchr(pair, '=');
+ if (!equals)
+ continue;
+ const unsigned keylen = equals - pair;
+ if (keylen == j->first.size() &&
+ memcmp(pair, j->first.data(), keylen) == 0) {
+ found = true;
+ break;
+ }
+ }
+
+ // if found, we'll either be deleting or replacing this element.
+ if (found) {
+ count--;
+ size -= strlen(pair) + 1;
+ if (j->second.size())
+ found = false;
+ }
+
+ // if !found, then we have a new element to add.
+ if (!found && !j->second.empty()) {
+ count++;
+ size += j->first.size() + 1 /* '=' */ + j->second.size() + 1 /* NUL */;
+ }
+ }
+
+ count++; // for the final NULL
+ uint8_t *buffer = new uint8_t[sizeof(char*) * count + size];
+ char **const ret = reinterpret_cast<char**>(buffer);
+ unsigned k = 0;
+ char *scratch = reinterpret_cast<char*>(buffer + sizeof(char*) * count);
+
+ for (unsigned i = 0; env[i]; i++) {
+ const char *const pair = env[i];
+ const char *const equals = strchr(pair, '=');
+ if (!equals) {
+ const unsigned len = strlen(pair);
+ ret[k++] = scratch;
+ memcpy(scratch, pair, len + 1);
+ scratch += len + 1;
+ continue;
+ }
+ const unsigned keylen = equals - pair;
+ bool handled = false;
+ for (EnvironmentVector::const_iterator
+ j = changes.begin(); j != changes.end(); j++) {
+ if (j->first.size() == keylen &&
+ memcmp(j->first.data(), pair, keylen) == 0) {
+ if (!j->second.empty()) {
+ ret[k++] = scratch;
+ memcpy(scratch, pair, keylen + 1);
+ scratch += keylen + 1;
+ memcpy(scratch, j->second.c_str(), j->second.size() + 1);
+ scratch += j->second.size() + 1;
+ }
+ handled = true;
+ break;
+ }
+ }
+
+ if (!handled) {
+ const unsigned len = strlen(pair);
+ ret[k++] = scratch;
+ memcpy(scratch, pair, len + 1);
+ scratch += len + 1;
+ }
+ }
+
+ // Now handle new elements
+ for (EnvironmentVector::const_iterator
+ j = changes.begin(); j != changes.end(); j++) {
+ if (j->second.empty())
+ continue;
+
+ bool found = false;
+ for (unsigned i = 0; env[i]; i++) {
+ const char *const pair = env[i];
+ const char *const equals = strchr(pair, '=');
+ if (!equals)
+ continue;
+ const unsigned keylen = equals - pair;
+ if (keylen == j->first.size() &&
+ memcmp(pair, j->first.data(), keylen) == 0) {
+ found = true;
+ break;
+ }
+ }
+
+ if (!found) {
+ ret[k++] = scratch;
+ memcpy(scratch, j->first.data(), j->first.size());
+ scratch += j->first.size();
+ *scratch++ = '=';
+ memcpy(scratch, j->second.c_str(), j->second.size() + 1);
+ scratch += j->second.size() + 1;
+ }
+ }
+
+ ret[k] = NULL;
+ return ret;
+}
+
+bool LaunchProcess(const std::vector<std::string>& argv,
+ const LaunchOptions& options,
+ ProcessHandle* process_handle) {
+ size_t fd_shuffle_size = 0;
+ if (options.fds_to_remap) {
+ fd_shuffle_size = options.fds_to_remap->size();
+ }
+
+ InjectiveMultimap fd_shuffle1;
+ InjectiveMultimap fd_shuffle2;
+ fd_shuffle1.reserve(fd_shuffle_size);
+ fd_shuffle2.reserve(fd_shuffle_size);
+
+ scoped_ptr<char*[]> argv_cstr(new char*[argv.size() + 1]);
+ scoped_ptr<char*[]> new_environ;
+ if (options.environ)
+ new_environ.reset(AlterEnvironment(*options.environ, GetEnvironment()));
+
+ sigset_t full_sigset;
+ sigfillset(&full_sigset);
+ const sigset_t orig_sigmask = SetSignalMask(full_sigset);
+
+ pid_t pid;
+#if defined(OS_LINUX)
+ if (options.clone_flags) {
+ // Signal handling in this function assumes the creation of a new
+ // process, so we check that a thread is not being created by mistake
+ // and that signal handling follows the process-creation rules.
+ RAW_CHECK(
+ !(options.clone_flags & (CLONE_SIGHAND | CLONE_THREAD | CLONE_VM)));
+ pid = syscall(__NR_clone, options.clone_flags, 0, 0, 0);
+ } else
+#endif
+ {
+ pid = fork();
+ }
+
+ // Always restore the original signal mask in the parent.
+ if (pid != 0) {
+ SetSignalMask(orig_sigmask);
+ }
+
+ if (pid < 0) {
+ DPLOG(ERROR) << "fork";
+ return false;
+ } else if (pid == 0) {
+ // Child process
+
+ // DANGER: fork() rule: in the child, if you don't end up doing exec*(),
+ // you call _exit() instead of exit(). This is because _exit() does not
+ // call any previously-registered (in the parent) exit handlers, which
+ // might do things like block waiting for threads that don't even exist
+ // in the child.
+
+ // If a child process uses the readline library, the process block forever.
+ // In BSD like OSes including OS X it is safe to assign /dev/null as stdin.
+ // See http://crbug.com/56596.
+ int null_fd = HANDLE_EINTR(open("/dev/null", O_RDONLY));
+ if (null_fd < 0) {
+ RAW_LOG(ERROR, "Failed to open /dev/null");
+ _exit(127);
+ }
+
+ file_util::ScopedFD null_fd_closer(&null_fd);
+ int new_fd = HANDLE_EINTR(dup2(null_fd, STDIN_FILENO));
+ if (new_fd != STDIN_FILENO) {
+ RAW_LOG(ERROR, "Failed to dup /dev/null for stdin");
+ _exit(127);
+ }
+
+ if (options.new_process_group) {
+ // Instead of inheriting the process group ID of the parent, the child
+ // starts off a new process group with pgid equal to its process ID.
+ if (setpgid(0, 0) < 0) {
+ RAW_LOG(ERROR, "setpgid failed");
+ _exit(127);
+ }
+ }
+
+ // Stop type-profiler.
+ // The profiler should be stopped between fork and exec since it inserts
+ // locks at new/delete expressions. See http://crbug.com/36678.
+ base::type_profiler::Controller::Stop();
+
+ if (options.maximize_rlimits) {
+ // Some resource limits need to be maximal in this child.
+ std::set<int>::const_iterator resource;
+ for (resource = options.maximize_rlimits->begin();
+ resource != options.maximize_rlimits->end();
+ ++resource) {
+ struct rlimit limit;
+ if (getrlimit(*resource, &limit) < 0) {
+ RAW_LOG(WARNING, "getrlimit failed");
+ } else if (limit.rlim_cur < limit.rlim_max) {
+ limit.rlim_cur = limit.rlim_max;
+ if (setrlimit(*resource, &limit) < 0) {
+ RAW_LOG(WARNING, "setrlimit failed");
+ }
+ }
+ }
+ }
+
+#if defined(OS_MACOSX)
+ RestoreDefaultExceptionHandler();
+#endif // defined(OS_MACOSX)
+
+ ResetChildSignalHandlersToDefaults();
+ SetSignalMask(orig_sigmask);
+
+#if 0
+ // When debugging it can be helpful to check that we really aren't making
+ // any hidden calls to malloc.
+ void *malloc_thunk =
+ reinterpret_cast<void*>(reinterpret_cast<intptr_t>(malloc) & ~4095);
+ mprotect(malloc_thunk, 4096, PROT_READ | PROT_WRITE | PROT_EXEC);
+ memset(reinterpret_cast<void*>(malloc), 0xff, 8);
+#endif // 0
+
+ // DANGER: no calls to malloc are allowed from now on:
+ // http://crbug.com/36678
+
+#if defined(OS_CHROMEOS)
+ if (options.ctrl_terminal_fd >= 0) {
+ // Set process' controlling terminal.
+ if (HANDLE_EINTR(setsid()) != -1) {
+ if (HANDLE_EINTR(
+ ioctl(options.ctrl_terminal_fd, TIOCSCTTY, NULL)) == -1) {
+ RAW_LOG(WARNING, "ioctl(TIOCSCTTY), ctrl terminal not set");
+ }
+ } else {
+ RAW_LOG(WARNING, "setsid failed, ctrl terminal not set");
+ }
+ }
+#endif // defined(OS_CHROMEOS)
+
+ if (options.fds_to_remap) {
+ for (FileHandleMappingVector::const_iterator
+ it = options.fds_to_remap->begin();
+ it != options.fds_to_remap->end(); ++it) {
+ fd_shuffle1.push_back(InjectionArc(it->first, it->second, false));
+ fd_shuffle2.push_back(InjectionArc(it->first, it->second, false));
+ }
+ }
+
+ if (options.environ)
+ SetEnvironment(new_environ.get());
+
+ // fd_shuffle1 is mutated by this call because it cannot malloc.
+ if (!ShuffleFileDescriptors(&fd_shuffle1))
+ _exit(127);
+
+ CloseSuperfluousFds(fd_shuffle2);
+
+ for (size_t i = 0; i < argv.size(); i++)
+ argv_cstr[i] = const_cast<char*>(argv[i].c_str());
+ argv_cstr[argv.size()] = NULL;
+ execvp(argv_cstr[0], argv_cstr.get());
+
+ RAW_LOG(ERROR, "LaunchProcess: failed to execvp:");
+ RAW_LOG(ERROR, argv_cstr[0]);
+ _exit(127);
+ } else {
+ // Parent process
+ if (options.wait) {
+ // While this isn't strictly disk IO, waiting for another process to
+ // finish is the sort of thing ThreadRestrictions is trying to prevent.
+ base::ThreadRestrictions::AssertIOAllowed();
+ pid_t ret = HANDLE_EINTR(waitpid(pid, 0, 0));
+ DPCHECK(ret > 0);
+ }
+
+ if (process_handle)
+ *process_handle = pid;
+ }
+
+ return true;
+}
+
+
+bool LaunchProcess(const CommandLine& cmdline,
+ const LaunchOptions& options,
+ ProcessHandle* process_handle) {
+ return LaunchProcess(cmdline.argv(), options, process_handle);
+}
+
+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.
+}
+
+// Return value used by GetAppOutputInternal to encapsulate the various exit
+// scenarios from the function.
+enum GetAppOutputInternalResult {
+ EXECUTE_FAILURE,
+ EXECUTE_SUCCESS,
+ GOT_MAX_OUTPUT,
+};
+
+// Executes the application specified by |argv| and wait for it to exit. Stores
+// the output (stdout) in |output|. If |do_search_path| is set, it searches the
+// path for the application; in that case, |envp| must be null, and it will use
+// the current environment. If |do_search_path| is false, |argv[0]| should fully
+// specify the path of the application, and |envp| will be used as the
+// environment. Redirects stderr to /dev/null.
+// If we successfully start the application and get all requested output, we
+// return GOT_MAX_OUTPUT, or if there is a problem starting or exiting
+// the application we return RUN_FAILURE. Otherwise we return EXECUTE_SUCCESS.
+// The GOT_MAX_OUTPUT return value exists so a caller that asks for limited
+// output can treat this as a success, despite having an exit code of SIG_PIPE
+// due to us closing the output pipe.
+// In the case of EXECUTE_SUCCESS, the application exit code will be returned
+// in |*exit_code|, which should be checked to determine if the application
+// ran successfully.
+static GetAppOutputInternalResult GetAppOutputInternal(
+ const std::vector<std::string>& argv,
+ char* const envp[],
+ std::string* output,
+ size_t max_output,
+ bool do_search_path,
+ int* exit_code) {
+ // Doing a blocking wait for another command to finish counts as IO.
+ base::ThreadRestrictions::AssertIOAllowed();
+ // exit_code must be supplied so calling function can determine success.
+ DCHECK(exit_code);
+ *exit_code = EXIT_FAILURE;
+
+ int pipe_fd[2];
+ pid_t pid;
+ InjectiveMultimap fd_shuffle1, fd_shuffle2;
+ scoped_ptr<char*[]> argv_cstr(new char*[argv.size() + 1]);
+
+ fd_shuffle1.reserve(3);
+ fd_shuffle2.reserve(3);
+
+ // Either |do_search_path| should be false or |envp| should be null, but not
+ // both.
+ DCHECK(!do_search_path ^ !envp);
+
+ if (pipe(pipe_fd) < 0)
+ return EXECUTE_FAILURE;
+
+ switch (pid = fork()) {
+ case -1: // error
+ close(pipe_fd[0]);
+ close(pipe_fd[1]);
+ return EXECUTE_FAILURE;
+ case 0: // child
+ {
+#if defined(OS_MACOSX)
+ RestoreDefaultExceptionHandler();
+#endif
+ // DANGER: no calls to malloc are allowed from now on:
+ // http://crbug.com/36678
+
+ // Obscure fork() rule: in the child, if you don't end up doing exec*(),
+ // you call _exit() instead of exit(). This is because _exit() does not
+ // call any previously-registered (in the parent) exit handlers, which
+ // might do things like block waiting for threads that don't even exist
+ // in the child.
+ int dev_null = open("/dev/null", O_WRONLY);
+ if (dev_null < 0)
+ _exit(127);
+
+ // Stop type-profiler.
+ // The profiler should be stopped between fork and exec since it inserts
+ // locks at new/delete expressions. See http://crbug.com/36678.
+ base::type_profiler::Controller::Stop();
+
+ fd_shuffle1.push_back(InjectionArc(pipe_fd[1], STDOUT_FILENO, true));
+ fd_shuffle1.push_back(InjectionArc(dev_null, STDERR_FILENO, true));
+ fd_shuffle1.push_back(InjectionArc(dev_null, STDIN_FILENO, true));
+ // Adding another element here? Remeber to increase the argument to
+ // reserve(), above.
+
+ std::copy(fd_shuffle1.begin(), fd_shuffle1.end(),
+ std::back_inserter(fd_shuffle2));
+
+ if (!ShuffleFileDescriptors(&fd_shuffle1))
+ _exit(127);
+
+ CloseSuperfluousFds(fd_shuffle2);
+
+ for (size_t i = 0; i < argv.size(); i++)
+ argv_cstr[i] = const_cast<char*>(argv[i].c_str());
+ argv_cstr[argv.size()] = NULL;
+ if (do_search_path)
+ execvp(argv_cstr[0], argv_cstr.get());
+ else
+ execve(argv_cstr[0], argv_cstr.get(), envp);
+ _exit(127);
+ }
+ default: // parent
+ {
+ // Close our writing end of pipe now. Otherwise later read would not
+ // be able to detect end of child's output (in theory we could still
+ // write to the pipe).
+ close(pipe_fd[1]);
+
+ output->clear();
+ char buffer[256];
+ size_t output_buf_left = max_output;
+ ssize_t bytes_read = 1; // A lie to properly handle |max_output == 0|
+ // case in the logic below.
+
+ while (output_buf_left > 0) {
+ bytes_read = HANDLE_EINTR(read(pipe_fd[0], buffer,
+ std::min(output_buf_left, sizeof(buffer))));
+ if (bytes_read <= 0)
+ break;
+ output->append(buffer, bytes_read);
+ output_buf_left -= static_cast<size_t>(bytes_read);
+ }
+ close(pipe_fd[0]);
+
+ // Always wait for exit code (even if we know we'll declare
+ // GOT_MAX_OUTPUT).
+ bool success = WaitForExitCode(pid, exit_code);
+
+ // If we stopped because we read as much as we wanted, we return
+ // GOT_MAX_OUTPUT (because the child may exit due to |SIGPIPE|).
+ if (!output_buf_left && bytes_read > 0)
+ return GOT_MAX_OUTPUT;
+ else if (success)
+ return EXECUTE_SUCCESS;
+ return EXECUTE_FAILURE;
+ }
+ }
+}
+
+bool GetAppOutput(const CommandLine& cl, std::string* output) {
+ return GetAppOutput(cl.argv(), output);
+}
+
+bool GetAppOutput(const std::vector<std::string>& argv, std::string* output) {
+ // Run |execve()| with the current environment and store "unlimited" data.
+ int exit_code;
+ GetAppOutputInternalResult result = GetAppOutputInternal(
+ argv, NULL, output, std::numeric_limits<std::size_t>::max(), true,
+ &exit_code);
+ return result == EXECUTE_SUCCESS && exit_code == EXIT_SUCCESS;
+}
+
+// TODO(viettrungluu): Conceivably, we should have a timeout as well, so we
+// don't hang if what we're calling hangs.
+bool GetAppOutputRestricted(const CommandLine& cl,
+ std::string* output, size_t max_output) {
+ // Run |execve()| with the empty environment.
+ char* const empty_environ = NULL;
+ int exit_code;
+ GetAppOutputInternalResult result = GetAppOutputInternal(
+ cl.argv(), &empty_environ, output, max_output, false, &exit_code);
+ return result == GOT_MAX_OUTPUT || (result == EXECUTE_SUCCESS &&
+ exit_code == EXIT_SUCCESS);
+}
+
+bool GetAppOutputWithExitCode(const CommandLine& cl,
+ std::string* output,
+ int* exit_code) {
+ // Run |execve()| with the current environment and store "unlimited" data.
+ GetAppOutputInternalResult result = GetAppOutputInternal(
+ cl.argv(), NULL, output, std::numeric_limits<std::size_t>::max(), true,
+ exit_code);
+ return result == EXECUTE_SUCCESS;
+}
+
+} // namespace base
diff --git a/chromium/base/process/launch_win.cc b/chromium/base/process/launch_win.cc
new file mode 100644
index 00000000000..c5126c56fb8
--- /dev/null
+++ b/chromium/base/process/launch_win.cc
@@ -0,0 +1,283 @@
+// 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/launch.h"
+
+#include <fcntl.h>
+#include <io.h>
+#include <windows.h>
+#include <userenv.h>
+#include <psapi.h>
+
+#include <ios>
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/command_line.h"
+#include "base/debug/stack_trace.h"
+#include "base/logging.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/message_loop/message_loop.h"
+#include "base/metrics/histogram.h"
+#include "base/process/kill.h"
+#include "base/sys_info.h"
+#include "base/win/object_watcher.h"
+#include "base/win/scoped_handle.h"
+#include "base/win/scoped_process_information.h"
+#include "base/win/windows_version.h"
+
+// userenv.dll is required for CreateEnvironmentBlock().
+#pragma comment(lib, "userenv.lib")
+
+namespace base {
+
+namespace {
+
+// This exit code is used by the Windows task manager when it kills a
+// process. It's value is obviously not that unique, and it's
+// surprising to me that the task manager uses this value, but it
+// seems to be common practice on Windows to test for it as an
+// indication that the task manager has killed something if the
+// process goes away.
+const DWORD kProcessKilledExitCode = 1;
+
+} // namespace
+
+void RouteStdioToConsole() {
+ // Don't change anything if stdout or stderr already point to a
+ // valid stream.
+ //
+ // If we are running under Buildbot or under Cygwin's default
+ // terminal (mintty), stderr and stderr will be pipe handles. In
+ // that case, we don't want to open CONOUT$, because its output
+ // likely does not go anywhere.
+ //
+ // We don't use GetStdHandle() to check stdout/stderr here because
+ // it can return dangling IDs of handles that were never inherited
+ // by this process. These IDs could have been reused by the time
+ // this function is called. The CRT checks the validity of
+ // stdout/stderr on startup (before the handle IDs can be reused).
+ // _fileno(stdout) will return -2 (_NO_CONSOLE_FILENO) if stdout was
+ // invalid.
+ if (_fileno(stdout) >= 0 || _fileno(stderr) >= 0)
+ return;
+
+ if (!AttachConsole(ATTACH_PARENT_PROCESS)) {
+ unsigned int result = GetLastError();
+ // Was probably already attached.
+ if (result == ERROR_ACCESS_DENIED)
+ return;
+ // Don't bother creating a new console for each child process if the
+ // parent process is invalid (eg: crashed).
+ if (result == ERROR_GEN_FAILURE)
+ return;
+ // Make a new console if attaching to parent fails with any other error.
+ // It should be ERROR_INVALID_HANDLE at this point, which means the browser
+ // was likely not started from a console.
+ AllocConsole();
+ }
+
+ // Arbitrary byte count to use when buffering output lines. More
+ // means potential waste, less means more risk of interleaved
+ // log-lines in output.
+ enum { kOutputBufferSize = 64 * 1024 };
+
+ if (freopen("CONOUT$", "w", stdout)) {
+ setvbuf(stdout, NULL, _IOLBF, kOutputBufferSize);
+ // Overwrite FD 1 for the benefit of any code that uses this FD
+ // directly. This is safe because the CRT allocates FDs 0, 1 and
+ // 2 at startup even if they don't have valid underlying Windows
+ // handles. This means we won't be overwriting an FD created by
+ // _open() after startup.
+ _dup2(_fileno(stdout), 1);
+ }
+ if (freopen("CONOUT$", "w", stderr)) {
+ setvbuf(stderr, NULL, _IOLBF, kOutputBufferSize);
+ _dup2(_fileno(stderr), 2);
+ }
+
+ // Fix all cout, wcout, cin, wcin, cerr, wcerr, clog and wclog.
+ std::ios::sync_with_stdio();
+}
+
+bool LaunchProcess(const string16& cmdline,
+ const LaunchOptions& options,
+ ProcessHandle* process_handle) {
+ STARTUPINFO startup_info = {};
+ startup_info.cb = sizeof(startup_info);
+ if (options.empty_desktop_name)
+ startup_info.lpDesktop = L"";
+ startup_info.dwFlags = STARTF_USESHOWWINDOW;
+ startup_info.wShowWindow = options.start_hidden ? SW_HIDE : SW_SHOW;
+
+ if (options.stdin_handle || options.stdout_handle || options.stderr_handle) {
+ DCHECK(options.inherit_handles);
+ DCHECK(options.stdin_handle);
+ DCHECK(options.stdout_handle);
+ DCHECK(options.stderr_handle);
+ startup_info.dwFlags |= STARTF_USESTDHANDLES;
+ startup_info.hStdInput = options.stdin_handle;
+ startup_info.hStdOutput = options.stdout_handle;
+ startup_info.hStdError = options.stderr_handle;
+ }
+
+ DWORD flags = 0;
+
+ if (options.job_handle) {
+ flags |= CREATE_SUSPENDED;
+
+ // If this code is run under a debugger, the launched process is
+ // automatically associated with a job object created by the debugger.
+ // The CREATE_BREAKAWAY_FROM_JOB flag is used to prevent this.
+ flags |= CREATE_BREAKAWAY_FROM_JOB;
+ }
+
+ if (options.force_breakaway_from_job_)
+ flags |= CREATE_BREAKAWAY_FROM_JOB;
+
+ base::win::ScopedProcessInformation process_info;
+
+ if (options.as_user) {
+ flags |= CREATE_UNICODE_ENVIRONMENT;
+ void* enviroment_block = NULL;
+
+ if (!CreateEnvironmentBlock(&enviroment_block, options.as_user, FALSE)) {
+ DPLOG(ERROR);
+ return false;
+ }
+
+ BOOL launched =
+ CreateProcessAsUser(options.as_user, NULL,
+ const_cast<wchar_t*>(cmdline.c_str()),
+ NULL, NULL, options.inherit_handles, flags,
+ enviroment_block, NULL, &startup_info,
+ process_info.Receive());
+ DestroyEnvironmentBlock(enviroment_block);
+ if (!launched) {
+ DPLOG(ERROR);
+ return false;
+ }
+ } else {
+ if (!CreateProcess(NULL,
+ const_cast<wchar_t*>(cmdline.c_str()), NULL, NULL,
+ options.inherit_handles, flags, NULL, NULL,
+ &startup_info, process_info.Receive())) {
+ DPLOG(ERROR);
+ return false;
+ }
+ }
+
+ if (options.job_handle) {
+ if (0 == AssignProcessToJobObject(options.job_handle,
+ process_info.process_handle())) {
+ DLOG(ERROR) << "Could not AssignProcessToObject.";
+ KillProcess(process_info.process_handle(), kProcessKilledExitCode, true);
+ return false;
+ }
+
+ ResumeThread(process_info.thread_handle());
+ }
+
+ if (options.wait)
+ WaitForSingleObject(process_info.process_handle(), INFINITE);
+
+ // If the caller wants the process handle, we won't close it.
+ if (process_handle)
+ *process_handle = process_info.TakeProcessHandle();
+
+ return true;
+}
+
+bool LaunchProcess(const CommandLine& cmdline,
+ const LaunchOptions& options,
+ ProcessHandle* process_handle) {
+ return LaunchProcess(cmdline.GetCommandLineString(), options, process_handle);
+}
+
+bool SetJobObjectAsKillOnJobClose(HANDLE job_object) {
+ JOBOBJECT_EXTENDED_LIMIT_INFORMATION limit_info = {0};
+ limit_info.BasicLimitInformation.LimitFlags =
+ JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE;
+ return 0 != SetInformationJobObject(
+ job_object,
+ JobObjectExtendedLimitInformation,
+ &limit_info,
+ sizeof(limit_info));
+}
+
+bool GetAppOutput(const CommandLine& cl, std::string* output) {
+ HANDLE out_read = NULL;
+ HANDLE out_write = NULL;
+
+ SECURITY_ATTRIBUTES sa_attr;
+ // Set the bInheritHandle flag so pipe handles are inherited.
+ sa_attr.nLength = sizeof(SECURITY_ATTRIBUTES);
+ sa_attr.bInheritHandle = TRUE;
+ sa_attr.lpSecurityDescriptor = NULL;
+
+ // Create the pipe for the child process's STDOUT.
+ if (!CreatePipe(&out_read, &out_write, &sa_attr, 0)) {
+ NOTREACHED() << "Failed to create pipe";
+ return false;
+ }
+
+ // Ensure we don't leak the handles.
+ win::ScopedHandle scoped_out_read(out_read);
+ win::ScopedHandle scoped_out_write(out_write);
+
+ // Ensure the read handle to the pipe for STDOUT is not inherited.
+ if (!SetHandleInformation(out_read, HANDLE_FLAG_INHERIT, 0)) {
+ NOTREACHED() << "Failed to disabled pipe inheritance";
+ return false;
+ }
+
+ FilePath::StringType writable_command_line_string(cl.GetCommandLineString());
+
+ base::win::ScopedProcessInformation proc_info;
+ STARTUPINFO start_info = { 0 };
+
+ start_info.cb = sizeof(STARTUPINFO);
+ start_info.hStdOutput = out_write;
+ // Keep the normal stdin and stderr.
+ start_info.hStdInput = GetStdHandle(STD_INPUT_HANDLE);
+ start_info.hStdError = GetStdHandle(STD_ERROR_HANDLE);
+ start_info.dwFlags |= STARTF_USESTDHANDLES;
+
+ // Create the child process.
+ if (!CreateProcess(NULL,
+ &writable_command_line_string[0],
+ NULL, NULL,
+ TRUE, // Handles are inherited.
+ 0, NULL, NULL, &start_info, proc_info.Receive())) {
+ NOTREACHED() << "Failed to start process";
+ return false;
+ }
+
+ // Close our writing end of pipe now. Otherwise later read would not be able
+ // to detect end of child's output.
+ scoped_out_write.Close();
+
+ // Read output from the child process's pipe for STDOUT
+ const int kBufferSize = 1024;
+ char buffer[kBufferSize];
+
+ for (;;) {
+ DWORD bytes_read = 0;
+ BOOL success = ReadFile(out_read, buffer, kBufferSize, &bytes_read, NULL);
+ if (!success || bytes_read == 0)
+ break;
+ output->append(buffer, bytes_read);
+ }
+
+ // Let's wait for the process to finish.
+ WaitForSingleObject(proc_info.process_handle(), INFINITE);
+
+ return true;
+}
+
+void RaiseProcessToHighPriority() {
+ SetPriorityClass(GetCurrentProcess(), HIGH_PRIORITY_CLASS);
+}
+
+} // namespace base
diff --git a/chromium/base/process/memory.h b/chromium/base/process/memory.h
new file mode 100644
index 00000000000..de79477e95d
--- /dev/null
+++ b/chromium/base/process/memory.h
@@ -0,0 +1,69 @@
+// 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.
+
+#ifndef BASE_PROCESS_MEMORY_H_
+#define BASE_PROCESS_MEMORY_H_
+
+#include "base/base_export.h"
+#include "base/basictypes.h"
+#include "base/process/process_handle.h"
+#include "build/build_config.h"
+
+#if defined(OS_WIN)
+#include <windows.h>
+#endif
+
+namespace base {
+
+// 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
+// better to call this function early in initialization and again before
+// entering the main loop.
+// Note: Returns true on Windows 2000 without doing anything.
+BASE_EXPORT bool EnableLowFragmentationHeap();
+
+// Enables 'terminate on heap corruption' flag. Helps protect against heap
+// overflow. Has no effect if the OS doesn't provide the necessary facility.
+BASE_EXPORT void EnableTerminationOnHeapCorruption();
+
+// Turns on process termination if memory runs out.
+BASE_EXPORT void EnableTerminationOnOutOfMemory();
+
+#if defined(OS_WIN)
+// Returns the module handle to which an address belongs. The reference count
+// of the module is not incremented.
+BASE_EXPORT HMODULE GetModuleFromAddress(void* address);
+#endif
+
+#if defined(OS_LINUX) || defined(OS_ANDROID)
+BASE_EXPORT extern size_t g_oom_size;
+
+// The maximum allowed value for the OOM score.
+const int kMaxOomScore = 1000;
+
+// This adjusts /proc/<pid>/oom_score_adj so the Linux OOM killer will
+// prefer to kill certain process types over others. The range for the
+// adjustment is [-1000, 1000], with [0, 1000] being user accessible.
+// If the Linux system doesn't support the newer oom_score_adj range
+// of [0, 1000], then we revert to using the older oom_adj, and
+// translate the given value into [0, 15]. Some aliasing of values
+// may occur in that case, of course.
+BASE_EXPORT bool AdjustOOMScore(ProcessId process, int score);
+#endif
+
+#if defined(OS_MACOSX)
+// Very large images or svg canvases can cause huge mallocs. Skia
+// does tricks on tcmalloc-based systems to allow malloc to fail with
+// a NULL rather than hit the oom crasher. This replicates that for
+// OSX.
+//
+// IF YOU USE THIS WITHOUT CONSULTING YOUR FRIENDLY OSX DEVELOPER,
+// YOUR CODE IS LIKELY TO BE REVERTED. THANK YOU.
+BASE_EXPORT void* UncheckedMalloc(size_t size);
+#endif // defined(OS_MACOSX)
+
+} // namespace base
+
+#endif // BASE_PROCESS_MEMORY_H_
diff --git a/chromium/base/process/memory_linux.cc b/chromium/base/process/memory_linux.cc
new file mode 100644
index 00000000000..f81429b2ac0
--- /dev/null
+++ b/chromium/base/process/memory_linux.cc
@@ -0,0 +1,183 @@
+// 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/memory.h"
+
+#include <new>
+
+#include "base/file_util.h"
+#include "base/files/file_path.h"
+#include "base/logging.h"
+#include "base/process/internal_linux.h"
+#include "base/strings/string_number_conversions.h"
+
+namespace base {
+
+size_t g_oom_size = 0U;
+
+namespace {
+
+void OnNoMemorySize(size_t size) {
+ g_oom_size = size;
+
+ if (size != 0)
+ LOG(FATAL) << "Out of memory, size = " << size;
+ LOG(FATAL) << "Out of memory.";
+}
+
+void OnNoMemory() {
+ OnNoMemorySize(0);
+}
+
+} // namespace
+
+#if !defined(ADDRESS_SANITIZER) && !defined(MEMORY_SANITIZER) && \
+ !defined(THREAD_SANITIZER) && !defined(LEAK_SANITIZER)
+
+#if defined(LIBC_GLIBC) && !defined(USE_TCMALLOC)
+
+extern "C" {
+void* __libc_malloc(size_t size);
+void* __libc_realloc(void* ptr, size_t size);
+void* __libc_calloc(size_t nmemb, size_t size);
+void* __libc_valloc(size_t size);
+void* __libc_pvalloc(size_t size);
+void* __libc_memalign(size_t alignment, size_t size);
+
+// Overriding the system memory allocation functions:
+//
+// For security reasons, we want malloc failures to be fatal. Too much code
+// doesn't check for a NULL return value from malloc and unconditionally uses
+// the resulting pointer. If the first offset that they try to access is
+// attacker controlled, then the attacker can direct the code to access any
+// part of memory.
+//
+// Thus, we define all the standard malloc functions here and mark them as
+// visibility 'default'. This means that they replace the malloc functions for
+// all Chromium code and also for all code in shared libraries. There are tests
+// for this in process_util_unittest.cc.
+//
+// If we are using tcmalloc, then the problem is moot since tcmalloc handles
+// this for us. Thus this code is in a !defined(USE_TCMALLOC) block.
+//
+// If we are testing the binary with AddressSanitizer, we should not
+// redefine malloc and let AddressSanitizer do it instead.
+//
+// We call the real libc functions in this code by using __libc_malloc etc.
+// Previously we tried using dlsym(RTLD_NEXT, ...) but that failed depending on
+// the link order. Since ld.so needs calloc during symbol resolution, it
+// defines its own versions of several of these functions in dl-minimal.c.
+// Depending on the runtime library order, dlsym ended up giving us those
+// functions and bad things happened. See crbug.com/31809
+//
+// This means that any code which calls __libc_* gets the raw libc versions of
+// these functions.
+
+#define DIE_ON_OOM_1(function_name) \
+ void* function_name(size_t) __attribute__ ((visibility("default"))); \
+ \
+ void* function_name(size_t size) { \
+ void* ret = __libc_##function_name(size); \
+ if (ret == NULL && size != 0) \
+ OnNoMemorySize(size); \
+ return ret; \
+ }
+
+#define DIE_ON_OOM_2(function_name, arg1_type) \
+ void* function_name(arg1_type, size_t) \
+ __attribute__ ((visibility("default"))); \
+ \
+ void* function_name(arg1_type arg1, size_t size) { \
+ void* ret = __libc_##function_name(arg1, size); \
+ if (ret == NULL && size != 0) \
+ OnNoMemorySize(size); \
+ return ret; \
+ }
+
+DIE_ON_OOM_1(malloc)
+DIE_ON_OOM_1(valloc)
+DIE_ON_OOM_1(pvalloc)
+
+DIE_ON_OOM_2(calloc, size_t)
+DIE_ON_OOM_2(realloc, void*)
+DIE_ON_OOM_2(memalign, size_t)
+
+// posix_memalign has a unique signature and doesn't have a __libc_ variant.
+int posix_memalign(void** ptr, size_t alignment, size_t size)
+ __attribute__ ((visibility("default")));
+
+int posix_memalign(void** ptr, size_t alignment, size_t size) {
+ // This will use the safe version of memalign, above.
+ *ptr = memalign(alignment, size);
+ return 0;
+}
+
+} // extern C
+
+#else
+
+// TODO(mostynb@opera.com): dlsym dance
+
+#endif // LIBC_GLIBC && !USE_TCMALLOC
+
+#endif // !*_SANITIZER
+
+void EnableTerminationOnHeapCorruption() {
+ // On Linux, there nothing to do AFAIK.
+}
+
+void EnableTerminationOnOutOfMemory() {
+#if defined(OS_ANDROID)
+ // Android doesn't support setting a new handler.
+ DLOG(WARNING) << "Not feasible.";
+#else
+ // Set the new-out of memory handler.
+ std::set_new_handler(&OnNoMemory);
+ // If we're using glibc's allocator, the above functions will override
+ // malloc and friends and make them die on out of memory.
+#endif
+}
+
+// NOTE: This is not the only version of this function in the source:
+// the setuid sandbox (in process_util_linux.c, in the sandbox source)
+// also has its own C version.
+bool AdjustOOMScore(ProcessId process, int score) {
+ if (score < 0 || score > kMaxOomScore)
+ return false;
+
+ 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");
+ if (PathExists(oom_file)) {
+ std::string score_str = IntToString(score);
+ DVLOG(1) << "Adjusting oom_score_adj of " << process << " to "
+ << score_str;
+ int score_len = static_cast<int>(score_str.length());
+ return (score_len == file_util::WriteFile(oom_file,
+ score_str.c_str(),
+ score_len));
+ }
+
+ // If the oom_score_adj file doesn't exist, then we write the old
+ // style file and translate the oom_adj score to the range 0-15.
+ oom_file = oom_path.AppendASCII("oom_adj");
+ if (PathExists(oom_file)) {
+ // Max score for the old oom_adj range. Used for conversion of new
+ // values to old values.
+ const int kMaxOldOomScore = 15;
+
+ int converted_score = score * kMaxOldOomScore / kMaxOomScore;
+ std::string score_str = IntToString(converted_score);
+ DVLOG(1) << "Adjusting oom_adj of " << process << " to " << score_str;
+ int score_len = static_cast<int>(score_str.length());
+ return (score_len == file_util::WriteFile(oom_file,
+ score_str.c_str(),
+ score_len));
+ }
+
+ return false;
+}
+
+} // namespace base
diff --git a/chromium/base/process/memory_mac.mm b/chromium/base/process/memory_mac.mm
new file mode 100644
index 00000000000..dd30e704c8f
--- /dev/null
+++ b/chromium/base/process/memory_mac.mm
@@ -0,0 +1,704 @@
+// 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/memory.h"
+
+#include <CoreFoundation/CoreFoundation.h>
+#include <errno.h>
+#include <mach/mach.h>
+#include <mach/mach_vm.h>
+#include <malloc/malloc.h>
+#import <objc/runtime.h>
+
+#include <new>
+
+#include "base/lazy_instance.h"
+#include "base/logging.h"
+#include "base/mac/mac_util.h"
+#include "base/scoped_clear_errno.h"
+#include "third_party/apple_apsl/CFBase.h"
+#include "third_party/apple_apsl/malloc.h"
+
+#if ARCH_CPU_32_BITS
+#include <dlfcn.h>
+#include <mach-o/nlist.h>
+
+#include "base/threading/thread_local.h"
+#include "third_party/mach_override/mach_override.h"
+#endif // ARCH_CPU_32_BITS
+
+namespace base {
+
+// These are helpers for EnableTerminationOnHeapCorruption, which is a no-op
+// on 64 bit Macs.
+#if ARCH_CPU_32_BITS
+namespace {
+
+// Finds the library path for malloc() and thus the libC part of libSystem,
+// which in Lion is in a separate image.
+const char* LookUpLibCPath() {
+ const void* addr = reinterpret_cast<void*>(&malloc);
+
+ Dl_info info;
+ if (dladdr(addr, &info))
+ return info.dli_fname;
+
+ DLOG(WARNING) << "Could not find image path for malloc()";
+ return NULL;
+}
+
+typedef void(*malloc_error_break_t)(void);
+malloc_error_break_t g_original_malloc_error_break = NULL;
+
+// Returns the function pointer for malloc_error_break. This symbol is declared
+// as __private_extern__ and cannot be dlsym()ed. Instead, use nlist() to
+// get it.
+malloc_error_break_t LookUpMallocErrorBreak() {
+ const char* lib_c_path = LookUpLibCPath();
+ if (!lib_c_path)
+ return NULL;
+
+ // Only need to look up two symbols, but nlist() requires a NULL-terminated
+ // array and takes no count.
+ struct nlist nl[3];
+ bzero(&nl, sizeof(nl));
+
+ // The symbol to find.
+ nl[0].n_un.n_name = const_cast<char*>("_malloc_error_break");
+
+ // A reference symbol by which the address of the desired symbol will be
+ // calculated.
+ nl[1].n_un.n_name = const_cast<char*>("_malloc");
+
+ int rv = nlist(lib_c_path, nl);
+ if (rv != 0 || nl[0].n_type == N_UNDF || nl[1].n_type == N_UNDF) {
+ return NULL;
+ }
+
+ // nlist() returns addresses as offsets in the image, not the instruction
+ // pointer in memory. Use the known in-memory address of malloc()
+ // to compute the offset for malloc_error_break().
+ uintptr_t reference_addr = reinterpret_cast<uintptr_t>(&malloc);
+ reference_addr -= nl[1].n_value;
+ reference_addr += nl[0].n_value;
+
+ return reinterpret_cast<malloc_error_break_t>(reference_addr);
+}
+
+// Combines ThreadLocalBoolean with AutoReset. It would be convenient
+// to compose ThreadLocalPointer<bool> with base::AutoReset<bool>, but that
+// would require allocating some storage for the bool.
+class ThreadLocalBooleanAutoReset {
+ public:
+ ThreadLocalBooleanAutoReset(ThreadLocalBoolean* tlb, bool new_value)
+ : scoped_tlb_(tlb),
+ original_value_(tlb->Get()) {
+ scoped_tlb_->Set(new_value);
+ }
+ ~ThreadLocalBooleanAutoReset() {
+ scoped_tlb_->Set(original_value_);
+ }
+
+ private:
+ ThreadLocalBoolean* scoped_tlb_;
+ bool original_value_;
+
+ DISALLOW_COPY_AND_ASSIGN(ThreadLocalBooleanAutoReset);
+};
+
+base::LazyInstance<ThreadLocalBoolean>::Leaky
+ g_unchecked_malloc = LAZY_INSTANCE_INITIALIZER;
+
+// NOTE(shess): This is called when the malloc library noticed that the heap
+// is fubar. Avoid calls which will re-enter the malloc library.
+void CrMallocErrorBreak() {
+ g_original_malloc_error_break();
+
+ // Out of memory is certainly not heap corruption, and not necessarily
+ // something for which the process should be terminated. Leave that decision
+ // to the OOM killer. The EBADF case comes up because the malloc library
+ // attempts to log to ASL (syslog) before calling this code, which fails
+ // accessing a Unix-domain socket because of sandboxing.
+ if (errno == ENOMEM || (errno == EBADF && g_unchecked_malloc.Get().Get()))
+ return;
+
+ // A unit test checks this error message, so it needs to be in release builds.
+ char buf[1024] =
+ "Terminating process due to a potential for future heap corruption: "
+ "errno=";
+ char errnobuf[] = {
+ '0' + ((errno / 100) % 10),
+ '0' + ((errno / 10) % 10),
+ '0' + (errno % 10),
+ '\000'
+ };
+ COMPILE_ASSERT(ELAST <= 999, errno_too_large_to_encode);
+ strlcat(buf, errnobuf, sizeof(buf));
+ RAW_LOG(ERROR, buf);
+
+ // Crash by writing to NULL+errno to allow analyzing errno from
+ // crash dump info (setting a breakpad key would re-enter the malloc
+ // library). Max documented errno in intro(2) is actually 102, but
+ // it really just needs to be "small" to stay on the right vm page.
+ const int kMaxErrno = 256;
+ char* volatile death_ptr = NULL;
+ death_ptr += std::min(errno, kMaxErrno);
+ *death_ptr = '!';
+}
+
+} // namespace
+#endif // ARCH_CPU_32_BITS
+
+void EnableTerminationOnHeapCorruption() {
+#if defined(ADDRESS_SANITIZER) || ARCH_CPU_64_BITS
+ // AddressSanitizer handles heap corruption, and on 64 bit Macs, the malloc
+ // system automatically abort()s on heap corruption.
+ return;
+#else
+ // Only override once, otherwise CrMallocErrorBreak() will recurse
+ // to itself.
+ if (g_original_malloc_error_break)
+ return;
+
+ malloc_error_break_t malloc_error_break = LookUpMallocErrorBreak();
+ if (!malloc_error_break) {
+ DLOG(WARNING) << "Could not find malloc_error_break";
+ return;
+ }
+
+ mach_error_t err = mach_override_ptr(
+ (void*)malloc_error_break,
+ (void*)&CrMallocErrorBreak,
+ (void**)&g_original_malloc_error_break);
+
+ if (err != err_none)
+ DLOG(WARNING) << "Could not override malloc_error_break; error = " << err;
+#endif // defined(ADDRESS_SANITIZER) || ARCH_CPU_64_BITS
+}
+
+// ------------------------------------------------------------------------
+
+namespace {
+
+bool g_oom_killer_enabled;
+
+// Starting with Mac OS X 10.7, the zone allocators set up by the system are
+// read-only, to prevent them from being overwritten in an attack. However,
+// blindly unprotecting and reprotecting the zone allocators fails with
+// GuardMalloc because GuardMalloc sets up its zone allocator using a block of
+// memory in its bss. Explicit saving/restoring of the protection is required.
+//
+// This function takes a pointer to a malloc zone, de-protects it if necessary,
+// and returns (in the out parameters) a region of memory (if any) to be
+// re-protected when modifications are complete. This approach assumes that
+// there is no contention for the protection of this memory.
+void DeprotectMallocZone(ChromeMallocZone* default_zone,
+ mach_vm_address_t* reprotection_start,
+ mach_vm_size_t* reprotection_length,
+ vm_prot_t* reprotection_value) {
+ mach_port_t unused;
+ *reprotection_start = reinterpret_cast<mach_vm_address_t>(default_zone);
+ struct vm_region_basic_info_64 info;
+ mach_msg_type_number_t count = VM_REGION_BASIC_INFO_COUNT_64;
+ kern_return_t result =
+ mach_vm_region(mach_task_self(),
+ reprotection_start,
+ reprotection_length,
+ VM_REGION_BASIC_INFO_64,
+ reinterpret_cast<vm_region_info_t>(&info),
+ &count,
+ &unused);
+ CHECK(result == KERN_SUCCESS);
+
+ result = mach_port_deallocate(mach_task_self(), unused);
+ CHECK(result == KERN_SUCCESS);
+
+ // Does the region fully enclose the zone pointers? Possibly unwarranted
+ // simplification used: using the size of a full version 8 malloc zone rather
+ // than the actual smaller size if the passed-in zone is not version 8.
+ CHECK(*reprotection_start <=
+ reinterpret_cast<mach_vm_address_t>(default_zone));
+ mach_vm_size_t zone_offset = reinterpret_cast<mach_vm_size_t>(default_zone) -
+ reinterpret_cast<mach_vm_size_t>(*reprotection_start);
+ CHECK(zone_offset + sizeof(ChromeMallocZone) <= *reprotection_length);
+
+ if (info.protection & VM_PROT_WRITE) {
+ // No change needed; the zone is already writable.
+ *reprotection_start = 0;
+ *reprotection_length = 0;
+ *reprotection_value = VM_PROT_NONE;
+ } else {
+ *reprotection_value = info.protection;
+ result = mach_vm_protect(mach_task_self(),
+ *reprotection_start,
+ *reprotection_length,
+ false,
+ info.protection | VM_PROT_WRITE);
+ CHECK(result == KERN_SUCCESS);
+ }
+}
+
+// === C malloc/calloc/valloc/realloc/posix_memalign ===
+
+typedef void* (*malloc_type)(struct _malloc_zone_t* zone,
+ size_t size);
+typedef void* (*calloc_type)(struct _malloc_zone_t* zone,
+ size_t num_items,
+ size_t size);
+typedef void* (*valloc_type)(struct _malloc_zone_t* zone,
+ size_t size);
+typedef void (*free_type)(struct _malloc_zone_t* zone,
+ void* ptr);
+typedef void* (*realloc_type)(struct _malloc_zone_t* zone,
+ void* ptr,
+ size_t size);
+typedef void* (*memalign_type)(struct _malloc_zone_t* zone,
+ size_t alignment,
+ size_t size);
+
+malloc_type g_old_malloc;
+calloc_type g_old_calloc;
+valloc_type g_old_valloc;
+free_type g_old_free;
+realloc_type g_old_realloc;
+memalign_type g_old_memalign;
+
+malloc_type g_old_malloc_purgeable;
+calloc_type g_old_calloc_purgeable;
+valloc_type g_old_valloc_purgeable;
+free_type g_old_free_purgeable;
+realloc_type g_old_realloc_purgeable;
+memalign_type g_old_memalign_purgeable;
+
+void* oom_killer_malloc(struct _malloc_zone_t* zone,
+ size_t size) {
+#if ARCH_CPU_32_BITS
+ ScopedClearErrno clear_errno;
+#endif // ARCH_CPU_32_BITS
+ void* result = g_old_malloc(zone, size);
+ if (!result && size)
+ debug::BreakDebugger();
+ return result;
+}
+
+void* oom_killer_calloc(struct _malloc_zone_t* zone,
+ size_t num_items,
+ size_t size) {
+#if ARCH_CPU_32_BITS
+ ScopedClearErrno clear_errno;
+#endif // ARCH_CPU_32_BITS
+ void* result = g_old_calloc(zone, num_items, size);
+ if (!result && num_items && size)
+ debug::BreakDebugger();
+ return result;
+}
+
+void* oom_killer_valloc(struct _malloc_zone_t* zone,
+ size_t size) {
+#if ARCH_CPU_32_BITS
+ ScopedClearErrno clear_errno;
+#endif // ARCH_CPU_32_BITS
+ void* result = g_old_valloc(zone, size);
+ if (!result && size)
+ debug::BreakDebugger();
+ return result;
+}
+
+void oom_killer_free(struct _malloc_zone_t* zone,
+ void* ptr) {
+#if ARCH_CPU_32_BITS
+ ScopedClearErrno clear_errno;
+#endif // ARCH_CPU_32_BITS
+ g_old_free(zone, ptr);
+}
+
+void* oom_killer_realloc(struct _malloc_zone_t* zone,
+ void* ptr,
+ size_t size) {
+#if ARCH_CPU_32_BITS
+ ScopedClearErrno clear_errno;
+#endif // ARCH_CPU_32_BITS
+ void* result = g_old_realloc(zone, ptr, size);
+ if (!result && size)
+ debug::BreakDebugger();
+ return result;
+}
+
+void* oom_killer_memalign(struct _malloc_zone_t* zone,
+ size_t alignment,
+ size_t size) {
+#if ARCH_CPU_32_BITS
+ ScopedClearErrno clear_errno;
+#endif // ARCH_CPU_32_BITS
+ void* result = g_old_memalign(zone, alignment, size);
+ // Only die if posix_memalign would have returned ENOMEM, since there are
+ // other reasons why NULL might be returned (see
+ // http://opensource.apple.com/source/Libc/Libc-583/gen/malloc.c ).
+ if (!result && size && alignment >= sizeof(void*)
+ && (alignment & (alignment - 1)) == 0) {
+ debug::BreakDebugger();
+ }
+ return result;
+}
+
+void* oom_killer_malloc_purgeable(struct _malloc_zone_t* zone,
+ size_t size) {
+#if ARCH_CPU_32_BITS
+ ScopedClearErrno clear_errno;
+#endif // ARCH_CPU_32_BITS
+ void* result = g_old_malloc_purgeable(zone, size);
+ if (!result && size)
+ debug::BreakDebugger();
+ return result;
+}
+
+void* oom_killer_calloc_purgeable(struct _malloc_zone_t* zone,
+ size_t num_items,
+ size_t size) {
+#if ARCH_CPU_32_BITS
+ ScopedClearErrno clear_errno;
+#endif // ARCH_CPU_32_BITS
+ void* result = g_old_calloc_purgeable(zone, num_items, size);
+ if (!result && num_items && size)
+ debug::BreakDebugger();
+ return result;
+}
+
+void* oom_killer_valloc_purgeable(struct _malloc_zone_t* zone,
+ size_t size) {
+#if ARCH_CPU_32_BITS
+ ScopedClearErrno clear_errno;
+#endif // ARCH_CPU_32_BITS
+ void* result = g_old_valloc_purgeable(zone, size);
+ if (!result && size)
+ debug::BreakDebugger();
+ return result;
+}
+
+void oom_killer_free_purgeable(struct _malloc_zone_t* zone,
+ void* ptr) {
+#if ARCH_CPU_32_BITS
+ ScopedClearErrno clear_errno;
+#endif // ARCH_CPU_32_BITS
+ g_old_free_purgeable(zone, ptr);
+}
+
+void* oom_killer_realloc_purgeable(struct _malloc_zone_t* zone,
+ void* ptr,
+ size_t size) {
+#if ARCH_CPU_32_BITS
+ ScopedClearErrno clear_errno;
+#endif // ARCH_CPU_32_BITS
+ void* result = g_old_realloc_purgeable(zone, ptr, size);
+ if (!result && size)
+ debug::BreakDebugger();
+ return result;
+}
+
+void* oom_killer_memalign_purgeable(struct _malloc_zone_t* zone,
+ size_t alignment,
+ size_t size) {
+#if ARCH_CPU_32_BITS
+ ScopedClearErrno clear_errno;
+#endif // ARCH_CPU_32_BITS
+ void* result = g_old_memalign_purgeable(zone, alignment, size);
+ // Only die if posix_memalign would have returned ENOMEM, since there are
+ // other reasons why NULL might be returned (see
+ // http://opensource.apple.com/source/Libc/Libc-583/gen/malloc.c ).
+ if (!result && size && alignment >= sizeof(void*)
+ && (alignment & (alignment - 1)) == 0) {
+ debug::BreakDebugger();
+ }
+ return result;
+}
+
+// === C++ operator new ===
+
+void oom_killer_new() {
+ debug::BreakDebugger();
+}
+
+// === Core Foundation CFAllocators ===
+
+bool CanGetContextForCFAllocator() {
+ return !base::mac::IsOSLaterThanMountainLion_DontCallThis();
+}
+
+CFAllocatorContext* ContextForCFAllocator(CFAllocatorRef allocator) {
+ if (base::mac::IsOSSnowLeopard()) {
+ ChromeCFAllocatorLeopards* our_allocator =
+ const_cast<ChromeCFAllocatorLeopards*>(
+ reinterpret_cast<const ChromeCFAllocatorLeopards*>(allocator));
+ return &our_allocator->_context;
+ } else if (base::mac::IsOSLion() || base::mac::IsOSMountainLion()) {
+ ChromeCFAllocatorLions* our_allocator =
+ const_cast<ChromeCFAllocatorLions*>(
+ reinterpret_cast<const ChromeCFAllocatorLions*>(allocator));
+ return &our_allocator->_context;
+ } else {
+ return NULL;
+ }
+}
+
+CFAllocatorAllocateCallBack g_old_cfallocator_system_default;
+CFAllocatorAllocateCallBack g_old_cfallocator_malloc;
+CFAllocatorAllocateCallBack g_old_cfallocator_malloc_zone;
+
+void* oom_killer_cfallocator_system_default(CFIndex alloc_size,
+ CFOptionFlags hint,
+ void* info) {
+ void* result = g_old_cfallocator_system_default(alloc_size, hint, info);
+ if (!result)
+ debug::BreakDebugger();
+ return result;
+}
+
+void* oom_killer_cfallocator_malloc(CFIndex alloc_size,
+ CFOptionFlags hint,
+ void* info) {
+ void* result = g_old_cfallocator_malloc(alloc_size, hint, info);
+ if (!result)
+ debug::BreakDebugger();
+ return result;
+}
+
+void* oom_killer_cfallocator_malloc_zone(CFIndex alloc_size,
+ CFOptionFlags hint,
+ void* info) {
+ void* result = g_old_cfallocator_malloc_zone(alloc_size, hint, info);
+ if (!result)
+ debug::BreakDebugger();
+ return result;
+}
+
+// === Cocoa NSObject allocation ===
+
+typedef id (*allocWithZone_t)(id, SEL, NSZone*);
+allocWithZone_t g_old_allocWithZone;
+
+id oom_killer_allocWithZone(id self, SEL _cmd, NSZone* zone)
+{
+ id result = g_old_allocWithZone(self, _cmd, zone);
+ if (!result)
+ debug::BreakDebugger();
+ return result;
+}
+
+} // namespace
+
+void* UncheckedMalloc(size_t size) {
+ if (g_old_malloc) {
+#if ARCH_CPU_32_BITS
+ ScopedClearErrno clear_errno;
+ ThreadLocalBooleanAutoReset flag(g_unchecked_malloc.Pointer(), true);
+#endif // ARCH_CPU_32_BITS
+ return g_old_malloc(malloc_default_zone(), size);
+ }
+ return malloc(size);
+}
+
+void EnableTerminationOnOutOfMemory() {
+ if (g_oom_killer_enabled)
+ return;
+
+ g_oom_killer_enabled = true;
+
+ // === C malloc/calloc/valloc/realloc/posix_memalign ===
+
+ // This approach is not perfect, as requests for amounts of memory larger than
+ // MALLOC_ABSOLUTE_MAX_SIZE (currently SIZE_T_MAX - (2 * PAGE_SIZE)) will
+ // still fail with a NULL rather than dying (see
+ // http://opensource.apple.com/source/Libc/Libc-583/gen/malloc.c for details).
+ // Unfortunately, it's the best we can do. Also note that this does not affect
+ // allocations from non-default zones.
+
+ CHECK(!g_old_malloc && !g_old_calloc && !g_old_valloc && !g_old_realloc &&
+ !g_old_memalign) << "Old allocators unexpectedly non-null";
+
+ CHECK(!g_old_malloc_purgeable && !g_old_calloc_purgeable &&
+ !g_old_valloc_purgeable && !g_old_realloc_purgeable &&
+ !g_old_memalign_purgeable) << "Old allocators unexpectedly non-null";
+
+#if !defined(ADDRESS_SANITIZER)
+ // Don't do anything special on OOM for the malloc zones replaced by
+ // AddressSanitizer, as modifying or protecting them may not work correctly.
+
+ ChromeMallocZone* default_zone =
+ reinterpret_cast<ChromeMallocZone*>(malloc_default_zone());
+ ChromeMallocZone* purgeable_zone =
+ reinterpret_cast<ChromeMallocZone*>(malloc_default_purgeable_zone());
+
+ mach_vm_address_t default_reprotection_start = 0;
+ mach_vm_size_t default_reprotection_length = 0;
+ vm_prot_t default_reprotection_value = VM_PROT_NONE;
+ DeprotectMallocZone(default_zone,
+ &default_reprotection_start,
+ &default_reprotection_length,
+ &default_reprotection_value);
+
+ mach_vm_address_t purgeable_reprotection_start = 0;
+ mach_vm_size_t purgeable_reprotection_length = 0;
+ vm_prot_t purgeable_reprotection_value = VM_PROT_NONE;
+ if (purgeable_zone) {
+ DeprotectMallocZone(purgeable_zone,
+ &purgeable_reprotection_start,
+ &purgeable_reprotection_length,
+ &purgeable_reprotection_value);
+ }
+
+ // Default zone
+
+ g_old_malloc = default_zone->malloc;
+ g_old_calloc = default_zone->calloc;
+ g_old_valloc = default_zone->valloc;
+ g_old_free = default_zone->free;
+ g_old_realloc = default_zone->realloc;
+ CHECK(g_old_malloc && g_old_calloc && g_old_valloc && g_old_free &&
+ g_old_realloc)
+ << "Failed to get system allocation functions.";
+
+ default_zone->malloc = oom_killer_malloc;
+ default_zone->calloc = oom_killer_calloc;
+ default_zone->valloc = oom_killer_valloc;
+ default_zone->free = oom_killer_free;
+ default_zone->realloc = oom_killer_realloc;
+
+ if (default_zone->version >= 5) {
+ g_old_memalign = default_zone->memalign;
+ if (g_old_memalign)
+ default_zone->memalign = oom_killer_memalign;
+ }
+
+ // Purgeable zone (if it exists)
+
+ if (purgeable_zone) {
+ g_old_malloc_purgeable = purgeable_zone->malloc;
+ g_old_calloc_purgeable = purgeable_zone->calloc;
+ g_old_valloc_purgeable = purgeable_zone->valloc;
+ g_old_free_purgeable = purgeable_zone->free;
+ g_old_realloc_purgeable = purgeable_zone->realloc;
+ CHECK(g_old_malloc_purgeable && g_old_calloc_purgeable &&
+ g_old_valloc_purgeable && g_old_free_purgeable &&
+ g_old_realloc_purgeable)
+ << "Failed to get system allocation functions.";
+
+ purgeable_zone->malloc = oom_killer_malloc_purgeable;
+ purgeable_zone->calloc = oom_killer_calloc_purgeable;
+ purgeable_zone->valloc = oom_killer_valloc_purgeable;
+ purgeable_zone->free = oom_killer_free_purgeable;
+ purgeable_zone->realloc = oom_killer_realloc_purgeable;
+
+ if (purgeable_zone->version >= 5) {
+ g_old_memalign_purgeable = purgeable_zone->memalign;
+ if (g_old_memalign_purgeable)
+ purgeable_zone->memalign = oom_killer_memalign_purgeable;
+ }
+ }
+
+ // Restore protection if it was active.
+
+ if (default_reprotection_start) {
+ kern_return_t result = mach_vm_protect(mach_task_self(),
+ default_reprotection_start,
+ default_reprotection_length,
+ false,
+ default_reprotection_value);
+ CHECK(result == KERN_SUCCESS);
+ }
+
+ if (purgeable_reprotection_start) {
+ kern_return_t result = mach_vm_protect(mach_task_self(),
+ purgeable_reprotection_start,
+ purgeable_reprotection_length,
+ false,
+ purgeable_reprotection_value);
+ CHECK(result == KERN_SUCCESS);
+ }
+#endif
+
+ // === C malloc_zone_batch_malloc ===
+
+ // batch_malloc is omitted because the default malloc zone's implementation
+ // only supports batch_malloc for "tiny" allocations from the free list. It
+ // will fail for allocations larger than "tiny", and will only allocate as
+ // many blocks as it's able to from the free list. These factors mean that it
+ // can return less than the requested memory even in a non-out-of-memory
+ // situation. There's no good way to detect whether a batch_malloc failure is
+ // due to these other factors, or due to genuine memory or address space
+ // exhaustion. The fact that it only allocates space from the "tiny" free list
+ // means that it's likely that a failure will not be due to memory exhaustion.
+ // Similarly, these constraints on batch_malloc mean that callers must always
+ // be expecting to receive less memory than was requested, even in situations
+ // where memory pressure is not a concern. Finally, the only public interface
+ // to batch_malloc is malloc_zone_batch_malloc, which is specific to the
+ // system's malloc implementation. It's unlikely that anyone's even heard of
+ // it.
+
+ // === C++ operator new ===
+
+ // Yes, operator new does call through to malloc, but this will catch failures
+ // that our imperfect handling of malloc cannot.
+
+ std::set_new_handler(oom_killer_new);
+
+#ifndef ADDRESS_SANITIZER
+ // === Core Foundation CFAllocators ===
+
+ // This will not catch allocation done by custom allocators, but will catch
+ // all allocation done by system-provided ones.
+
+ CHECK(!g_old_cfallocator_system_default && !g_old_cfallocator_malloc &&
+ !g_old_cfallocator_malloc_zone)
+ << "Old allocators unexpectedly non-null";
+
+ bool cf_allocator_internals_known = CanGetContextForCFAllocator();
+
+ if (cf_allocator_internals_known) {
+ CFAllocatorContext* context =
+ ContextForCFAllocator(kCFAllocatorSystemDefault);
+ CHECK(context) << "Failed to get context for kCFAllocatorSystemDefault.";
+ g_old_cfallocator_system_default = context->allocate;
+ CHECK(g_old_cfallocator_system_default)
+ << "Failed to get kCFAllocatorSystemDefault allocation function.";
+ context->allocate = oom_killer_cfallocator_system_default;
+
+ context = ContextForCFAllocator(kCFAllocatorMalloc);
+ CHECK(context) << "Failed to get context for kCFAllocatorMalloc.";
+ g_old_cfallocator_malloc = context->allocate;
+ CHECK(g_old_cfallocator_malloc)
+ << "Failed to get kCFAllocatorMalloc allocation function.";
+ context->allocate = oom_killer_cfallocator_malloc;
+
+ context = ContextForCFAllocator(kCFAllocatorMallocZone);
+ CHECK(context) << "Failed to get context for kCFAllocatorMallocZone.";
+ g_old_cfallocator_malloc_zone = context->allocate;
+ CHECK(g_old_cfallocator_malloc_zone)
+ << "Failed to get kCFAllocatorMallocZone allocation function.";
+ context->allocate = oom_killer_cfallocator_malloc_zone;
+ } else {
+ NSLog(@"Internals of CFAllocator not known; out-of-memory failures via "
+ "CFAllocator will not result in termination. http://crbug.com/45650");
+ }
+#endif
+
+ // === Cocoa NSObject allocation ===
+
+ // Note that both +[NSObject new] and +[NSObject alloc] call through to
+ // +[NSObject allocWithZone:].
+
+ CHECK(!g_old_allocWithZone)
+ << "Old allocator unexpectedly non-null";
+
+ Class nsobject_class = [NSObject class];
+ Method orig_method = class_getClassMethod(nsobject_class,
+ @selector(allocWithZone:));
+ g_old_allocWithZone = reinterpret_cast<allocWithZone_t>(
+ method_getImplementation(orig_method));
+ CHECK(g_old_allocWithZone)
+ << "Failed to get allocWithZone allocation function.";
+ method_setImplementation(orig_method,
+ reinterpret_cast<IMP>(oom_killer_allocWithZone));
+}
+
+} // namespace base
diff --git a/chromium/base/process/memory_stubs.cc b/chromium/base/process/memory_stubs.cc
new file mode 100644
index 00000000000..b06c7d5f2b5
--- /dev/null
+++ b/chromium/base/process/memory_stubs.cc
@@ -0,0 +1,19 @@
+// 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/memory.h"
+
+namespace base {
+
+void EnableTerminationOnOutOfMemory() {
+}
+
+void EnableTerminationOnHeapCorruption() {
+}
+
+bool AdjustOOMScore(ProcessId process, int score) {
+ return false;
+}
+
+} // namespace base
diff --git a/chromium/base/process/memory_unittest.cc b/chromium/base/process/memory_unittest.cc
new file mode 100644
index 00000000000..a1f30526aa3
--- /dev/null
+++ b/chromium/base/process/memory_unittest.cc
@@ -0,0 +1,379 @@
+// 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.
+
+#define _CRT_SECURE_NO_WARNINGS
+
+#include "base/process/memory.h"
+
+#include <limits>
+
+#include "base/compiler_specific.h"
+#include "base/debug/alias.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+#if defined(OS_WIN)
+#include <windows.h>
+#endif
+#if defined(OS_POSIX)
+#include <errno.h>
+#endif
+#if defined(OS_MACOSX)
+#include <malloc/malloc.h>
+#include "base/process/memory_unittest_mac.h"
+#endif
+#if defined(OS_LINUX)
+#include <glib.h>
+#include <malloc.h>
+#endif
+
+#if defined(OS_WIN)
+// HeapQueryInformation function pointer.
+typedef BOOL (WINAPI* HeapQueryFn) \
+ (HANDLE, HEAP_INFORMATION_CLASS, PVOID, SIZE_T, PSIZE_T);
+
+const int kConstantInModule = 42;
+
+TEST(ProcessMemoryTest, GetModuleFromAddress) {
+ // Since the unit tests are their own EXE, this should be
+ // equivalent to the EXE's HINSTANCE.
+ //
+ // kConstantInModule is a constant in this file and
+ // therefore within the unit test EXE.
+ EXPECT_EQ(::GetModuleHandle(NULL),
+ base::GetModuleFromAddress(
+ const_cast<int*>(&kConstantInModule)));
+
+ // Any address within the kernel32 module should return
+ // kernel32's HMODULE. Our only assumption here is that
+ // kernel32 is larger than 4 bytes.
+ HMODULE kernel32 = ::GetModuleHandle(L"kernel32.dll");
+ HMODULE kernel32_from_address =
+ base::GetModuleFromAddress(reinterpret_cast<DWORD*>(kernel32) + 1);
+ EXPECT_EQ(kernel32, kernel32_from_address);
+}
+
+TEST(ProcessMemoryTest, EnableLFH) {
+ ASSERT_TRUE(base::EnableLowFragmentationHeap());
+ if (IsDebuggerPresent()) {
+ // Under these conditions, LFH can't be enabled. There's no point to test
+ // anything.
+ const char* no_debug_env = getenv("_NO_DEBUG_HEAP");
+ if (!no_debug_env || strcmp(no_debug_env, "1"))
+ return;
+ }
+ HMODULE kernel32 = GetModuleHandle(L"kernel32.dll");
+ ASSERT_TRUE(kernel32 != NULL);
+ HeapQueryFn heap_query = reinterpret_cast<HeapQueryFn>(GetProcAddress(
+ kernel32,
+ "HeapQueryInformation"));
+
+ // On Windows 2000, the function is not exported. This is not a reason to
+ // fail but we won't be able to retrieves information about the heap, so we
+ // should stop here.
+ if (heap_query == NULL)
+ return;
+
+ HANDLE heaps[1024] = { 0 };
+ unsigned number_heaps = GetProcessHeaps(1024, heaps);
+ EXPECT_GT(number_heaps, 0u);
+ for (unsigned i = 0; i < number_heaps; ++i) {
+ ULONG flag = 0;
+ SIZE_T length;
+ ASSERT_NE(0, heap_query(heaps[i],
+ HeapCompatibilityInformation,
+ &flag,
+ sizeof(flag),
+ &length));
+ // If flag is 0, the heap is a standard heap that does not support
+ // look-asides. If flag is 1, the heap supports look-asides. If flag is 2,
+ // the heap is a low-fragmentation heap (LFH). Note that look-asides are not
+ // supported on the LFH.
+
+ // We don't have any documented way of querying the HEAP_NO_SERIALIZE flag.
+ EXPECT_LE(flag, 2u);
+ EXPECT_NE(flag, 1u);
+ }
+}
+#endif // defined(OS_WIN)
+
+#if defined(OS_MACOSX)
+
+// For the following Mac tests:
+// Note that base::EnableTerminationOnHeapCorruption() is called as part of
+// test suite setup and does not need to be done again, else mach_override
+// will fail.
+
+#if !defined(ADDRESS_SANITIZER)
+// The following code tests the system implementation of malloc() thus no need
+// to test it under AddressSanitizer.
+TEST(ProcessMemoryTest, MacMallocFailureDoesNotTerminate) {
+ // Test that ENOMEM doesn't crash via CrMallocErrorBreak two ways: the exit
+ // code and lack of the error string. The number of bytes is one less than
+ // MALLOC_ABSOLUTE_MAX_SIZE, more than which the system early-returns NULL and
+ // does not call through malloc_error_break(). See the comment at
+ // EnableTerminationOnOutOfMemory() for more information.
+ void* buf = NULL;
+ ASSERT_EXIT(
+ {
+ base::EnableTerminationOnOutOfMemory();
+
+ buf = malloc(std::numeric_limits<size_t>::max() - (2 * PAGE_SIZE) - 1);
+ },
+ testing::KilledBySignal(SIGTRAP),
+ "\\*\\*\\* error: can't allocate region.*"
+ "(Terminating process due to a potential for future heap "
+ "corruption){0}");
+
+ base::debug::Alias(buf);
+}
+#endif // !defined(ADDRESS_SANITIZER)
+
+TEST(ProcessMemoryTest, MacTerminateOnHeapCorruption) {
+ // Assert that freeing an unallocated pointer will crash the process.
+ char buf[9];
+ asm("" : "=r" (buf)); // Prevent clang from being too smart.
+#if ARCH_CPU_64_BITS
+ // On 64 bit Macs, the malloc system automatically abort()s on heap corruption
+ // but does not output anything.
+ ASSERT_DEATH(free(buf), "");
+#elif defined(ADDRESS_SANITIZER)
+ // AddressSanitizer replaces malloc() and prints a different error message on
+ // heap corruption.
+ ASSERT_DEATH(free(buf), "attempting free on address which "
+ "was not malloc\\(\\)-ed");
+#else
+ ASSERT_DEATH(free(buf), "being freed.*"
+ "\\*\\*\\* set a breakpoint in malloc_error_break to debug.*"
+ "Terminating process due to a potential for future heap corruption");
+#endif // ARCH_CPU_64_BITS || defined(ADDRESS_SANITIZER)
+}
+
+#endif // defined(OS_MACOSX)
+
+// Android doesn't implement set_new_handler, so we can't use the
+// OutOfMemoryTest cases.
+// OpenBSD does not support these tests either.
+// AddressSanitizer and ThreadSanitizer define the malloc()/free()/etc.
+// functions so that they don't crash if the program is out of memory, so the
+// OOM tests aren't supposed to work.
+// TODO(vandebo) make this work on Windows too.
+#if !defined(OS_ANDROID) && !defined(OS_OPENBSD) && \
+ !defined(OS_WIN) && \
+ !defined(ADDRESS_SANITIZER) && !defined(THREAD_SANITIZER)
+
+#if defined(USE_TCMALLOC)
+extern "C" {
+int tc_set_new_mode(int mode);
+}
+#endif // defined(USE_TCMALLOC)
+
+class OutOfMemoryDeathTest : public testing::Test {
+ public:
+ OutOfMemoryDeathTest()
+ : value_(NULL),
+ // Make test size as large as possible minus a few pages so
+ // that alignment or other rounding doesn't make it wrap.
+ test_size_(std::numeric_limits<std::size_t>::max() - 12 * 1024),
+ signed_test_size_(std::numeric_limits<ssize_t>::max()) {
+ }
+
+#if defined(USE_TCMALLOC)
+ virtual void SetUp() OVERRIDE {
+ tc_set_new_mode(1);
+ }
+
+ virtual void TearDown() OVERRIDE {
+ tc_set_new_mode(0);
+ }
+#endif // defined(USE_TCMALLOC)
+
+ void SetUpInDeathAssert() {
+ // Must call EnableTerminationOnOutOfMemory() because that is called from
+ // chrome's main function and therefore hasn't been called yet.
+ // Since this call may result in another thread being created and death
+ // tests shouldn't be started in a multithread environment, this call
+ // should be done inside of the ASSERT_DEATH.
+ base::EnableTerminationOnOutOfMemory();
+ }
+
+ void* value_;
+ size_t test_size_;
+ ssize_t signed_test_size_;
+};
+
+TEST_F(OutOfMemoryDeathTest, New) {
+ ASSERT_DEATH({
+ SetUpInDeathAssert();
+ value_ = operator new(test_size_);
+ }, "");
+}
+
+TEST_F(OutOfMemoryDeathTest, NewArray) {
+ ASSERT_DEATH({
+ SetUpInDeathAssert();
+ value_ = new char[test_size_];
+ }, "");
+}
+
+TEST_F(OutOfMemoryDeathTest, Malloc) {
+ ASSERT_DEATH({
+ SetUpInDeathAssert();
+ value_ = malloc(test_size_);
+ }, "");
+}
+
+TEST_F(OutOfMemoryDeathTest, Realloc) {
+ ASSERT_DEATH({
+ SetUpInDeathAssert();
+ value_ = realloc(NULL, test_size_);
+ }, "");
+}
+
+TEST_F(OutOfMemoryDeathTest, Calloc) {
+ ASSERT_DEATH({
+ SetUpInDeathAssert();
+ value_ = calloc(1024, test_size_ / 1024L);
+ }, "");
+}
+
+TEST_F(OutOfMemoryDeathTest, Valloc) {
+ ASSERT_DEATH({
+ SetUpInDeathAssert();
+ value_ = valloc(test_size_);
+ }, "");
+}
+
+#if defined(OS_LINUX)
+TEST_F(OutOfMemoryDeathTest, Pvalloc) {
+ ASSERT_DEATH({
+ SetUpInDeathAssert();
+ value_ = pvalloc(test_size_);
+ }, "");
+}
+
+TEST_F(OutOfMemoryDeathTest, Memalign) {
+ ASSERT_DEATH({
+ SetUpInDeathAssert();
+ value_ = memalign(4, test_size_);
+ }, "");
+}
+
+TEST_F(OutOfMemoryDeathTest, ViaSharedLibraries) {
+ // g_try_malloc is documented to return NULL on failure. (g_malloc is the
+ // 'safe' default that crashes if allocation fails). However, since we have
+ // hopefully overridden malloc, even g_try_malloc should fail. This tests
+ // that the run-time symbol resolution is overriding malloc for shared
+ // libraries as well as for our code.
+ ASSERT_DEATH({
+ SetUpInDeathAssert();
+ value_ = g_try_malloc(test_size_);
+ }, "");
+}
+#endif // OS_LINUX
+
+// Android doesn't implement posix_memalign().
+#if defined(OS_POSIX) && !defined(OS_ANDROID)
+TEST_F(OutOfMemoryDeathTest, Posix_memalign) {
+ // Grab the return value of posix_memalign to silence a compiler warning
+ // about unused return values. We don't actually care about the return
+ // value, since we're asserting death.
+ ASSERT_DEATH({
+ SetUpInDeathAssert();
+ EXPECT_EQ(ENOMEM, posix_memalign(&value_, 8, test_size_));
+ }, "");
+}
+#endif // defined(OS_POSIX) && !defined(OS_ANDROID)
+
+#if defined(OS_MACOSX)
+
+// Purgeable zone tests
+
+TEST_F(OutOfMemoryDeathTest, MallocPurgeable) {
+ malloc_zone_t* zone = malloc_default_purgeable_zone();
+ ASSERT_DEATH({
+ SetUpInDeathAssert();
+ value_ = malloc_zone_malloc(zone, test_size_);
+ }, "");
+}
+
+TEST_F(OutOfMemoryDeathTest, ReallocPurgeable) {
+ malloc_zone_t* zone = malloc_default_purgeable_zone();
+ ASSERT_DEATH({
+ SetUpInDeathAssert();
+ value_ = malloc_zone_realloc(zone, NULL, test_size_);
+ }, "");
+}
+
+TEST_F(OutOfMemoryDeathTest, CallocPurgeable) {
+ malloc_zone_t* zone = malloc_default_purgeable_zone();
+ ASSERT_DEATH({
+ SetUpInDeathAssert();
+ value_ = malloc_zone_calloc(zone, 1024, test_size_ / 1024L);
+ }, "");
+}
+
+TEST_F(OutOfMemoryDeathTest, VallocPurgeable) {
+ malloc_zone_t* zone = malloc_default_purgeable_zone();
+ ASSERT_DEATH({
+ SetUpInDeathAssert();
+ value_ = malloc_zone_valloc(zone, test_size_);
+ }, "");
+}
+
+TEST_F(OutOfMemoryDeathTest, PosixMemalignPurgeable) {
+ malloc_zone_t* zone = malloc_default_purgeable_zone();
+ ASSERT_DEATH({
+ SetUpInDeathAssert();
+ value_ = malloc_zone_memalign(zone, 8, test_size_);
+ }, "");
+}
+
+// Since these allocation functions take a signed size, it's possible that
+// calling them just once won't be enough to exhaust memory. In the 32-bit
+// environment, it's likely that these allocation attempts will fail because
+// not enough contiguous address space is available. In the 64-bit environment,
+// it's likely that they'll fail because they would require a preposterous
+// amount of (virtual) memory.
+
+TEST_F(OutOfMemoryDeathTest, CFAllocatorSystemDefault) {
+ ASSERT_DEATH({
+ SetUpInDeathAssert();
+ while ((value_ =
+ base::AllocateViaCFAllocatorSystemDefault(signed_test_size_))) {}
+ }, "");
+}
+
+TEST_F(OutOfMemoryDeathTest, CFAllocatorMalloc) {
+ ASSERT_DEATH({
+ SetUpInDeathAssert();
+ while ((value_ =
+ base::AllocateViaCFAllocatorMalloc(signed_test_size_))) {}
+ }, "");
+}
+
+TEST_F(OutOfMemoryDeathTest, CFAllocatorMallocZone) {
+ ASSERT_DEATH({
+ SetUpInDeathAssert();
+ while ((value_ =
+ base::AllocateViaCFAllocatorMallocZone(signed_test_size_))) {}
+ }, "");
+}
+
+#if !defined(ARCH_CPU_64_BITS)
+
+// See process_util_unittest_mac.mm for an explanation of why this test isn't
+// run in the 64-bit environment.
+
+TEST_F(OutOfMemoryDeathTest, PsychoticallyBigObjCObject) {
+ ASSERT_DEATH({
+ SetUpInDeathAssert();
+ while ((value_ = base::AllocatePsychoticallyBigObjCObject())) {}
+ }, "");
+}
+
+#endif // !ARCH_CPU_64_BITS
+#endif // OS_MACOSX
+
+#endif // !defined(OS_ANDROID) && !defined(OS_OPENBSD) &&
+ // !defined(OS_WIN) && !defined(ADDRESS_SANITIZER)
diff --git a/chromium/base/process/memory_unittest_mac.h b/chromium/base/process/memory_unittest_mac.h
new file mode 100644
index 00000000000..472d2c59627
--- /dev/null
+++ b/chromium/base/process/memory_unittest_mac.h
@@ -0,0 +1,32 @@
+// Copyright (c) 2010 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 helpers for the process_util_unittest to allow it to fully
+// test the Mac code.
+
+#ifndef BASE_PROCESS_MEMORY_UNITTEST_MAC_H_
+#define BASE_PROCESS_MEMORY_UNITTEST_MAC_H_
+
+#include "base/basictypes.h"
+
+namespace base {
+
+// Allocates memory via system allocators. Alas, they take a _signed_ size for
+// allocation.
+void* AllocateViaCFAllocatorSystemDefault(ssize_t size);
+void* AllocateViaCFAllocatorMalloc(ssize_t size);
+void* AllocateViaCFAllocatorMallocZone(ssize_t size);
+
+#if !defined(ARCH_CPU_64_BITS)
+// See process_util_unittest_mac.mm for an explanation of why this function
+// isn't implemented for the 64-bit environment.
+
+// Allocates a huge Objective C object.
+void* AllocatePsychoticallyBigObjCObject();
+
+#endif // !ARCH_CPU_64_BITS
+
+} // namespace base
+
+#endif // BASE_PROCESS_MEMORY_UNITTEST_MAC_H_
diff --git a/chromium/base/process/memory_unittest_mac.mm b/chromium/base/process/memory_unittest_mac.mm
new file mode 100644
index 00000000000..bc4bf65347d
--- /dev/null
+++ b/chromium/base/process/memory_unittest_mac.mm
@@ -0,0 +1,59 @@
+// Copyright (c) 2010 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/memory_unittest_mac.h"
+
+#import <Foundation/Foundation.h>
+#include <CoreFoundation/CoreFoundation.h>
+
+#if !defined(ARCH_CPU_64_BITS)
+
+// In the 64-bit environment, the Objective-C 2.0 Runtime Reference states
+// that sizeof(anInstance) is constrained to 32 bits. That's not necessarily
+// "psychotically big" and in fact a 64-bit program is expected to be able to
+// successfully allocate an object that large, likely reserving a good deal of
+// swap space. The only way to test the behavior of memory exhaustion for
+// Objective-C allocation in this environment would be to loop over allocation
+// of these large objects, but that would slowly consume all available memory
+// and cause swap file proliferation. That's bad, so this behavior isn't
+// tested in the 64-bit environment.
+
+@interface PsychoticallyBigObjCObject : NSObject
+{
+ // In the 32-bit environment, the compiler limits Objective-C objects to
+ // < 2GB in size.
+ int justUnder2Gigs_[(2U * 1024 * 1024 * 1024 - 1) / sizeof(int)];
+}
+
+@end
+
+@implementation PsychoticallyBigObjCObject
+
+@end
+
+namespace base {
+
+void* AllocatePsychoticallyBigObjCObject() {
+ return [[PsychoticallyBigObjCObject alloc] init];
+}
+
+} // namespace base
+
+#endif // ARCH_CPU_64_BITS
+
+namespace base {
+
+void* AllocateViaCFAllocatorSystemDefault(ssize_t size) {
+ return CFAllocatorAllocate(kCFAllocatorSystemDefault, size, 0);
+}
+
+void* AllocateViaCFAllocatorMalloc(ssize_t size) {
+ return CFAllocatorAllocate(kCFAllocatorMalloc, size, 0);
+}
+
+void* AllocateViaCFAllocatorMallocZone(ssize_t size) {
+ return CFAllocatorAllocate(kCFAllocatorMallocZone, size, 0);
+}
+
+} // namespace base
diff --git a/chromium/base/process/memory_win.cc b/chromium/base/process/memory_win.cc
new file mode 100644
index 00000000000..c53a1be5395
--- /dev/null
+++ b/chromium/base/process/memory_win.cc
@@ -0,0 +1,85 @@
+// 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/memory.h"
+
+#include <psapi.h>
+
+#include "base/logging.h"
+#include "base/memory/scoped_ptr.h"
+
+namespace base {
+
+namespace {
+
+void OnNoMemory() {
+ // Kill the process. This is important for security, since WebKit doesn't
+ // NULL-check many memory allocations. If a malloc fails, returns NULL, and
+ // the buffer is then used, it provides a handy mapping of memory starting at
+ // address 0 for an attacker to utilize.
+ __debugbreak();
+ _exit(1);
+}
+
+// HeapSetInformation function pointer.
+typedef BOOL (WINAPI* HeapSetFn)(HANDLE, HEAP_INFORMATION_CLASS, PVOID, SIZE_T);
+
+} // namespace
+
+bool EnableLowFragmentationHeap() {
+ HMODULE kernel32 = GetModuleHandle(L"kernel32.dll");
+ HeapSetFn heap_set = reinterpret_cast<HeapSetFn>(GetProcAddress(
+ kernel32,
+ "HeapSetInformation"));
+
+ // On Windows 2000, the function is not exported. This is not a reason to
+ // fail.
+ if (!heap_set)
+ return true;
+
+ unsigned number_heaps = GetProcessHeaps(0, NULL);
+ if (!number_heaps)
+ return false;
+
+ // Gives us some extra space in the array in case a thread is creating heaps
+ // at the same time we're querying them.
+ static const int MARGIN = 8;
+ scoped_ptr<HANDLE[]> heaps(new HANDLE[number_heaps + MARGIN]);
+ number_heaps = GetProcessHeaps(number_heaps + MARGIN, heaps.get());
+ if (!number_heaps)
+ return false;
+
+ for (unsigned i = 0; i < number_heaps; ++i) {
+ ULONG lfh_flag = 2;
+ // Don't bother with the result code. It may fails on heaps that have the
+ // HEAP_NO_SERIALIZE flag. This is expected and not a problem at all.
+ heap_set(heaps[i],
+ HeapCompatibilityInformation,
+ &lfh_flag,
+ sizeof(lfh_flag));
+ }
+ return true;
+}
+
+void EnableTerminationOnHeapCorruption() {
+ // Ignore the result code. Supported on XP SP3 and Vista.
+ HeapSetInformation(NULL, HeapEnableTerminationOnCorruption, NULL, 0);
+}
+
+void EnableTerminationOnOutOfMemory() {
+ std::set_new_handler(&OnNoMemory);
+}
+
+HMODULE GetModuleFromAddress(void* address) {
+ HMODULE instance = NULL;
+ if (!::GetModuleHandleExA(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS |
+ GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT,
+ static_cast<char*>(address),
+ &instance)) {
+ NOTREACHED();
+ }
+ return instance;
+}
+
+} // namespace base
diff --git a/chromium/base/process/process.h b/chromium/base/process/process.h
new file mode 100644
index 00000000000..20e8884b972
--- /dev/null
+++ b/chromium/base/process/process.h
@@ -0,0 +1,70 @@
+// Copyright (c) 2011 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.
+
+#ifndef BASE_PROCESS_PROCESS_PROCESS_H_
+#define BASE_PROCESS_PROCESS_PROCESS_H_
+
+#include "base/base_export.h"
+#include "base/basictypes.h"
+#include "base/process/process_handle.h"
+#include "build/build_config.h"
+
+namespace base {
+
+class BASE_EXPORT Process {
+ public:
+ Process() : process_(kNullProcessHandle) {
+ }
+
+ explicit Process(ProcessHandle handle) : process_(handle) {
+ }
+
+ // A handle to the current process.
+ static Process Current();
+
+ static bool CanBackgroundProcesses();
+
+ // Get/Set the handle for this process. The handle will be 0 if the process
+ // is no longer running.
+ ProcessHandle handle() const { return process_; }
+ void set_handle(ProcessHandle handle) {
+ process_ = handle;
+ }
+
+ // Get the PID for this process.
+ ProcessId pid() const;
+
+ // Is the this process the current process.
+ bool is_current() const;
+
+ // Close the process handle. This will not terminate the process.
+ void Close();
+
+ // Terminates the process with extreme prejudice. The given result code will
+ // be the exit code of the process. If the process has already exited, this
+ // will do nothing.
+ void Terminate(int result_code);
+
+ // A process is backgrounded when it's priority is lower than normal.
+ // Return true if this process is backgrounded, false otherwise.
+ bool IsProcessBackgrounded() const;
+
+ // Set a process as backgrounded. If value is true, the priority
+ // of the process will be lowered. If value is false, the priority
+ // of the process will be made "normal" - equivalent to default
+ // process priority.
+ // Returns true if the priority was changed, false otherwise.
+ bool SetProcessBackgrounded(bool value);
+
+ // Returns an integer representing the priority of a process. The meaning
+ // of this value is OS dependent.
+ int GetPriority() const;
+
+ private:
+ ProcessHandle process_;
+};
+
+} // namespace base
+
+#endif // BASE_PROCESS_PROCESS_PROCESS_H_
diff --git a/chromium/base/process/process_handle.h b/chromium/base/process/process_handle.h
new file mode 100644
index 00000000000..a37784275ea
--- /dev/null
+++ b/chromium/base/process/process_handle.h
@@ -0,0 +1,96 @@
+// 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.
+
+#ifndef BASE_PROCESS_PROCESS_HANDLE_H_
+#define BASE_PROCESS_PROCESS_HANDLE_H_
+
+#include "base/base_export.h"
+#include "base/basictypes.h"
+#include "base/files/file_path.h"
+#include "build/build_config.h"
+
+#include <sys/types.h>
+#if defined(OS_WIN)
+#include <windows.h>
+#endif
+
+namespace base {
+
+// ProcessHandle is a platform specific type which represents the underlying OS
+// handle to a process.
+// ProcessId is a number which identifies the process in the OS.
+#if defined(OS_WIN)
+typedef HANDLE ProcessHandle;
+typedef DWORD ProcessId;
+typedef HANDLE UserTokenHandle;
+const ProcessHandle kNullProcessHandle = NULL;
+const ProcessId kNullProcessId = 0;
+#elif defined(OS_POSIX)
+// On POSIX, our ProcessHandle will just be the PID.
+typedef pid_t ProcessHandle;
+typedef pid_t ProcessId;
+const ProcessHandle kNullProcessHandle = 0;
+const ProcessId kNullProcessId = 0;
+#endif // defined(OS_WIN)
+
+// Returns the id of the current process.
+BASE_EXPORT ProcessId GetCurrentProcId();
+
+// Returns the ProcessHandle of the current process.
+BASE_EXPORT ProcessHandle GetCurrentProcessHandle();
+
+// Converts a PID to a process handle. This handle must be closed by
+// CloseProcessHandle when you are done with it. Returns true on success.
+BASE_EXPORT bool OpenProcessHandle(ProcessId pid, ProcessHandle* handle);
+
+// Converts a PID to a process handle. On Windows the handle is opened
+// with more access rights and must only be used by trusted code.
+// You have to close returned handle using CloseProcessHandle. Returns true
+// on success.
+// TODO(sanjeevr): Replace all calls to OpenPrivilegedProcessHandle with the
+// more specific OpenProcessHandleWithAccess method and delete this.
+BASE_EXPORT bool OpenPrivilegedProcessHandle(ProcessId pid,
+ ProcessHandle* handle);
+
+// Converts a PID to a process handle using the desired access flags. Use a
+// combination of the kProcessAccess* flags defined above for |access_flags|.
+BASE_EXPORT bool OpenProcessHandleWithAccess(ProcessId pid,
+ uint32 access_flags,
+ ProcessHandle* handle);
+
+// Closes the process handle opened by OpenProcessHandle.
+BASE_EXPORT void CloseProcessHandle(ProcessHandle process);
+
+// Returns the unique ID for the specified process. This is functionally the
+// same as Windows' GetProcessId(), but works on versions of Windows before
+// Win XP SP1 as well.
+BASE_EXPORT ProcessId GetProcId(ProcessHandle process);
+
+#if defined(OS_WIN)
+enum IntegrityLevel {
+ INTEGRITY_UNKNOWN,
+ LOW_INTEGRITY,
+ MEDIUM_INTEGRITY,
+ HIGH_INTEGRITY,
+};
+// Determine the integrity level of the specified process. Returns false
+// if the system does not support integrity levels (pre-Vista) or in the case
+// of an underlying system failure.
+BASE_EXPORT bool GetProcessIntegrityLevel(ProcessHandle process,
+ IntegrityLevel* level);
+#endif
+
+#if defined(OS_LINUX) || defined(OS_ANDROID) || defined(OS_BSD)
+// Returns the path to the executable of the given process.
+BASE_EXPORT FilePath GetProcessExecutablePath(ProcessHandle process);
+#endif
+
+#if defined(OS_POSIX)
+// Returns the ID for the parent of the given process.
+BASE_EXPORT ProcessId GetParentProcessId(ProcessHandle process);
+#endif
+
+} // namespace base
+
+#endif // BASE_PROCESS_PROCESS_HANDLE_H_
diff --git a/chromium/base/process/process_handle_freebsd.cc b/chromium/base/process/process_handle_freebsd.cc
new file mode 100644
index 00000000000..8a0e9de8f2d
--- /dev/null
+++ b/chromium/base/process/process_handle_freebsd.cc
@@ -0,0 +1,39 @@
+// Copyright (c) 2011 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_handle.h"
+
+#include <sys/sysctl.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+namespace base {
+
+ProcessId GetParentProcessId(ProcessHandle process) {
+ struct kinfo_proc info;
+ size_t length;
+ int mib[] = { CTL_KERN, KERN_PROC, KERN_PROC_PID, process };
+
+ if (sysctl(mib, arraysize(mib), &info, &length, NULL, 0) < 0)
+ return -1;
+
+ return info.ki_ppid;
+}
+
+FilePath GetProcessExecutablePath(ProcessHandle process) {
+ char pathname[PATH_MAX];
+ size_t length;
+ int mib[] = { CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, process };
+
+ length = sizeof(pathname);
+
+ if (sysctl(mib, arraysize(mib), pathname, &length, NULL, 0) < 0 ||
+ length == 0) {
+ return FilePath();
+ }
+
+ return FilePath(std::string(pathname));
+}
+
+} // namespace base
diff --git a/chromium/base/process/process_handle_linux.cc b/chromium/base/process/process_handle_linux.cc
new file mode 100644
index 00000000000..91441f7b38c
--- /dev/null
+++ b/chromium/base/process/process_handle_linux.cc
@@ -0,0 +1,30 @@
+// 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_handle.h"
+
+#include "base/file_util.h"
+#include "base/process/internal_linux.h"
+
+namespace base {
+
+ProcessId GetParentProcessId(ProcessHandle process) {
+ ProcessId pid =
+ internal::ReadProcStatsAndGetFieldAsInt(process, internal::VM_PPID);
+ if (pid)
+ return pid;
+ return -1;
+}
+
+FilePath GetProcessExecutablePath(ProcessHandle process) {
+ 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
+ return FilePath();
+ }
+ return exe_name;
+}
+
+} // namespace base
diff --git a/chromium/base/process/process_handle_mac.cc b/chromium/base/process/process_handle_mac.cc
new file mode 100644
index 00000000000..6cb8d686e4d
--- /dev/null
+++ b/chromium/base/process/process_handle_mac.cc
@@ -0,0 +1,27 @@
+// 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_handle.h"
+
+#include <sys/sysctl.h>
+#include <sys/types.h>
+
+#include "base/logging.h"
+
+namespace base {
+
+ProcessId GetParentProcessId(ProcessHandle process) {
+ struct kinfo_proc info;
+ size_t length = sizeof(struct kinfo_proc);
+ int mib[4] = { CTL_KERN, KERN_PROC, KERN_PROC_PID, process };
+ if (sysctl(mib, 4, &info, &length, NULL, 0) < 0) {
+ DPLOG(ERROR) << "sysctl";
+ return -1;
+ }
+ if (length == 0)
+ return -1;
+ return info.kp_eproc.e_ppid;
+}
+
+} // namespace base
diff --git a/chromium/base/process/process_handle_openbsd.cc b/chromium/base/process/process_handle_openbsd.cc
new file mode 100644
index 00000000000..3508ccb04cb
--- /dev/null
+++ b/chromium/base/process/process_handle_openbsd.cc
@@ -0,0 +1,49 @@
+// Copyright (c) 2011 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_handle.h"
+
+#include <sys/sysctl.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+namespace base {
+
+ProcessId GetParentProcessId(ProcessHandle process) {
+ 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_ppid;
+}
+
+FilePath GetProcessExecutablePath(ProcessHandle process) {
+ struct kinfo_proc kp;
+ size_t len;
+ int mib[] = { CTL_KERN, KERN_PROC, KERN_PROC_PID, process,
+ sizeof(struct kinfo_proc), 0 };
+
+ if (sysctl(mib, arraysize(mib), NULL, &len, NULL, 0) == -1)
+ return FilePath();
+ mib[5] = (len / sizeof(struct kinfo_proc));
+ if (sysctl(mib, arraysize(mib), &kp, &len, NULL, 0) < 0)
+ return FilePath();
+ if ((kp.p_flag & P_SYSTEM) != 0)
+ return FilePath();
+ if (strcmp(kp.p_comm, "chrome") == 0)
+ return FilePath(kp.p_comm);
+
+ return FilePath();
+}
+
+} // namespace base
diff --git a/chromium/base/process/process_handle_posix.cc b/chromium/base/process/process_handle_posix.cc
new file mode 100644
index 00000000000..4013254a5c2
--- /dev/null
+++ b/chromium/base/process/process_handle_posix.cc
@@ -0,0 +1,49 @@
+// 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_handle.h"
+
+#include <unistd.h>
+
+namespace base {
+
+ProcessId GetCurrentProcId() {
+ return getpid();
+}
+
+ProcessHandle GetCurrentProcessHandle() {
+ return GetCurrentProcId();
+}
+
+bool OpenProcessHandle(ProcessId pid, ProcessHandle* handle) {
+ // On Posix platforms, process handles are the same as PIDs, so we
+ // don't need to do anything.
+ *handle = pid;
+ return true;
+}
+
+bool OpenPrivilegedProcessHandle(ProcessId pid, ProcessHandle* handle) {
+ // On POSIX permissions are checked for each operation on process,
+ // not when opening a "handle".
+ return OpenProcessHandle(pid, handle);
+}
+
+bool OpenProcessHandleWithAccess(ProcessId pid,
+ uint32 access_flags,
+ ProcessHandle* handle) {
+ // On POSIX permissions are checked for each operation on process,
+ // not when opening a "handle".
+ return OpenProcessHandle(pid, handle);
+}
+
+void CloseProcessHandle(ProcessHandle process) {
+ // See OpenProcessHandle, nothing to do.
+ return;
+}
+
+ProcessId GetProcId(ProcessHandle process) {
+ return process;
+}
+
+} // namespace base
diff --git a/chromium/base/process/process_handle_win.cc b/chromium/base/process/process_handle_win.cc
new file mode 100644
index 00000000000..3bc3a125e0d
--- /dev/null
+++ b/chromium/base/process/process_handle_win.cc
@@ -0,0 +1,126 @@
+// 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_handle.h"
+
+#include <windows.h>
+
+#include "base/memory/scoped_ptr.h"
+#include "base/win/scoped_handle.h"
+#include "base/win/windows_version.h"
+
+namespace base {
+
+ProcessId GetCurrentProcId() {
+ return ::GetCurrentProcessId();
+}
+
+ProcessHandle GetCurrentProcessHandle() {
+ return ::GetCurrentProcess();
+}
+
+bool OpenProcessHandle(ProcessId pid, ProcessHandle* handle) {
+ // We try to limit privileges granted to the handle. If you need this
+ // for test code, consider using OpenPrivilegedProcessHandle instead of
+ // adding more privileges here.
+ ProcessHandle result = OpenProcess(PROCESS_TERMINATE |
+ PROCESS_QUERY_INFORMATION |
+ SYNCHRONIZE,
+ FALSE, pid);
+
+ if (result == NULL)
+ return false;
+
+ *handle = result;
+ return true;
+}
+
+bool OpenPrivilegedProcessHandle(ProcessId pid, ProcessHandle* handle) {
+ ProcessHandle result = OpenProcess(PROCESS_DUP_HANDLE |
+ PROCESS_TERMINATE |
+ PROCESS_QUERY_INFORMATION |
+ PROCESS_VM_READ |
+ SYNCHRONIZE,
+ FALSE, pid);
+
+ if (result == NULL)
+ return false;
+
+ *handle = result;
+ return true;
+}
+
+bool OpenProcessHandleWithAccess(ProcessId pid,
+ uint32 access_flags,
+ ProcessHandle* handle) {
+ ProcessHandle result = OpenProcess(access_flags, FALSE, pid);
+
+ if (result == NULL)
+ return false;
+
+ *handle = result;
+ return true;
+}
+
+void CloseProcessHandle(ProcessHandle process) {
+ CloseHandle(process);
+}
+
+ProcessId GetProcId(ProcessHandle process) {
+ // This returns 0 if we have insufficient rights to query the process handle.
+ return GetProcessId(process);
+}
+
+bool GetProcessIntegrityLevel(ProcessHandle process, IntegrityLevel *level) {
+ if (!level)
+ return false;
+
+ if (win::GetVersion() < base::win::VERSION_VISTA)
+ return false;
+
+ HANDLE process_token;
+ if (!OpenProcessToken(process, TOKEN_QUERY | TOKEN_QUERY_SOURCE,
+ &process_token))
+ return false;
+
+ win::ScopedHandle scoped_process_token(process_token);
+
+ DWORD token_info_length = 0;
+ if (GetTokenInformation(process_token, TokenIntegrityLevel, NULL, 0,
+ &token_info_length) ||
+ GetLastError() != ERROR_INSUFFICIENT_BUFFER)
+ return false;
+
+ scoped_ptr<char[]> token_label_bytes(new char[token_info_length]);
+ if (!token_label_bytes.get())
+ return false;
+
+ TOKEN_MANDATORY_LABEL* token_label =
+ reinterpret_cast<TOKEN_MANDATORY_LABEL*>(token_label_bytes.get());
+ if (!token_label)
+ return false;
+
+ if (!GetTokenInformation(process_token, TokenIntegrityLevel, token_label,
+ token_info_length, &token_info_length))
+ return false;
+
+ DWORD integrity_level = *GetSidSubAuthority(token_label->Label.Sid,
+ (DWORD)(UCHAR)(*GetSidSubAuthorityCount(token_label->Label.Sid)-1));
+
+ if (integrity_level < SECURITY_MANDATORY_MEDIUM_RID) {
+ *level = LOW_INTEGRITY;
+ } else if (integrity_level >= SECURITY_MANDATORY_MEDIUM_RID &&
+ integrity_level < SECURITY_MANDATORY_HIGH_RID) {
+ *level = MEDIUM_INTEGRITY;
+ } else if (integrity_level >= SECURITY_MANDATORY_HIGH_RID) {
+ *level = HIGH_INTEGRITY;
+ } else {
+ NOTREACHED();
+ return false;
+ }
+
+ return true;
+}
+
+} // namespace base
diff --git a/chromium/base/process/process_info.h b/chromium/base/process/process_info.h
new file mode 100644
index 00000000000..e9e7b4e8151
--- /dev/null
+++ b/chromium/base/process/process_info.h
@@ -0,0 +1,25 @@
+// 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.
+
+#ifndef BASE_PROCESS_PROCESS_PROCESS_INFO_H_
+#define BASE_PROCESS_PROCESS_PROCESS_INFO_H_
+
+#include "base/base_export.h"
+#include "base/basictypes.h"
+
+namespace base {
+
+class Time;
+
+// Vends information about the current process.
+class BASE_EXPORT CurrentProcessInfo {
+ public:
+ // Returns the time at which the process was launched. May be empty if an
+ // error occurred retrieving the information.
+ static const Time CreationTime();
+};
+
+} // namespace base
+
+#endif // BASE_PROCESS_PROCESS_PROCESS_INFO_H_
diff --git a/chromium/base/process/process_info_linux.cc b/chromium/base/process/process_info_linux.cc
new file mode 100644
index 00000000000..da34c180e8a
--- /dev/null
+++ b/chromium/base/process/process_info_linux.cc
@@ -0,0 +1,27 @@
+// Copyright 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_info.h"
+
+#include "base/basictypes.h"
+#include "base/logging.h"
+#include "base/process/internal_linux.h"
+#include "base/process/process_handle.h"
+#include "base/time/time.h"
+
+namespace base {
+
+//static
+const Time CurrentProcessInfo::CreationTime() {
+ ProcessHandle pid = GetCurrentProcessHandle();
+ int start_ticks = internal::ReadProcStatsAndGetFieldAsInt(
+ pid, internal::VM_STARTTIME);
+ DCHECK(start_ticks);
+ TimeDelta start_offset = internal::ClockTicksToTimeDelta(start_ticks);
+ Time boot_time = internal::GetBootTime();
+ DCHECK(!boot_time.is_null());
+ return Time(boot_time + start_offset);
+}
+
+} // namespace base
diff --git a/chromium/base/process/process_info_mac.cc b/chromium/base/process/process_info_mac.cc
new file mode 100644
index 00000000000..ab8394b891d
--- /dev/null
+++ b/chromium/base/process/process_info_mac.cc
@@ -0,0 +1,31 @@
+// 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/process_info.h"
+
+#include <sys/sysctl.h>
+#include <sys/time.h>
+#include <unistd.h>
+
+#include "base/basictypes.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/time/time.h"
+
+namespace base {
+
+//static
+const Time CurrentProcessInfo::CreationTime() {
+ int mib[] = { CTL_KERN, KERN_PROC, KERN_PROC_PID, getpid() };
+ size_t len = 0;
+ if (sysctl(mib, arraysize(mib), NULL, &len, NULL, 0) < 0)
+ return Time();
+
+ scoped_ptr_malloc<struct kinfo_proc>
+ proc(static_cast<struct kinfo_proc*>(malloc(len)));
+ if (sysctl(mib, arraysize(mib), proc.get(), &len, NULL, 0) < 0)
+ return Time();
+ return Time::FromTimeVal(proc->kp_proc.p_un.__p_starttime);
+}
+
+} // namespace base
diff --git a/chromium/base/process/process_info_win.cc b/chromium/base/process/process_info_win.cc
new file mode 100644
index 00000000000..b930ae6dd88
--- /dev/null
+++ b/chromium/base/process/process_info_win.cc
@@ -0,0 +1,25 @@
+// 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/process_info.h"
+
+#include <windows.h>
+
+#include "base/basictypes.h"
+#include "base/time/time.h"
+
+namespace base {
+
+//static
+const Time CurrentProcessInfo::CreationTime() {
+ FILETIME creation_time = {};
+ FILETIME ignore = {};
+ if (::GetProcessTimes(::GetCurrentProcess(), &creation_time, &ignore,
+ &ignore, &ignore) == false)
+ return Time();
+
+ return Time::FromFileTime(creation_time);
+}
+
+} // namespace base
diff --git a/chromium/base/process/process_iterator.cc b/chromium/base/process/process_iterator.cc
new file mode 100644
index 00000000000..b9ef047732f
--- /dev/null
+++ b/chromium/base/process/process_iterator.cc
@@ -0,0 +1,65 @@
+// 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_iterator.h"
+
+namespace base {
+
+#if defined(OS_POSIX)
+ProcessEntry::ProcessEntry() : pid_(0), ppid_(0), gid_(0) {}
+ProcessEntry::~ProcessEntry() {}
+#endif
+
+const ProcessEntry* ProcessIterator::NextProcessEntry() {
+ bool result = false;
+ do {
+ result = CheckForNextProcess();
+ } while (result && !IncludeEntry());
+ if (result)
+ return &entry_;
+ return NULL;
+}
+
+ProcessIterator::ProcessEntries ProcessIterator::Snapshot() {
+ ProcessEntries found;
+ while (const ProcessEntry* process_entry = NextProcessEntry()) {
+ found.push_back(*process_entry);
+ }
+ return found;
+}
+
+bool ProcessIterator::IncludeEntry() {
+ return !filter_ || filter_->Includes(entry_);
+}
+
+NamedProcessIterator::NamedProcessIterator(
+ const FilePath::StringType& executable_name,
+ const ProcessFilter* filter) : ProcessIterator(filter),
+ executable_name_(executable_name) {
+#if defined(OS_ANDROID)
+ // On Android, the process name contains only the last 15 characters, which
+ // is in file /proc/<pid>/stat, the string between open parenthesis and close
+ // parenthesis. Please See ProcessIterator::CheckForNextProcess for details.
+ // Now if the length of input process name is greater than 15, only save the
+ // last 15 characters.
+ if (executable_name_.size() > 15) {
+ executable_name_ = FilePath::StringType(executable_name_,
+ executable_name_.size() - 15, 15);
+ }
+#endif
+}
+
+NamedProcessIterator::~NamedProcessIterator() {
+}
+
+int GetProcessCount(const FilePath::StringType& executable_name,
+ const ProcessFilter* filter) {
+ int count = 0;
+ NamedProcessIterator iter(executable_name, filter);
+ while (iter.NextProcessEntry())
+ ++count;
+ return count;
+}
+
+} // namespace base
diff --git a/chromium/base/process/process_iterator.h b/chromium/base/process/process_iterator.h
new file mode 100644
index 00000000000..aa8ba74aba0
--- /dev/null
+++ b/chromium/base/process/process_iterator.h
@@ -0,0 +1,183 @@
+// 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 methods to iterate over processes on the system.
+
+#ifndef BASE_PROCESS_PROCESS_ITERATOR_H_
+#define BASE_PROCESS_PROCESS_ITERATOR_H_
+
+#include <list>
+#include <string>
+#include <vector>
+
+#include "base/base_export.h"
+#include "base/basictypes.h"
+#include "base/files/file_path.h"
+#include "base/process/process.h"
+#include "build/build_config.h"
+
+#if defined(OS_WIN)
+#include <windows.h>
+#include <tlhelp32.h>
+#elif defined(OS_MACOSX) || defined(OS_BSD)
+#include <sys/sysctl.h>
+#elif defined(OS_POSIX)
+#include <dirent.h>
+#endif
+
+namespace base {
+
+#if defined(OS_WIN)
+struct ProcessEntry : public PROCESSENTRY32 {
+ ProcessId pid() const { return th32ProcessID; }
+ ProcessId parent_pid() const { return th32ParentProcessID; }
+ const wchar_t* exe_file() const { return szExeFile; }
+};
+
+// 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
+// the specific semantics of each mask value.
+const uint32 kProcessAccessTerminate = PROCESS_TERMINATE;
+const uint32 kProcessAccessCreateThread = PROCESS_CREATE_THREAD;
+const uint32 kProcessAccessSetSessionId = PROCESS_SET_SESSIONID;
+const uint32 kProcessAccessVMOperation = PROCESS_VM_OPERATION;
+const uint32 kProcessAccessVMRead = PROCESS_VM_READ;
+const uint32 kProcessAccessVMWrite = PROCESS_VM_WRITE;
+const uint32 kProcessAccessDuplicateHandle = PROCESS_DUP_HANDLE;
+const uint32 kProcessAccessCreateProcess = PROCESS_CREATE_PROCESS;
+const uint32 kProcessAccessSetQuota = PROCESS_SET_QUOTA;
+const uint32 kProcessAccessSetInformation = PROCESS_SET_INFORMATION;
+const uint32 kProcessAccessQueryInformation = PROCESS_QUERY_INFORMATION;
+const uint32 kProcessAccessSuspendResume = PROCESS_SUSPEND_RESUME;
+const uint32 kProcessAccessQueryLimitedInfomation =
+ PROCESS_QUERY_LIMITED_INFORMATION;
+const uint32 kProcessAccessWaitForTermination = SYNCHRONIZE;
+#elif defined(OS_POSIX)
+struct BASE_EXPORT ProcessEntry {
+ ProcessEntry();
+ ~ProcessEntry();
+
+ ProcessId pid() const { return pid_; }
+ ProcessId parent_pid() const { return ppid_; }
+ ProcessId gid() const { return gid_; }
+ const char* exe_file() const { return exe_file_.c_str(); }
+ const std::vector<std::string>& cmd_line_args() const {
+ return cmd_line_args_;
+ }
+
+ ProcessId pid_;
+ ProcessId ppid_;
+ ProcessId gid_;
+ std::string exe_file_;
+ std::vector<std::string> cmd_line_args_;
+};
+
+// Process access masks. They are not used on Posix because access checking
+// does not happen during handle creation.
+const uint32 kProcessAccessTerminate = 0;
+const uint32 kProcessAccessCreateThread = 0;
+const uint32 kProcessAccessSetSessionId = 0;
+const uint32 kProcessAccessVMOperation = 0;
+const uint32 kProcessAccessVMRead = 0;
+const uint32 kProcessAccessVMWrite = 0;
+const uint32 kProcessAccessDuplicateHandle = 0;
+const uint32 kProcessAccessCreateProcess = 0;
+const uint32 kProcessAccessSetQuota = 0;
+const uint32 kProcessAccessSetInformation = 0;
+const uint32 kProcessAccessQueryInformation = 0;
+const uint32 kProcessAccessSuspendResume = 0;
+const uint32 kProcessAccessQueryLimitedInfomation = 0;
+const uint32 kProcessAccessWaitForTermination = 0;
+#endif // defined(OS_POSIX)
+
+// Used to filter processes by process ID.
+class ProcessFilter {
+ public:
+ // Returns true to indicate set-inclusion and false otherwise. This method
+ // should not have side-effects and should be idempotent.
+ virtual bool Includes(const ProcessEntry& entry) const = 0;
+
+ protected:
+ virtual ~ProcessFilter() {}
+};
+
+// This class provides a way to iterate through a list of processes on the
+// current machine with a specified filter.
+// To use, create an instance and then call NextProcessEntry() until it returns
+// false.
+class BASE_EXPORT ProcessIterator {
+ public:
+ typedef std::list<ProcessEntry> ProcessEntries;
+
+ explicit ProcessIterator(const ProcessFilter* filter);
+ virtual ~ProcessIterator();
+
+ // If there's another process that matches the given executable name,
+ // returns a const pointer to the corresponding PROCESSENTRY32.
+ // If there are no more matching processes, returns NULL.
+ // The returned pointer will remain valid until NextProcessEntry()
+ // is called again or this NamedProcessIterator goes out of scope.
+ const ProcessEntry* NextProcessEntry();
+
+ // Takes a snapshot of all the ProcessEntry found.
+ ProcessEntries Snapshot();
+
+ protected:
+ virtual bool IncludeEntry();
+ const ProcessEntry& entry() { return entry_; }
+
+ private:
+ // Determines whether there's another process (regardless of executable)
+ // left in the list of all processes. Returns true and sets entry_ to
+ // that process's info if there is one, false otherwise.
+ bool CheckForNextProcess();
+
+ // Initializes a PROCESSENTRY32 data structure so that it's ready for
+ // use with Process32First/Process32Next.
+ void InitProcessEntry(ProcessEntry* entry);
+
+#if defined(OS_WIN)
+ HANDLE snapshot_;
+ bool started_iteration_;
+#elif defined(OS_MACOSX) || defined(OS_BSD)
+ std::vector<kinfo_proc> kinfo_procs_;
+ size_t index_of_kinfo_proc_;
+#elif defined(OS_POSIX)
+ DIR* procfs_dir_;
+#endif
+ ProcessEntry entry_;
+ const ProcessFilter* filter_;
+
+ DISALLOW_COPY_AND_ASSIGN(ProcessIterator);
+};
+
+// This class provides a way to iterate through the list of processes
+// on the current machine that were started from the given executable
+// name. To use, create an instance and then call NextProcessEntry()
+// until it returns false.
+class BASE_EXPORT NamedProcessIterator : public ProcessIterator {
+ public:
+ NamedProcessIterator(const FilePath::StringType& executable_name,
+ const ProcessFilter* filter);
+ virtual ~NamedProcessIterator();
+
+ protected:
+ virtual bool IncludeEntry() OVERRIDE;
+
+ private:
+ FilePath::StringType executable_name_;
+
+ DISALLOW_COPY_AND_ASSIGN(NamedProcessIterator);
+};
+
+// Returns the number of processes on the machine that are running from the
+// given executable name. If filter is non-null, then only processes selected
+// by the filter will be counted.
+BASE_EXPORT int GetProcessCount(const FilePath::StringType& executable_name,
+ const ProcessFilter* filter);
+
+} // namespace base
+
+#endif // BASE_PROCESS_PROCESS_ITERATOR_H_
diff --git a/chromium/base/process/process_iterator_freebsd.cc b/chromium/base/process/process_iterator_freebsd.cc
new file mode 100644
index 00000000000..e8225b0054e
--- /dev/null
+++ b/chromium/base/process/process_iterator_freebsd.cc
@@ -0,0 +1,124 @@
+// 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_iterator.h"
+
+#include <sys/sysctl.h>
+
+#include "base/logging.h"
+#include "base/strings/string_util.h"
+
+namespace base {
+
+ProcessIterator::ProcessIterator(const ProcessFilter* filter)
+ : index_of_kinfo_proc_(),
+ filter_(filter) {
+
+ int mib[] = { CTL_KERN, KERN_PROC, KERN_PROC_UID, getuid() };
+
+ bool done = false;
+ int try_num = 1;
+ const int max_tries = 10;
+
+ do {
+ size_t len = 0;
+ if (sysctl(mib, arraysize(mib), NULL, &len, NULL, 0) < 0) {
+ LOG(ERROR) << "failed to get the size needed for the process list";
+ kinfo_procs_.resize(0);
+ done = true;
+ } else {
+ size_t num_of_kinfo_proc = len / sizeof(struct kinfo_proc);
+ // Leave some spare room for process table growth (more could show up
+ // between when we check and now)
+ num_of_kinfo_proc += 16;
+ kinfo_procs_.resize(num_of_kinfo_proc);
+ len = num_of_kinfo_proc * sizeof(struct kinfo_proc);
+ if (sysctl(mib, arraysize(mib), &kinfo_procs_[0], &len, NULL, 0) <0) {
+ // If we get a mem error, it just means we need a bigger buffer, so
+ // loop around again. Anything else is a real error and give up.
+ if (errno != ENOMEM) {
+ LOG(ERROR) << "failed to get the process list";
+ kinfo_procs_.resize(0);
+ done = true;
+ }
+ } else {
+ // Got the list, just make sure we're sized exactly right
+ size_t num_of_kinfo_proc = len / sizeof(struct kinfo_proc);
+ kinfo_procs_.resize(num_of_kinfo_proc);
+ done = true;
+ }
+ }
+ } while (!done && (try_num++ < max_tries));
+
+ if (!done) {
+ LOG(ERROR) << "failed to collect the process list in a few tries";
+ kinfo_procs_.resize(0);
+ }
+}
+
+ProcessIterator::~ProcessIterator() {
+}
+
+bool ProcessIterator::CheckForNextProcess() {
+ std::string data;
+
+ for (; index_of_kinfo_proc_ < kinfo_procs_.size(); ++index_of_kinfo_proc_) {
+ size_t length;
+ struct kinfo_proc kinfo = kinfo_procs_[index_of_kinfo_proc_];
+ int mib[] = { CTL_KERN, KERN_PROC_ARGS, kinfo.ki_pid };
+
+ if ((kinfo.ki_pid > 0) && (kinfo.ki_stat == SZOMB))
+ continue;
+
+ length = 0;
+ if (sysctl(mib, arraysize(mib), NULL, &length, NULL, 0) < 0) {
+ LOG(ERROR) << "failed to figure out the buffer size for a command line";
+ continue;
+ }
+
+ data.resize(length);
+
+ if (sysctl(mib, arraysize(mib), &data[0], &length, NULL, 0) < 0) {
+ LOG(ERROR) << "failed to fetch a commandline";
+ continue;
+ }
+
+ std::string delimiters;
+ delimiters.push_back('\0');
+ Tokenize(data, delimiters, &entry_.cmd_line_args_);
+
+ size_t exec_name_end = data.find('\0');
+ if (exec_name_end == std::string::npos) {
+ LOG(ERROR) << "command line data didn't match expected format";
+ continue;
+ }
+
+ entry_.pid_ = kinfo.ki_pid;
+ entry_.ppid_ = kinfo.ki_ppid;
+ entry_.gid_ = kinfo.ki_pgid;
+
+ size_t last_slash = data.rfind('/', exec_name_end);
+ if (last_slash == std::string::npos) {
+ entry_.exe_file_.assign(data, 0, exec_name_end);
+ } else {
+ entry_.exe_file_.assign(data, last_slash + 1,
+ exec_name_end - last_slash - 1);
+ }
+
+ // Start w/ the next entry next time through
+ ++index_of_kinfo_proc_;
+
+ return true;
+ }
+ return false;
+}
+
+bool NamedProcessIterator::IncludeEntry() {
+ if (executable_name_ != entry().exe_file())
+ return false;
+
+ return ProcessIterator::IncludeEntry();
+}
+
+} // namespace base
diff --git a/chromium/base/process/process_iterator_linux.cc b/chromium/base/process/process_iterator_linux.cc
new file mode 100644
index 00000000000..6da51ca3b55
--- /dev/null
+++ b/chromium/base/process/process_iterator_linux.cc
@@ -0,0 +1,137 @@
+// 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_iterator.h"
+
+#include "base/file_util.h"
+#include "base/logging.h"
+#include "base/process/internal_linux.h"
+#include "base/strings/string_util.h"
+#include "base/threading/thread_restrictions.h"
+
+namespace base {
+
+namespace {
+
+// 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,
+ internal::ProcStatsFields field_num) {
+ if (field_num < internal::VM_COMM || field_num > internal::VM_STATE) {
+ NOTREACHED();
+ return std::string();
+ }
+
+ if (proc_stats.size() > static_cast<size_t>(field_num))
+ return proc_stats[field_num];
+
+ NOTREACHED();
+ return 0;
+}
+
+// Reads /proc/<pid>/cmdline and populates |proc_cmd_line_args| with the command
+// line arguments. Returns true if successful.
+// Note: /proc/<pid>/cmdline contains command line arguments separated by single
+// null characters. We tokenize it into a vector of strings using '\0' as a
+// delimiter.
+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 = internal::GetProcPidDir(pid).Append("cmdline");
+ std::string cmd_line;
+ if (!file_util::ReadFileToString(cmd_line_file, &cmd_line))
+ return false;
+ std::string delimiters;
+ delimiters.push_back('\0');
+ Tokenize(cmd_line, delimiters, proc_cmd_line_args);
+ return true;
+}
+
+} // namespace
+
+ProcessIterator::ProcessIterator(const ProcessFilter* filter)
+ : filter_(filter) {
+ procfs_dir_ = opendir(internal::kProcDir);
+}
+
+ProcessIterator::~ProcessIterator() {
+ if (procfs_dir_) {
+ closedir(procfs_dir_);
+ procfs_dir_ = NULL;
+ }
+}
+
+bool ProcessIterator::CheckForNextProcess() {
+ // TODO(port): skip processes owned by different UID
+
+ pid_t pid = kNullProcessId;
+ std::vector<std::string> cmd_line_args;
+ std::string stats_data;
+ std::vector<std::string> proc_stats;
+
+ // Arbitrarily guess that there will never be more than 200 non-process
+ // files in /proc. Hardy has 53 and Lucid has 61.
+ int skipped = 0;
+ const int kSkipLimit = 200;
+ while (skipped < kSkipLimit) {
+ dirent* slot = readdir(procfs_dir_);
+ // all done looking through /proc?
+ if (!slot)
+ return false;
+
+ // If not a process, keep looking for one.
+ pid = internal::ProcDirSlotToPid(slot->d_name);
+ if (!pid) {
+ skipped++;
+ continue;
+ }
+
+ if (!GetProcCmdline(pid, &cmd_line_args))
+ continue;
+
+ if (!internal::ReadProcStats(pid, &stats_data))
+ continue;
+ if (!internal::ParseProcStats(stats_data, &proc_stats))
+ continue;
+
+ std::string runstate =
+ GetProcStatsFieldAsString(proc_stats, internal::VM_STATE);
+ if (runstate.size() != 1) {
+ NOTREACHED();
+ continue;
+ }
+
+ // Is the process in 'Zombie' state, i.e. dead but waiting to be reaped?
+ // Allowed values: D R S T Z
+ if (runstate[0] != 'Z')
+ break;
+
+ // Nope, it's a zombie; somebody isn't cleaning up after their children.
+ // (e.g. WaitForProcessesToExit doesn't clean up after dead children yet.)
+ // There could be a lot of zombies, can't really decrement i here.
+ }
+ if (skipped >= kSkipLimit) {
+ NOTREACHED();
+ return false;
+ }
+
+ entry_.pid_ = pid;
+ 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;
+}
+
+bool NamedProcessIterator::IncludeEntry() {
+ if (executable_name_ != entry().exe_file())
+ return false;
+ return ProcessIterator::IncludeEntry();
+}
+
+} // namespace base
diff --git a/chromium/base/process/process_iterator_mac.cc b/chromium/base/process/process_iterator_mac.cc
new file mode 100644
index 00000000000..e35c2ae19ba
--- /dev/null
+++ b/chromium/base/process/process_iterator_mac.cc
@@ -0,0 +1,135 @@
+// 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_iterator.h"
+
+#include <errno.h>
+#include <sys/sysctl.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include "base/logging.h"
+#include "base/strings/string_util.h"
+
+namespace base {
+
+ProcessIterator::ProcessIterator(const ProcessFilter* filter)
+ : index_of_kinfo_proc_(0),
+ filter_(filter) {
+ // Get a snapshot of all of my processes (yes, as we loop it can go stale, but
+ // but trying to find where we were in a constantly changing list is basically
+ // impossible.
+
+ int mib[] = { CTL_KERN, KERN_PROC, KERN_PROC_UID, geteuid() };
+
+ // Since more processes could start between when we get the size and when
+ // we get the list, we do a loop to keep trying until we get it.
+ bool done = false;
+ int try_num = 1;
+ const int max_tries = 10;
+ do {
+ // Get the size of the buffer
+ size_t len = 0;
+ if (sysctl(mib, arraysize(mib), NULL, &len, NULL, 0) < 0) {
+ DLOG(ERROR) << "failed to get the size needed for the process list";
+ kinfo_procs_.resize(0);
+ done = true;
+ } else {
+ size_t num_of_kinfo_proc = len / sizeof(struct kinfo_proc);
+ // Leave some spare room for process table growth (more could show up
+ // between when we check and now)
+ num_of_kinfo_proc += 16;
+ kinfo_procs_.resize(num_of_kinfo_proc);
+ len = num_of_kinfo_proc * sizeof(struct kinfo_proc);
+ // Load the list of processes
+ if (sysctl(mib, arraysize(mib), &kinfo_procs_[0], &len, NULL, 0) < 0) {
+ // If we get a mem error, it just means we need a bigger buffer, so
+ // loop around again. Anything else is a real error and give up.
+ if (errno != ENOMEM) {
+ DLOG(ERROR) << "failed to get the process list";
+ kinfo_procs_.resize(0);
+ done = true;
+ }
+ } else {
+ // Got the list, just make sure we're sized exactly right
+ size_t num_of_kinfo_proc = len / sizeof(struct kinfo_proc);
+ kinfo_procs_.resize(num_of_kinfo_proc);
+ done = true;
+ }
+ }
+ } while (!done && (try_num++ < max_tries));
+
+ if (!done) {
+ DLOG(ERROR) << "failed to collect the process list in a few tries";
+ kinfo_procs_.resize(0);
+ }
+}
+
+ProcessIterator::~ProcessIterator() {
+}
+
+bool ProcessIterator::CheckForNextProcess() {
+ std::string data;
+ for (; index_of_kinfo_proc_ < kinfo_procs_.size(); ++index_of_kinfo_proc_) {
+ kinfo_proc& kinfo = kinfo_procs_[index_of_kinfo_proc_];
+
+ // Skip processes just awaiting collection
+ if ((kinfo.kp_proc.p_pid > 0) && (kinfo.kp_proc.p_stat == SZOMB))
+ continue;
+
+ int mib[] = { CTL_KERN, KERN_PROCARGS, kinfo.kp_proc.p_pid };
+
+ // Find out what size buffer we need.
+ size_t data_len = 0;
+ if (sysctl(mib, arraysize(mib), NULL, &data_len, NULL, 0) < 0) {
+ DVPLOG(1) << "failed to figure out the buffer size for a commandline";
+ continue;
+ }
+
+ data.resize(data_len);
+ if (sysctl(mib, arraysize(mib), &data[0], &data_len, NULL, 0) < 0) {
+ DVPLOG(1) << "failed to fetch a commandline";
+ continue;
+ }
+
+ // |data| contains all the command line parameters of the process, separated
+ // by blocks of one or more null characters. We tokenize |data| into a
+ // vector of strings using '\0' as a delimiter and populate
+ // |entry_.cmd_line_args_|.
+ std::string delimiters;
+ delimiters.push_back('\0');
+ Tokenize(data, delimiters, &entry_.cmd_line_args_);
+
+ // |data| starts with the full executable path followed by a null character.
+ // We search for the first instance of '\0' and extract everything before it
+ // to populate |entry_.exe_file_|.
+ size_t exec_name_end = data.find('\0');
+ if (exec_name_end == std::string::npos) {
+ DLOG(ERROR) << "command line data didn't match expected format";
+ continue;
+ }
+
+ entry_.pid_ = kinfo.kp_proc.p_pid;
+ entry_.ppid_ = kinfo.kp_eproc.e_ppid;
+ entry_.gid_ = kinfo.kp_eproc.e_pgid;
+ size_t last_slash = data.rfind('/', exec_name_end);
+ if (last_slash == std::string::npos)
+ entry_.exe_file_.assign(data, 0, exec_name_end);
+ else
+ entry_.exe_file_.assign(data, last_slash + 1,
+ exec_name_end - last_slash - 1);
+ // Start w/ the next entry next time through
+ ++index_of_kinfo_proc_;
+ // Done
+ return true;
+ }
+ return false;
+}
+
+bool NamedProcessIterator::IncludeEntry() {
+ return (executable_name_ == entry().exe_file() &&
+ ProcessIterator::IncludeEntry());
+}
+
+} // namespace base
diff --git a/chromium/base/process/process_iterator_openbsd.cc b/chromium/base/process/process_iterator_openbsd.cc
new file mode 100644
index 00000000000..7c44eb118c0
--- /dev/null
+++ b/chromium/base/process/process_iterator_openbsd.cc
@@ -0,0 +1,128 @@
+// 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_iterator.h"
+
+#include <errno.h>
+#include <sys/sysctl.h>
+
+#include "base/logging.h"
+#include "base/strings/string_util.h"
+
+namespace base {
+
+ProcessIterator::ProcessIterator(const ProcessFilter* filter)
+ : index_of_kinfo_proc_(),
+ filter_(filter) {
+
+ int mib[] = { CTL_KERN, KERN_PROC, KERN_PROC_UID, getuid(),
+ sizeof(struct kinfo_proc), 0 };
+
+ bool done = false;
+ int try_num = 1;
+ const int max_tries = 10;
+
+ do {
+ size_t len = 0;
+ if (sysctl(mib, arraysize(mib), NULL, &len, NULL, 0) < 0) {
+ DLOG(ERROR) << "failed to get the size needed for the process list";
+ kinfo_procs_.resize(0);
+ done = true;
+ } else {
+ size_t num_of_kinfo_proc = len / sizeof(struct kinfo_proc);
+ // Leave some spare room for process table growth (more could show up
+ // between when we check and now)
+ num_of_kinfo_proc += 16;
+ kinfo_procs_.resize(num_of_kinfo_proc);
+ len = num_of_kinfo_proc * sizeof(struct kinfo_proc);
+ if (sysctl(mib, arraysize(mib), &kinfo_procs_[0], &len, NULL, 0) < 0) {
+ // If we get a mem error, it just means we need a bigger buffer, so
+ // loop around again. Anything else is a real error and give up.
+ if (errno != ENOMEM) {
+ DLOG(ERROR) << "failed to get the process list";
+ kinfo_procs_.resize(0);
+ done = true;
+ }
+ } else {
+ // Got the list, just make sure we're sized exactly right
+ size_t num_of_kinfo_proc = len / sizeof(struct kinfo_proc);
+ kinfo_procs_.resize(num_of_kinfo_proc);
+ done = true;
+ }
+ }
+ } while (!done && (try_num++ < max_tries));
+
+ if (!done) {
+ DLOG(ERROR) << "failed to collect the process list in a few tries";
+ kinfo_procs_.resize(0);
+ }
+}
+
+ProcessIterator::~ProcessIterator() {
+}
+
+bool ProcessIterator::CheckForNextProcess() {
+ std::string data;
+ for (; index_of_kinfo_proc_ < kinfo_procs_.size(); ++index_of_kinfo_proc_) {
+ kinfo_proc& kinfo = kinfo_procs_[index_of_kinfo_proc_];
+
+ // Skip processes just awaiting collection
+ if ((kinfo.p_pid > 0) && (kinfo.p_stat == SZOMB))
+ continue;
+
+ int mib[] = { CTL_KERN, KERN_PROC_ARGS, kinfo.p_pid };
+
+ // Find out what size buffer we need.
+ size_t data_len = 0;
+ if (sysctl(mib, arraysize(mib), NULL, &data_len, NULL, 0) < 0) {
+ DVPLOG(1) << "failed to figure out the buffer size for a commandline";
+ continue;
+ }
+
+ data.resize(data_len);
+ if (sysctl(mib, arraysize(mib), &data[0], &data_len, NULL, 0) < 0) {
+ DVPLOG(1) << "failed to fetch a commandline";
+ continue;
+ }
+
+ // |data| contains all the command line parameters of the process, separated
+ // by blocks of one or more null characters. We tokenize |data| into a
+ // vector of strings using '\0' as a delimiter and populate
+ // |entry_.cmd_line_args_|.
+ std::string delimiters;
+ delimiters.push_back('\0');
+ Tokenize(data, delimiters, &entry_.cmd_line_args_);
+
+ // |data| starts with the full executable path followed by a null character.
+ // We search for the first instance of '\0' and extract everything before it
+ // to populate |entry_.exe_file_|.
+ size_t exec_name_end = data.find('\0');
+ if (exec_name_end == std::string::npos) {
+ DLOG(ERROR) << "command line data didn't match expected format";
+ continue;
+ }
+
+ entry_.pid_ = kinfo.p_pid;
+ entry_.ppid_ = kinfo.p_ppid;
+ entry_.gid_ = kinfo.p__pgid;
+ size_t last_slash = data.rfind('/', exec_name_end);
+ if (last_slash == std::string::npos)
+ entry_.exe_file_.assign(data, 0, exec_name_end);
+ else
+ entry_.exe_file_.assign(data, last_slash + 1,
+ exec_name_end - last_slash - 1);
+ // Start w/ the next entry next time through
+ ++index_of_kinfo_proc_;
+ // Done
+ return true;
+ }
+ return false;
+}
+
+bool NamedProcessIterator::IncludeEntry() {
+ return (executable_name_ == entry().exe_file() &&
+ ProcessIterator::IncludeEntry());
+}
+
+} // namespace base
diff --git a/chromium/base/process/process_iterator_win.cc b/chromium/base/process/process_iterator_win.cc
new file mode 100644
index 00000000000..9d5a970ef48
--- /dev/null
+++ b/chromium/base/process/process_iterator_win.cc
@@ -0,0 +1,41 @@
+// 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_iterator.h"
+
+namespace base {
+
+ProcessIterator::ProcessIterator(const ProcessFilter* filter)
+ : started_iteration_(false),
+ filter_(filter) {
+ snapshot_ = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
+}
+
+ProcessIterator::~ProcessIterator() {
+ CloseHandle(snapshot_);
+}
+
+bool ProcessIterator::CheckForNextProcess() {
+ InitProcessEntry(&entry_);
+
+ if (!started_iteration_) {
+ started_iteration_ = true;
+ return !!Process32First(snapshot_, &entry_);
+ }
+
+ return !!Process32Next(snapshot_, &entry_);
+}
+
+void ProcessIterator::InitProcessEntry(ProcessEntry* entry) {
+ memset(entry, 0, sizeof(*entry));
+ entry->dwSize = sizeof(*entry);
+}
+
+bool NamedProcessIterator::IncludeEntry() {
+ // Case insensitive.
+ return _wcsicmp(executable_name_.c_str(), entry().exe_file()) == 0 &&
+ ProcessIterator::IncludeEntry();
+}
+
+} // namespace base
diff --git a/chromium/base/process/process_linux.cc b/chromium/base/process/process_linux.cc
new file mode 100644
index 00000000000..93006aafe59
--- /dev/null
+++ b/chromium/base/process/process_linux.cc
@@ -0,0 +1,137 @@
+// Copyright (c) 2011 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.h"
+
+#include <errno.h>
+#include <sys/resource.h>
+
+#include "base/file_util.h"
+#include "base/lazy_instance.h"
+#include "base/logging.h"
+#include "base/strings/string_split.h"
+#include "base/strings/stringprintf.h"
+#include "base/synchronization/lock.h"
+
+namespace {
+const int kForegroundPriority = 0;
+
+#if defined(OS_CHROMEOS)
+// We are more aggressive in our lowering of background process priority
+// for chromeos as we have much more control over other processes running
+// on the machine.
+//
+// TODO(davemoore) Refactor this by adding support for higher levels to set
+// the foregrounding / backgrounding process so we don't have to keep
+// chrome / chromeos specific logic here.
+const int kBackgroundPriority = 19;
+const char kControlPath[] = "/sys/fs/cgroup/cpu%s/cgroup.procs";
+const char kForeground[] = "/chrome_renderers/foreground";
+const char kBackground[] = "/chrome_renderers/background";
+const char kProcPath[] = "/proc/%d/cgroup";
+
+struct CGroups {
+ // Check for cgroups files. ChromeOS supports these by default. It creates
+ // a cgroup mount in /sys/fs/cgroup and then configures two cpu task groups,
+ // one contains at most a single foreground renderer and the other contains
+ // all background renderers. This allows us to limit the impact of background
+ // renderers on foreground ones to a greater level than simple renicing.
+ bool enabled;
+ base::FilePath foreground_file;
+ base::FilePath background_file;
+
+ CGroups() {
+ foreground_file =
+ base::FilePath(base::StringPrintf(kControlPath, kForeground));
+ background_file =
+ base::FilePath(base::StringPrintf(kControlPath, kBackground));
+ file_util::FileSystemType foreground_type;
+ file_util::FileSystemType background_type;
+ enabled =
+ file_util::GetFileSystemType(foreground_file, &foreground_type) &&
+ file_util::GetFileSystemType(background_file, &background_type) &&
+ foreground_type == file_util::FILE_SYSTEM_CGROUP &&
+ background_type == file_util::FILE_SYSTEM_CGROUP;
+ }
+};
+
+base::LazyInstance<CGroups> cgroups = LAZY_INSTANCE_INITIALIZER;
+#else
+const int kBackgroundPriority = 5;
+#endif
+}
+
+namespace base {
+
+bool Process::IsProcessBackgrounded() const {
+ DCHECK(process_);
+
+#if defined(OS_CHROMEOS)
+ if (cgroups.Get().enabled) {
+ std::string proc;
+ if (file_util::ReadFileToString(
+ base::FilePath(StringPrintf(kProcPath, process_)),
+ &proc)) {
+ std::vector<std::string> proc_parts;
+ base::SplitString(proc, ':', &proc_parts);
+ DCHECK(proc_parts.size() == 3);
+ bool ret = proc_parts[2] == std::string(kBackground);
+ return ret;
+ } else {
+ return false;
+ }
+ }
+#endif
+ return GetPriority() == kBackgroundPriority;
+}
+
+bool Process::SetProcessBackgrounded(bool background) {
+ DCHECK(process_);
+
+#if defined(OS_CHROMEOS)
+ if (cgroups.Get().enabled) {
+ std::string pid = StringPrintf("%d", process_);
+ const base::FilePath file =
+ background ?
+ cgroups.Get().background_file : cgroups.Get().foreground_file;
+ return file_util::WriteFile(file, pid.c_str(), pid.size()) > 0;
+ }
+#endif // OS_CHROMEOS
+
+ if (!CanBackgroundProcesses())
+ return false;
+
+ int priority = background ? kBackgroundPriority : kForegroundPriority;
+ int result = setpriority(PRIO_PROCESS, process_, priority);
+ DPCHECK(result == 0);
+ return result == 0;
+}
+
+struct CheckForNicePermission {
+ CheckForNicePermission() : can_reraise_priority(false) {
+ // We won't be able to raise the priority if we don't have the right rlimit.
+ // The limit may be adjusted in /etc/security/limits.conf for PAM systems.
+ struct rlimit rlim;
+ if ((getrlimit(RLIMIT_NICE, &rlim) == 0) &&
+ (20 - kForegroundPriority) <= static_cast<int>(rlim.rlim_cur)) {
+ can_reraise_priority = true;
+ }
+ };
+
+ bool can_reraise_priority;
+};
+
+// static
+bool Process::CanBackgroundProcesses() {
+#if defined(OS_CHROMEOS)
+ if (cgroups.Get().enabled)
+ return true;
+#endif
+
+ static LazyInstance<CheckForNicePermission> check_for_nice_permission =
+ LAZY_INSTANCE_INITIALIZER;
+ return check_for_nice_permission.Get().can_reraise_priority;
+}
+
+} // namespace base
diff --git a/chromium/base/process/process_metrics.h b/chromium/base/process/process_metrics.h
new file mode 100644
index 00000000000..e329b4ef73b
--- /dev/null
+++ b/chromium/base/process/process_metrics.h
@@ -0,0 +1,269 @@
+// 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/process_handle.h"
+#include "base/time/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 ChromeOS:
+// priv: Pages mapped only by this process.
+// shared: PSS or 0 if the kernel doesn't support this.
+// shareable: 0
+// swapped Pages swapped out to zram.
+//
+// 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;
+#if defined(OS_CHROMEOS)
+ size_t swapped;
+#endif
+};
+
+// 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)
+
+#if defined(OS_LINUX) || defined(OS_ANDROID)
+ bool GetWorkingSetKBytesStatm(WorkingSetKBytes* ws_usage) const;
+#endif
+
+#if defined(OS_CHROMEOS)
+ bool GetWorkingSetKBytesTotmaps(WorkingSetKBytes *ws_usage) const;
+#endif
+
+ 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)
+
+#if defined(OS_LINUX) || defined(OS_ANDROID)
+// 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.
+// Returns 0 on failure.
+BASE_EXPORT int GetNumberOfThreads(ProcessHandle process);
+
+// /proc/self/exe refers to the current executable.
+BASE_EXPORT extern const char kProcSelfExe[];
+#endif // defined(OS_LINUX) || defined(OS_ANDROID)
+
+#if defined(OS_POSIX)
+// Returns the maximum number of file descriptors that can be open by a process
+// at once. If the number is unavailable, a conservative best guess is returned.
+size_t GetMaxFds();
+#endif // defined(OS_POSIX)
+
+} // namespace base
+
+#endif // BASE_PROCESS_PROCESS_METRICS_H_
diff --git a/chromium/base/process/process_metrics_freebsd.cc b/chromium/base/process/process_metrics_freebsd.cc
new file mode 100644
index 00000000000..019454cd81a
--- /dev/null
+++ b/chromium/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/chromium/base/process/process_metrics_ios.cc b/chromium/base/process/process_metrics_ios.cc
new file mode 100644
index 00000000000..94c671901b6
--- /dev/null
+++ b/chromium/base/process/process_metrics_ios.cc
@@ -0,0 +1,64 @@
+// 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;
+}
+
+size_t GetMaxFds() {
+ static const rlim_t kSystemDefaultMaxFds = 256;
+ rlim_t max_fds;
+ struct rlimit nofile;
+ if (getrlimit(RLIMIT_NOFILE, &nofile)) {
+ // Error case: Take a best guess.
+ max_fds = kSystemDefaultMaxFds;
+ } else {
+ max_fds = nofile.rlim_cur;
+ }
+
+ if (max_fds > INT_MAX)
+ max_fds = INT_MAX;
+
+ return static_cast<size_t>(max_fds);
+}
+
+} // namespace base
diff --git a/chromium/base/process/process_metrics_linux.cc b/chromium/base/process/process_metrics_linux.cc
new file mode 100644
index 00000000000..1c86ee467c9
--- /dev/null
+++ b/chromium/base/process/process_metrics_linux.cc
@@ -0,0 +1,516 @@
+// 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 <fcntl.h>
+#include <sys/stat.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/strings/string_number_conversions.h"
+#include "base/strings/string_split.h"
+#include "base/strings/string_tokenizer.h"
+#include "base/strings/string_util.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;
+}
+
+bool ProcessMetrics::GetWorkingSetKBytes(WorkingSetKBytes* ws_usage) const {
+#if defined(OS_CHROMEOS)
+ if (GetWorkingSetKBytesTotmaps(ws_usage))
+ return true;
+#endif
+ return GetWorkingSetKBytesStatm(ws_usage);
+}
+
+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_);
+
+ // 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.
+ TimeDelta cpu_time = internal::ClockTicksToTimeDelta(cpu);
+ TimeDelta last_cpu_time = internal::ClockTicksToTimeDelta(last_cpu_);
+ int percentage = 100 * (cpu_time - last_cpu_time).InSecondsF() /
+ 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();
+}
+
+#if defined(OS_CHROMEOS)
+// Private, Shared and Proportional working set sizes are obtained from
+// /proc/<pid>/totmaps
+bool ProcessMetrics::GetWorkingSetKBytesTotmaps(WorkingSetKBytes *ws_usage)
+ const {
+ // The format of /proc/<pid>/totmaps is:
+ //
+ // Rss: 6120 kB
+ // Pss: 3335 kB
+ // Shared_Clean: 1008 kB
+ // Shared_Dirty: 4012 kB
+ // Private_Clean: 4 kB
+ // Private_Dirty: 1096 kB
+ // Referenced: XXX kB
+ // Anonymous: XXX kB
+ // AnonHugePages: XXX kB
+ // Swap: XXX kB
+ // Locked: XXX kB
+ const size_t kPssIndex = (1 * 3) + 1;
+ const size_t kPrivate_CleanIndex = (4 * 3) + 1;
+ const size_t kPrivate_DirtyIndex = (5 * 3) + 1;
+ const size_t kSwapIndex = (9 * 3) + 1;
+
+ std::string totmaps_data;
+ {
+ FilePath totmaps_file = internal::GetProcPidDir(process_).Append("totmaps");
+ ThreadRestrictions::ScopedAllowIO allow_io;
+ bool ret = file_util::ReadFileToString(totmaps_file, &totmaps_data);
+ if (!ret || totmaps_data.length() == 0)
+ return false;
+ }
+
+ std::vector<std::string> totmaps_fields;
+ SplitStringAlongWhitespace(totmaps_data, &totmaps_fields);
+
+ DCHECK_EQ("Pss:", totmaps_fields[kPssIndex-1]);
+ DCHECK_EQ("Private_Clean:", totmaps_fields[kPrivate_CleanIndex - 1]);
+ DCHECK_EQ("Private_Dirty:", totmaps_fields[kPrivate_DirtyIndex - 1]);
+ DCHECK_EQ("Swap:", totmaps_fields[kSwapIndex-1]);
+
+ int pss = 0;
+ int private_clean = 0;
+ int private_dirty = 0;
+ int swap = 0;
+ bool ret = true;
+ ret &= StringToInt(totmaps_fields[kPssIndex], &pss);
+ ret &= StringToInt(totmaps_fields[kPrivate_CleanIndex], &private_clean);
+ ret &= StringToInt(totmaps_fields[kPrivate_DirtyIndex], &private_dirty);
+ ret &= StringToInt(totmaps_fields[kSwapIndex], &swap);
+
+ // On ChromeOS swap is to zram. We count this as private / shared, as
+ // increased swap decreases available RAM to user processes, which would
+ // otherwise create surprising results.
+ ws_usage->priv = private_clean + private_dirty + swap;
+ ws_usage->shared = pss + swap;
+ ws_usage->shareable = 0;
+ ws_usage->swapped = swap;
+ return ret;
+}
+#endif
+
+// Private and Shared working set sizes are obtained from /proc/<pid>/statm.
+bool ProcessMetrics::GetWorkingSetKBytesStatm(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;
+ bool ret = true;
+ ret &= StringToInt(statm_vec[1], &statm_rss);
+ ret &= 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;
+
+#if defined(OS_CHROMEOS)
+ // Can't get swapped memory from statm.
+ ws_usage->swapped = 0;
+#endif
+
+ return ret;
+}
+
+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;
+}
+
+const char kProcSelfExe[] = "/proc/self/exe";
+
+int GetNumberOfThreads(ProcessHandle process) {
+ return internal::ReadProcStatsAndGetFieldAsInt(process,
+ internal::VM_NUMTHREADS);
+}
+
+} // namespace base
diff --git a/chromium/base/process/process_metrics_mac.cc b/chromium/base/process/process_metrics_mac.cc
new file mode 100644
index 00000000000..048735ed36b
--- /dev/null
+++ b/chromium/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/containers/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/chromium/base/process/process_metrics_openbsd.cc b/chromium/base/process/process_metrics_openbsd.cc
new file mode 100644
index 00000000000..36f607c0a9c
--- /dev/null
+++ b/chromium/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/chromium/base/process/process_metrics_posix.cc b/chromium/base/process/process_metrics_posix.cc
new file mode 100644
index 00000000000..531f6a40d70
--- /dev/null
+++ b/chromium/base/process/process_metrics_posix.cc
@@ -0,0 +1,55 @@
+// 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/resource.h>
+#include <sys/time.h>
+
+#include "base/logging.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() { }
+
+#if defined(OS_LINUX)
+static const rlim_t kSystemDefaultMaxFds = 8192;
+#elif defined(OS_MACOSX)
+static const rlim_t kSystemDefaultMaxFds = 256;
+#elif defined(OS_SOLARIS)
+static const rlim_t kSystemDefaultMaxFds = 8192;
+#elif defined(OS_FREEBSD)
+static const rlim_t kSystemDefaultMaxFds = 8192;
+#elif defined(OS_OPENBSD)
+static const rlim_t kSystemDefaultMaxFds = 256;
+#elif defined(OS_ANDROID)
+static const rlim_t kSystemDefaultMaxFds = 1024;
+#endif
+
+size_t GetMaxFds() {
+ rlim_t max_fds;
+ struct rlimit nofile;
+ if (getrlimit(RLIMIT_NOFILE, &nofile)) {
+ // getrlimit failed. Take a best guess.
+ max_fds = kSystemDefaultMaxFds;
+ RAW_LOG(ERROR, "getrlimit(RLIMIT_NOFILE) failed");
+ } else {
+ max_fds = nofile.rlim_cur;
+ }
+
+ if (max_fds > INT_MAX)
+ max_fds = INT_MAX;
+
+ return static_cast<size_t>(max_fds);
+}
+
+} // namespace base
diff --git a/chromium/base/process/process_metrics_win.cc b/chromium/base/process/process_metrics_win.cc
new file mode 100644
index 00000000000..f42ea86feb7
--- /dev/null
+++ b/chromium/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/chromium/base/process/process_posix.cc b/chromium/base/process/process_posix.cc
new file mode 100644
index 00000000000..3d8b31d0352
--- /dev/null
+++ b/chromium/base/process/process_posix.cc
@@ -0,0 +1,73 @@
+// Copyright (c) 2011 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.h"
+
+#include <sys/resource.h>
+#include <sys/time.h>
+#include <sys/types.h>
+
+#include "base/logging.h"
+#include "base/process/kill.h"
+
+namespace base {
+
+// static
+Process Process::Current() {
+ return Process(GetCurrentProcessHandle());
+}
+
+ProcessId Process::pid() const {
+ if (process_ == 0)
+ return 0;
+
+ return GetProcId(process_);
+}
+
+bool Process::is_current() const {
+ return process_ == GetCurrentProcessHandle();
+}
+
+void Process::Close() {
+ process_ = 0;
+ // if the process wasn't terminated (so we waited) or the state
+ // wasn't already collected w/ a wait from process_utils, we're gonna
+ // end up w/ a zombie when it does finally exit.
+}
+
+void Process::Terminate(int result_code) {
+ // result_code isn't supportable.
+ if (!process_)
+ return;
+ // We don't wait here. It's the responsibility of other code to reap the
+ // child.
+ KillProcess(process_, result_code, false);
+}
+
+#if !defined(OS_LINUX)
+bool Process::IsProcessBackgrounded() const {
+ // See SetProcessBackgrounded().
+ return false;
+}
+
+bool Process::SetProcessBackgrounded(bool value) {
+ // POSIX only allows lowering the priority of a process, so if we
+ // were to lower it we wouldn't be able to raise it back to its initial
+ // priority.
+ return false;
+}
+
+// static
+bool Process::CanBackgroundProcesses() {
+ return false;
+}
+
+#endif
+
+int Process::GetPriority() const {
+ DCHECK(process_);
+ return getpriority(PRIO_PROCESS, process_);
+}
+
+} // namspace base
diff --git a/chromium/base/process/process_util_unittest.cc b/chromium/base/process/process_util_unittest.cc
new file mode 100644
index 00000000000..77f058c8f26
--- /dev/null
+++ b/chromium/base/process/process_util_unittest.cc
@@ -0,0 +1,961 @@
+// 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.
+
+#define _CRT_SECURE_NO_WARNINGS
+
+#include <limits>
+
+#include "base/command_line.h"
+#include "base/debug/alias.h"
+#include "base/debug/stack_trace.h"
+#include "base/files/file_path.h"
+#include "base/logging.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/path_service.h"
+#include "base/posix/eintr_wrapper.h"
+#include "base/process/kill.h"
+#include "base/process/launch.h"
+#include "base/process/memory.h"
+#include "base/process/process.h"
+#include "base/process/process_metrics.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/test/multiprocess_test.h"
+#include "base/test/test_timeouts.h"
+#include "base/third_party/dynamic_annotations/dynamic_annotations.h"
+#include "base/threading/platform_thread.h"
+#include "base/threading/thread.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "testing/multiprocess_func_list.h"
+
+#if defined(OS_LINUX)
+#include <glib.h>
+#include <malloc.h>
+#include <sched.h>
+#endif
+#if defined(OS_POSIX)
+#include <dlfcn.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <sys/resource.h>
+#include <sys/socket.h>
+#include <sys/wait.h>
+#endif
+#if defined(OS_WIN)
+#include <windows.h>
+#endif
+#if defined(OS_MACOSX)
+#include <mach/vm_param.h>
+#include <malloc/malloc.h>
+#endif
+
+using base::FilePath;
+
+namespace {
+
+#if defined(OS_WIN)
+const wchar_t kProcessName[] = L"base_unittests.exe";
+#else
+const wchar_t kProcessName[] = L"base_unittests";
+#endif // defined(OS_WIN)
+
+#if defined(OS_ANDROID)
+const char kShellPath[] = "/system/bin/sh";
+const char kPosixShell[] = "sh";
+#else
+const char kShellPath[] = "/bin/sh";
+const char kPosixShell[] = "bash";
+#endif
+
+const char kSignalFileSlow[] = "SlowChildProcess.die";
+const char kSignalFileCrash[] = "CrashingChildProcess.die";
+const char kSignalFileKill[] = "KilledChildProcess.die";
+
+#if defined(OS_WIN)
+const int kExpectedStillRunningExitCode = 0x102;
+const int kExpectedKilledExitCode = 1;
+#else
+const int kExpectedStillRunningExitCode = 0;
+#endif
+
+// Sleeps until file filename is created.
+void WaitToDie(const char* filename) {
+ FILE* fp;
+ do {
+ base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(10));
+ fp = fopen(filename, "r");
+ } while (!fp);
+ fclose(fp);
+}
+
+// Signals children they should die now.
+void SignalChildren(const char* filename) {
+ FILE* fp = fopen(filename, "w");
+ fclose(fp);
+}
+
+// Using a pipe to the child to wait for an event was considered, but
+// there were cases in the past where pipes caused problems (other
+// libraries closing the fds, child deadlocking). This is a simple
+// case, so it's not worth the risk. Using wait loops is discouraged
+// in most instances.
+base::TerminationStatus WaitForChildTermination(base::ProcessHandle handle,
+ int* exit_code) {
+ // Now we wait until the result is something other than STILL_RUNNING.
+ base::TerminationStatus status = base::TERMINATION_STATUS_STILL_RUNNING;
+ const base::TimeDelta kInterval = base::TimeDelta::FromMilliseconds(20);
+ base::TimeDelta waited;
+ do {
+ status = base::GetTerminationStatus(handle, exit_code);
+ base::PlatformThread::Sleep(kInterval);
+ waited += kInterval;
+ } while (status == base::TERMINATION_STATUS_STILL_RUNNING &&
+// Waiting for more time for process termination on android devices.
+#if defined(OS_ANDROID)
+ waited < TestTimeouts::large_test_timeout());
+#else
+ waited < TestTimeouts::action_max_timeout());
+#endif
+
+ return status;
+}
+
+} // namespace
+
+class ProcessUtilTest : public base::MultiProcessTest {
+ public:
+#if defined(OS_POSIX)
+ // Spawn a child process that counts how many file descriptors are open.
+ int CountOpenFDsInChild();
+#endif
+ // Converts the filename to a platform specific filepath.
+ // On Android files can not be created in arbitrary directories.
+ static std::string GetSignalFilePath(const char* filename);
+};
+
+std::string ProcessUtilTest::GetSignalFilePath(const char* filename) {
+#if !defined(OS_ANDROID)
+ return filename;
+#else
+ FilePath tmp_dir;
+ PathService::Get(base::DIR_CACHE, &tmp_dir);
+ tmp_dir = tmp_dir.Append(filename);
+ return tmp_dir.value();
+#endif
+}
+
+MULTIPROCESS_TEST_MAIN(SimpleChildProcess) {
+ return 0;
+}
+
+TEST_F(ProcessUtilTest, SpawnChild) {
+ base::ProcessHandle handle = this->SpawnChild("SimpleChildProcess", false);
+ ASSERT_NE(base::kNullProcessHandle, handle);
+ EXPECT_TRUE(base::WaitForSingleProcess(
+ handle, TestTimeouts::action_max_timeout()));
+ base::CloseProcessHandle(handle);
+}
+
+MULTIPROCESS_TEST_MAIN(SlowChildProcess) {
+ WaitToDie(ProcessUtilTest::GetSignalFilePath(kSignalFileSlow).c_str());
+ return 0;
+}
+
+TEST_F(ProcessUtilTest, KillSlowChild) {
+ const std::string signal_file =
+ ProcessUtilTest::GetSignalFilePath(kSignalFileSlow);
+ remove(signal_file.c_str());
+ base::ProcessHandle handle = this->SpawnChild("SlowChildProcess", false);
+ ASSERT_NE(base::kNullProcessHandle, handle);
+ SignalChildren(signal_file.c_str());
+ EXPECT_TRUE(base::WaitForSingleProcess(
+ handle, TestTimeouts::action_max_timeout()));
+ base::CloseProcessHandle(handle);
+ remove(signal_file.c_str());
+}
+
+// Times out on Linux and Win, flakes on other platforms, http://crbug.com/95058
+TEST_F(ProcessUtilTest, DISABLED_GetTerminationStatusExit) {
+ const std::string signal_file =
+ ProcessUtilTest::GetSignalFilePath(kSignalFileSlow);
+ remove(signal_file.c_str());
+ base::ProcessHandle handle = this->SpawnChild("SlowChildProcess", false);
+ ASSERT_NE(base::kNullProcessHandle, handle);
+
+ int exit_code = 42;
+ EXPECT_EQ(base::TERMINATION_STATUS_STILL_RUNNING,
+ base::GetTerminationStatus(handle, &exit_code));
+ EXPECT_EQ(kExpectedStillRunningExitCode, exit_code);
+
+ SignalChildren(signal_file.c_str());
+ exit_code = 42;
+ base::TerminationStatus status =
+ WaitForChildTermination(handle, &exit_code);
+ EXPECT_EQ(base::TERMINATION_STATUS_NORMAL_TERMINATION, status);
+ EXPECT_EQ(0, exit_code);
+ base::CloseProcessHandle(handle);
+ remove(signal_file.c_str());
+}
+
+#if defined(OS_WIN)
+// TODO(cpu): figure out how to test this in other platforms.
+TEST_F(ProcessUtilTest, GetProcId) {
+ base::ProcessId id1 = base::GetProcId(GetCurrentProcess());
+ EXPECT_NE(0ul, id1);
+ base::ProcessHandle handle = this->SpawnChild("SimpleChildProcess", false);
+ ASSERT_NE(base::kNullProcessHandle, handle);
+ base::ProcessId id2 = base::GetProcId(handle);
+ EXPECT_NE(0ul, id2);
+ EXPECT_NE(id1, id2);
+ base::CloseProcessHandle(handle);
+}
+#endif
+
+#if !defined(OS_MACOSX)
+// This test is disabled on Mac, since it's flaky due to ReportCrash
+// taking a variable amount of time to parse and load the debug and
+// symbol data for this unit test's executable before firing the
+// signal handler.
+//
+// TODO(gspencer): turn this test process into a very small program
+// with no symbols (instead of using the multiprocess testing
+// framework) to reduce the ReportCrash overhead.
+
+MULTIPROCESS_TEST_MAIN(CrashingChildProcess) {
+ WaitToDie(ProcessUtilTest::GetSignalFilePath(kSignalFileCrash).c_str());
+#if defined(OS_POSIX)
+ // Have to disable to signal handler for segv so we can get a crash
+ // instead of an abnormal termination through the crash dump handler.
+ ::signal(SIGSEGV, SIG_DFL);
+#endif
+ // Make this process have a segmentation fault.
+ volatile int* oops = NULL;
+ *oops = 0xDEAD;
+ return 1;
+}
+
+// This test intentionally crashes, so we don't need to run it under
+// AddressSanitizer.
+// TODO(jschuh): crbug.com/175753 Fix this in Win64 bots.
+#if defined(ADDRESS_SANITIZER) || (defined(OS_WIN) && defined(ARCH_CPU_X86_64))
+#define MAYBE_GetTerminationStatusCrash DISABLED_GetTerminationStatusCrash
+#else
+#define MAYBE_GetTerminationStatusCrash GetTerminationStatusCrash
+#endif
+TEST_F(ProcessUtilTest, MAYBE_GetTerminationStatusCrash) {
+ const std::string signal_file =
+ ProcessUtilTest::GetSignalFilePath(kSignalFileCrash);
+ remove(signal_file.c_str());
+ base::ProcessHandle handle = this->SpawnChild("CrashingChildProcess",
+ false);
+ ASSERT_NE(base::kNullProcessHandle, handle);
+
+ int exit_code = 42;
+ EXPECT_EQ(base::TERMINATION_STATUS_STILL_RUNNING,
+ base::GetTerminationStatus(handle, &exit_code));
+ EXPECT_EQ(kExpectedStillRunningExitCode, exit_code);
+
+ SignalChildren(signal_file.c_str());
+ exit_code = 42;
+ base::TerminationStatus status =
+ WaitForChildTermination(handle, &exit_code);
+ EXPECT_EQ(base::TERMINATION_STATUS_PROCESS_CRASHED, status);
+
+#if defined(OS_WIN)
+ EXPECT_EQ(0xc0000005, exit_code);
+#elif defined(OS_POSIX)
+ int signaled = WIFSIGNALED(exit_code);
+ EXPECT_NE(0, signaled);
+ int signal = WTERMSIG(exit_code);
+ EXPECT_EQ(SIGSEGV, signal);
+#endif
+ base::CloseProcessHandle(handle);
+
+ // Reset signal handlers back to "normal".
+ base::debug::EnableInProcessStackDumping();
+ remove(signal_file.c_str());
+}
+#endif // !defined(OS_MACOSX)
+
+MULTIPROCESS_TEST_MAIN(KilledChildProcess) {
+ WaitToDie(ProcessUtilTest::GetSignalFilePath(kSignalFileKill).c_str());
+#if defined(OS_WIN)
+ // Kill ourselves.
+ HANDLE handle = ::OpenProcess(PROCESS_ALL_ACCESS, 0, ::GetCurrentProcessId());
+ ::TerminateProcess(handle, kExpectedKilledExitCode);
+#elif defined(OS_POSIX)
+ // Send a SIGKILL to this process, just like the OOM killer would.
+ ::kill(getpid(), SIGKILL);
+#endif
+ return 1;
+}
+
+TEST_F(ProcessUtilTest, GetTerminationStatusKill) {
+ const std::string signal_file =
+ ProcessUtilTest::GetSignalFilePath(kSignalFileKill);
+ remove(signal_file.c_str());
+ base::ProcessHandle handle = this->SpawnChild("KilledChildProcess",
+ false);
+ ASSERT_NE(base::kNullProcessHandle, handle);
+
+ int exit_code = 42;
+ EXPECT_EQ(base::TERMINATION_STATUS_STILL_RUNNING,
+ base::GetTerminationStatus(handle, &exit_code));
+ EXPECT_EQ(kExpectedStillRunningExitCode, exit_code);
+
+ SignalChildren(signal_file.c_str());
+ exit_code = 42;
+ base::TerminationStatus status =
+ WaitForChildTermination(handle, &exit_code);
+ EXPECT_EQ(base::TERMINATION_STATUS_PROCESS_WAS_KILLED, status);
+#if defined(OS_WIN)
+ EXPECT_EQ(kExpectedKilledExitCode, exit_code);
+#elif defined(OS_POSIX)
+ int signaled = WIFSIGNALED(exit_code);
+ EXPECT_NE(0, signaled);
+ int signal = WTERMSIG(exit_code);
+ EXPECT_EQ(SIGKILL, signal);
+#endif
+ base::CloseProcessHandle(handle);
+ remove(signal_file.c_str());
+}
+
+// Ensure that the priority of a process is restored correctly after
+// backgrounding and restoring.
+// Note: a platform may not be willing or able to lower the priority of
+// a process. The calls to SetProcessBackground should be noops then.
+TEST_F(ProcessUtilTest, SetProcessBackgrounded) {
+ base::ProcessHandle handle = this->SpawnChild("SimpleChildProcess", false);
+ base::Process process(handle);
+ int old_priority = process.GetPriority();
+#if defined(OS_WIN)
+ EXPECT_TRUE(process.SetProcessBackgrounded(true));
+ EXPECT_TRUE(process.IsProcessBackgrounded());
+ EXPECT_TRUE(process.SetProcessBackgrounded(false));
+ EXPECT_FALSE(process.IsProcessBackgrounded());
+#else
+ process.SetProcessBackgrounded(true);
+ process.SetProcessBackgrounded(false);
+#endif
+ int new_priority = process.GetPriority();
+ EXPECT_EQ(old_priority, new_priority);
+}
+
+// Same as SetProcessBackgrounded but to this very process. It uses
+// a different code path at least for Windows.
+TEST_F(ProcessUtilTest, SetProcessBackgroundedSelf) {
+ base::Process process(base::Process::Current().handle());
+ int old_priority = process.GetPriority();
+#if defined(OS_WIN)
+ EXPECT_TRUE(process.SetProcessBackgrounded(true));
+ EXPECT_TRUE(process.IsProcessBackgrounded());
+ EXPECT_TRUE(process.SetProcessBackgrounded(false));
+ EXPECT_FALSE(process.IsProcessBackgrounded());
+#else
+ process.SetProcessBackgrounded(true);
+ process.SetProcessBackgrounded(false);
+#endif
+ int new_priority = process.GetPriority();
+ EXPECT_EQ(old_priority, new_priority);
+}
+
+#if defined(OS_LINUX) || defined(OS_ANDROID)
+TEST_F(ProcessUtilTest, GetSystemMemoryInfo) {
+ base::SystemMemoryInfoKB info;
+ EXPECT_TRUE(base::GetSystemMemoryInfo(&info));
+
+ // Ensure each field received a value.
+ EXPECT_GT(info.total, 0);
+ EXPECT_GT(info.free, 0);
+ EXPECT_GT(info.buffers, 0);
+ EXPECT_GT(info.cached, 0);
+ EXPECT_GT(info.active_anon, 0);
+ EXPECT_GT(info.inactive_anon, 0);
+ EXPECT_GT(info.active_file, 0);
+ EXPECT_GT(info.inactive_file, 0);
+
+ // All the values should be less than the total amount of memory.
+ EXPECT_LT(info.free, info.total);
+ EXPECT_LT(info.buffers, info.total);
+ EXPECT_LT(info.cached, info.total);
+ EXPECT_LT(info.active_anon, info.total);
+ EXPECT_LT(info.inactive_anon, info.total);
+ EXPECT_LT(info.active_file, info.total);
+ EXPECT_LT(info.inactive_file, info.total);
+
+#if defined(OS_CHROMEOS)
+ // Chrome OS exposes shmem.
+ EXPECT_GT(info.shmem, 0);
+ EXPECT_LT(info.shmem, info.total);
+ // Chrome unit tests are not run on actual Chrome OS hardware, so gem_objects
+ // and gem_size cannot be tested here.
+#endif
+}
+#endif // defined(OS_LINUX) || defined(OS_ANDROID)
+
+// TODO(estade): if possible, port these 2 tests.
+#if defined(OS_WIN)
+TEST_F(ProcessUtilTest, CalcFreeMemory) {
+ scoped_ptr<base::ProcessMetrics> metrics(
+ base::ProcessMetrics::CreateProcessMetrics(::GetCurrentProcess()));
+ ASSERT_TRUE(NULL != metrics.get());
+
+ bool using_tcmalloc = false;
+
+ // Detect if we are using tcmalloc
+#if !defined(NO_TCMALLOC)
+ const char* chrome_allocator = getenv("CHROME_ALLOCATOR");
+ if (!chrome_allocator || _stricmp(chrome_allocator, "tcmalloc") == 0)
+ using_tcmalloc = true;
+#endif
+
+ // Typical values here is ~1900 for total and ~1000 for largest. Obviously
+ // it depends in what other tests have done to this process.
+ base::FreeMBytes free_mem1 = {0};
+ EXPECT_TRUE(metrics->CalculateFreeMemory(&free_mem1));
+ EXPECT_LT(10u, free_mem1.total);
+ EXPECT_LT(10u, free_mem1.largest);
+ EXPECT_GT(2048u, free_mem1.total);
+ EXPECT_GT(2048u, free_mem1.largest);
+ EXPECT_GE(free_mem1.total, free_mem1.largest);
+ EXPECT_TRUE(NULL != free_mem1.largest_ptr);
+
+ // Allocate 20M and check again. It should have gone down.
+ const int kAllocMB = 20;
+ scoped_ptr<char[]> alloc(new char[kAllocMB * 1024 * 1024]);
+ size_t expected_total = free_mem1.total - kAllocMB;
+ size_t expected_largest = free_mem1.largest;
+
+ base::FreeMBytes free_mem2 = {0};
+ EXPECT_TRUE(metrics->CalculateFreeMemory(&free_mem2));
+ EXPECT_GE(free_mem2.total, free_mem2.largest);
+ // This test is flaky when using tcmalloc, because tcmalloc
+ // allocation strategy sometimes results in less than the
+ // full drop of 20Mb of free memory.
+ if (!using_tcmalloc)
+ EXPECT_GE(expected_total, free_mem2.total);
+ EXPECT_GE(expected_largest, free_mem2.largest);
+ EXPECT_TRUE(NULL != free_mem2.largest_ptr);
+}
+
+TEST_F(ProcessUtilTest, GetAppOutput) {
+ // Let's create a decently long message.
+ std::string message;
+ for (int i = 0; i < 1025; i++) { // 1025 so it does not end on a kilo-byte
+ // boundary.
+ message += "Hello!";
+ }
+ // cmd.exe's echo always adds a \r\n to its output.
+ std::string expected(message);
+ expected += "\r\n";
+
+ FilePath cmd(L"cmd.exe");
+ CommandLine cmd_line(cmd);
+ cmd_line.AppendArg("/c");
+ cmd_line.AppendArg("echo " + message + "");
+ std::string output;
+ ASSERT_TRUE(base::GetAppOutput(cmd_line, &output));
+ EXPECT_EQ(expected, output);
+
+ // Let's make sure stderr is ignored.
+ CommandLine other_cmd_line(cmd);
+ other_cmd_line.AppendArg("/c");
+ // http://msdn.microsoft.com/library/cc772622.aspx
+ cmd_line.AppendArg("echo " + message + " >&2");
+ output.clear();
+ ASSERT_TRUE(base::GetAppOutput(other_cmd_line, &output));
+ EXPECT_EQ("", output);
+}
+
+TEST_F(ProcessUtilTest, LaunchAsUser) {
+ base::UserTokenHandle token;
+ ASSERT_TRUE(OpenProcessToken(GetCurrentProcess(), TOKEN_ALL_ACCESS, &token));
+ std::wstring cmdline =
+ this->MakeCmdLine("SimpleChildProcess", false).GetCommandLineString();
+ base::LaunchOptions options;
+ options.as_user = token;
+ EXPECT_TRUE(base::LaunchProcess(cmdline, options, NULL));
+}
+
+#endif // defined(OS_WIN)
+
+#if defined(OS_POSIX)
+
+namespace {
+
+// Returns the maximum number of files that a process can have open.
+// Returns 0 on error.
+int GetMaxFilesOpenInProcess() {
+ struct rlimit rlim;
+ if (getrlimit(RLIMIT_NOFILE, &rlim) != 0) {
+ return 0;
+ }
+
+ // rlim_t is a uint64 - clip to maxint. We do this since FD #s are ints
+ // which are all 32 bits on the supported platforms.
+ rlim_t max_int = static_cast<rlim_t>(std::numeric_limits<int32>::max());
+ if (rlim.rlim_cur > max_int) {
+ return max_int;
+ }
+
+ return rlim.rlim_cur;
+}
+
+const int kChildPipe = 20; // FD # for write end of pipe in child process.
+
+} // namespace
+
+MULTIPROCESS_TEST_MAIN(ProcessUtilsLeakFDChildProcess) {
+ // This child process counts the number of open FDs, it then writes that
+ // number out to a pipe connected to the parent.
+ int num_open_files = 0;
+ int write_pipe = kChildPipe;
+ int max_files = GetMaxFilesOpenInProcess();
+ for (int i = STDERR_FILENO + 1; i < max_files; i++) {
+ if (i != kChildPipe) {
+ int fd;
+ if ((fd = HANDLE_EINTR(dup(i))) != -1) {
+ close(fd);
+ num_open_files += 1;
+ }
+ }
+ }
+
+ int written = HANDLE_EINTR(write(write_pipe, &num_open_files,
+ sizeof(num_open_files)));
+ DCHECK_EQ(static_cast<size_t>(written), sizeof(num_open_files));
+ int ret = HANDLE_EINTR(close(write_pipe));
+ DPCHECK(ret == 0);
+
+ return 0;
+}
+
+int ProcessUtilTest::CountOpenFDsInChild() {
+ int fds[2];
+ if (pipe(fds) < 0)
+ NOTREACHED();
+
+ base::FileHandleMappingVector fd_mapping_vec;
+ fd_mapping_vec.push_back(std::pair<int, int>(fds[1], kChildPipe));
+ base::ProcessHandle handle = this->SpawnChild(
+ "ProcessUtilsLeakFDChildProcess", fd_mapping_vec, false);
+ CHECK(handle);
+ int ret = HANDLE_EINTR(close(fds[1]));
+ DPCHECK(ret == 0);
+
+ // Read number of open files in client process from pipe;
+ int num_open_files = -1;
+ ssize_t bytes_read =
+ HANDLE_EINTR(read(fds[0], &num_open_files, sizeof(num_open_files)));
+ CHECK_EQ(bytes_read, static_cast<ssize_t>(sizeof(num_open_files)));
+
+#if defined(THREAD_SANITIZER) || defined(USE_HEAPCHECKER)
+ // Compiler-based ThreadSanitizer makes this test slow.
+ CHECK(base::WaitForSingleProcess(handle, base::TimeDelta::FromSeconds(3)));
+#else
+ CHECK(base::WaitForSingleProcess(handle, base::TimeDelta::FromSeconds(1)));
+#endif
+ base::CloseProcessHandle(handle);
+ ret = HANDLE_EINTR(close(fds[0]));
+ DPCHECK(ret == 0);
+
+ return num_open_files;
+}
+
+#if defined(ADDRESS_SANITIZER) || defined(THREAD_SANITIZER)
+// ProcessUtilTest.FDRemapping is flaky when ran under xvfb-run on Precise.
+// The problem is 100% reproducible with both ASan and TSan.
+// See http://crbug.com/136720.
+#define MAYBE_FDRemapping DISABLED_FDRemapping
+#else
+#define MAYBE_FDRemapping FDRemapping
+#endif
+TEST_F(ProcessUtilTest, MAYBE_FDRemapping) {
+ int fds_before = CountOpenFDsInChild();
+
+ // open some dummy fds to make sure they don't propagate over to the
+ // child process.
+ int dev_null = open("/dev/null", O_RDONLY);
+ int sockets[2];
+ socketpair(AF_UNIX, SOCK_STREAM, 0, sockets);
+
+ int fds_after = CountOpenFDsInChild();
+
+ ASSERT_EQ(fds_after, fds_before);
+
+ int ret;
+ ret = HANDLE_EINTR(close(sockets[0]));
+ DPCHECK(ret == 0);
+ ret = HANDLE_EINTR(close(sockets[1]));
+ DPCHECK(ret == 0);
+ ret = HANDLE_EINTR(close(dev_null));
+ DPCHECK(ret == 0);
+}
+
+namespace {
+
+std::string TestLaunchProcess(const base::EnvironmentVector& env_changes,
+ const int clone_flags) {
+ std::vector<std::string> args;
+ base::FileHandleMappingVector fds_to_remap;
+
+ args.push_back(kPosixShell);
+ args.push_back("-c");
+ args.push_back("echo $BASE_TEST");
+
+ int fds[2];
+ PCHECK(pipe(fds) == 0);
+
+ fds_to_remap.push_back(std::make_pair(fds[1], 1));
+ base::LaunchOptions options;
+ options.wait = true;
+ options.environ = &env_changes;
+ options.fds_to_remap = &fds_to_remap;
+#if defined(OS_LINUX)
+ options.clone_flags = clone_flags;
+#else
+ CHECK_EQ(0, clone_flags);
+#endif // OS_LINUX
+ EXPECT_TRUE(base::LaunchProcess(args, options, NULL));
+ PCHECK(HANDLE_EINTR(close(fds[1])) == 0);
+
+ char buf[512];
+ const ssize_t n = HANDLE_EINTR(read(fds[0], buf, sizeof(buf)));
+ PCHECK(n > 0);
+
+ PCHECK(HANDLE_EINTR(close(fds[0])) == 0);
+
+ return std::string(buf, n);
+}
+
+const char kLargeString[] =
+ "0123456789012345678901234567890123456789012345678901234567890123456789"
+ "0123456789012345678901234567890123456789012345678901234567890123456789"
+ "0123456789012345678901234567890123456789012345678901234567890123456789"
+ "0123456789012345678901234567890123456789012345678901234567890123456789"
+ "0123456789012345678901234567890123456789012345678901234567890123456789"
+ "0123456789012345678901234567890123456789012345678901234567890123456789"
+ "0123456789012345678901234567890123456789012345678901234567890123456789";
+
+} // namespace
+
+TEST_F(ProcessUtilTest, LaunchProcess) {
+ base::EnvironmentVector env_changes;
+ const int no_clone_flags = 0;
+
+ env_changes.push_back(std::make_pair(std::string("BASE_TEST"),
+ std::string("bar")));
+ EXPECT_EQ("bar\n", TestLaunchProcess(env_changes, no_clone_flags));
+ env_changes.clear();
+
+ EXPECT_EQ(0, setenv("BASE_TEST", "testing", 1 /* override */));
+ EXPECT_EQ("testing\n", TestLaunchProcess(env_changes, no_clone_flags));
+
+ env_changes.push_back(
+ std::make_pair(std::string("BASE_TEST"), std::string()));
+ EXPECT_EQ("\n", TestLaunchProcess(env_changes, no_clone_flags));
+
+ env_changes[0].second = "foo";
+ EXPECT_EQ("foo\n", TestLaunchProcess(env_changes, no_clone_flags));
+
+ env_changes.clear();
+ EXPECT_EQ(0, setenv("BASE_TEST", kLargeString, 1 /* override */));
+ EXPECT_EQ(std::string(kLargeString) + "\n",
+ TestLaunchProcess(env_changes, no_clone_flags));
+
+ env_changes.push_back(std::make_pair(std::string("BASE_TEST"),
+ std::string("wibble")));
+ EXPECT_EQ("wibble\n", TestLaunchProcess(env_changes, no_clone_flags));
+
+#if defined(OS_LINUX)
+ // Test a non-trival value for clone_flags.
+ // Don't test on Valgrind as it has limited support for clone().
+ if (!RunningOnValgrind()) {
+ EXPECT_EQ("wibble\n", TestLaunchProcess(env_changes, CLONE_FS | SIGCHLD));
+ }
+#endif
+}
+
+TEST_F(ProcessUtilTest, AlterEnvironment) {
+ const char* const empty[] = { NULL };
+ const char* const a2[] = { "A=2", NULL };
+ base::EnvironmentVector changes;
+ char** e;
+
+ e = base::AlterEnvironment(changes, empty);
+ EXPECT_TRUE(e[0] == NULL);
+ delete[] e;
+
+ changes.push_back(std::make_pair(std::string("A"), std::string("1")));
+ e = base::AlterEnvironment(changes, empty);
+ EXPECT_EQ(std::string("A=1"), e[0]);
+ EXPECT_TRUE(e[1] == NULL);
+ delete[] e;
+
+ changes.clear();
+ changes.push_back(std::make_pair(std::string("A"), std::string()));
+ e = base::AlterEnvironment(changes, empty);
+ EXPECT_TRUE(e[0] == NULL);
+ delete[] e;
+
+ changes.clear();
+ e = base::AlterEnvironment(changes, a2);
+ EXPECT_EQ(std::string("A=2"), e[0]);
+ EXPECT_TRUE(e[1] == NULL);
+ delete[] e;
+
+ changes.clear();
+ changes.push_back(std::make_pair(std::string("A"), std::string("1")));
+ e = base::AlterEnvironment(changes, a2);
+ EXPECT_EQ(std::string("A=1"), e[0]);
+ EXPECT_TRUE(e[1] == NULL);
+ delete[] e;
+
+ changes.clear();
+ changes.push_back(std::make_pair(std::string("A"), std::string()));
+ e = base::AlterEnvironment(changes, a2);
+ EXPECT_TRUE(e[0] == NULL);
+ delete[] e;
+}
+
+TEST_F(ProcessUtilTest, GetAppOutput) {
+ std::string output;
+
+#if defined(OS_ANDROID)
+ std::vector<std::string> argv;
+ argv.push_back("sh"); // Instead of /bin/sh, force path search to find it.
+ argv.push_back("-c");
+
+ argv.push_back("exit 0");
+ EXPECT_TRUE(base::GetAppOutput(CommandLine(argv), &output));
+ EXPECT_STREQ("", output.c_str());
+
+ argv[2] = "exit 1";
+ EXPECT_FALSE(base::GetAppOutput(CommandLine(argv), &output));
+ EXPECT_STREQ("", output.c_str());
+
+ argv[2] = "echo foobar42";
+ EXPECT_TRUE(base::GetAppOutput(CommandLine(argv), &output));
+ EXPECT_STREQ("foobar42\n", output.c_str());
+#else
+ EXPECT_TRUE(base::GetAppOutput(CommandLine(FilePath("true")), &output));
+ EXPECT_STREQ("", output.c_str());
+
+ EXPECT_FALSE(base::GetAppOutput(CommandLine(FilePath("false")), &output));
+
+ std::vector<std::string> argv;
+ argv.push_back("/bin/echo");
+ argv.push_back("-n");
+ argv.push_back("foobar42");
+ EXPECT_TRUE(base::GetAppOutput(CommandLine(argv), &output));
+ EXPECT_STREQ("foobar42", output.c_str());
+#endif // defined(OS_ANDROID)
+}
+
+TEST_F(ProcessUtilTest, GetAppOutputRestricted) {
+ // Unfortunately, since we can't rely on the path, we need to know where
+ // everything is. So let's use /bin/sh, which is on every POSIX system, and
+ // its built-ins.
+ std::vector<std::string> argv;
+ argv.push_back(std::string(kShellPath)); // argv[0]
+ argv.push_back("-c"); // argv[1]
+
+ // On success, should set |output|. We use |/bin/sh -c 'exit 0'| instead of
+ // |true| since the location of the latter may be |/bin| or |/usr/bin| (and we
+ // need absolute paths).
+ argv.push_back("exit 0"); // argv[2]; equivalent to "true"
+ std::string output = "abc";
+ EXPECT_TRUE(base::GetAppOutputRestricted(CommandLine(argv), &output, 100));
+ EXPECT_STREQ("", output.c_str());
+
+ argv[2] = "exit 1"; // equivalent to "false"
+ output = "before";
+ EXPECT_FALSE(base::GetAppOutputRestricted(CommandLine(argv),
+ &output, 100));
+ EXPECT_STREQ("", output.c_str());
+
+ // Amount of output exactly equal to space allowed.
+ argv[2] = "echo 123456789"; // (the sh built-in doesn't take "-n")
+ output.clear();
+ EXPECT_TRUE(base::GetAppOutputRestricted(CommandLine(argv), &output, 10));
+ EXPECT_STREQ("123456789\n", output.c_str());
+
+ // Amount of output greater than space allowed.
+ output.clear();
+ EXPECT_TRUE(base::GetAppOutputRestricted(CommandLine(argv), &output, 5));
+ EXPECT_STREQ("12345", output.c_str());
+
+ // Amount of output less than space allowed.
+ output.clear();
+ EXPECT_TRUE(base::GetAppOutputRestricted(CommandLine(argv), &output, 15));
+ EXPECT_STREQ("123456789\n", output.c_str());
+
+ // Zero space allowed.
+ output = "abc";
+ EXPECT_TRUE(base::GetAppOutputRestricted(CommandLine(argv), &output, 0));
+ EXPECT_STREQ("", output.c_str());
+}
+
+#if !defined(OS_MACOSX) && !defined(OS_OPENBSD)
+// TODO(benwells): GetAppOutputRestricted should terminate applications
+// with SIGPIPE when we have enough output. http://crbug.com/88502
+TEST_F(ProcessUtilTest, GetAppOutputRestrictedSIGPIPE) {
+ std::vector<std::string> argv;
+ std::string output;
+
+ argv.push_back(std::string(kShellPath)); // argv[0]
+ argv.push_back("-c");
+#if defined(OS_ANDROID)
+ argv.push_back("while echo 12345678901234567890; do :; done");
+ EXPECT_TRUE(base::GetAppOutputRestricted(CommandLine(argv), &output, 10));
+ EXPECT_STREQ("1234567890", output.c_str());
+#else
+ argv.push_back("yes");
+ EXPECT_TRUE(base::GetAppOutputRestricted(CommandLine(argv), &output, 10));
+ EXPECT_STREQ("y\ny\ny\ny\ny\n", output.c_str());
+#endif
+}
+#endif
+
+TEST_F(ProcessUtilTest, GetAppOutputRestrictedNoZombies) {
+ std::vector<std::string> argv;
+
+ argv.push_back(std::string(kShellPath)); // argv[0]
+ argv.push_back("-c"); // argv[1]
+ argv.push_back("echo 123456789012345678901234567890"); // argv[2]
+
+ // Run |GetAppOutputRestricted()| 300 (> default per-user processes on Mac OS
+ // 10.5) times with an output buffer big enough to capture all output.
+ for (int i = 0; i < 300; i++) {
+ std::string output;
+ EXPECT_TRUE(base::GetAppOutputRestricted(CommandLine(argv), &output, 100));
+ EXPECT_STREQ("123456789012345678901234567890\n", output.c_str());
+ }
+
+ // Ditto, but with an output buffer too small to capture all output.
+ for (int i = 0; i < 300; i++) {
+ std::string output;
+ EXPECT_TRUE(base::GetAppOutputRestricted(CommandLine(argv), &output, 10));
+ EXPECT_STREQ("1234567890", output.c_str());
+ }
+}
+
+TEST_F(ProcessUtilTest, GetAppOutputWithExitCode) {
+ // Test getting output from a successful application.
+ std::vector<std::string> argv;
+ std::string output;
+ int exit_code;
+ argv.push_back(std::string(kShellPath)); // argv[0]
+ argv.push_back("-c"); // argv[1]
+ argv.push_back("echo foo"); // argv[2];
+ EXPECT_TRUE(base::GetAppOutputWithExitCode(CommandLine(argv), &output,
+ &exit_code));
+ EXPECT_STREQ("foo\n", output.c_str());
+ EXPECT_EQ(exit_code, 0);
+
+ // Test getting output from an application which fails with a specific exit
+ // code.
+ output.clear();
+ argv[2] = "echo foo; exit 2";
+ EXPECT_TRUE(base::GetAppOutputWithExitCode(CommandLine(argv), &output,
+ &exit_code));
+ EXPECT_STREQ("foo\n", output.c_str());
+ EXPECT_EQ(exit_code, 2);
+}
+
+TEST_F(ProcessUtilTest, GetParentProcessId) {
+ base::ProcessId ppid = base::GetParentProcessId(base::GetCurrentProcId());
+ EXPECT_EQ(ppid, getppid());
+}
+
+#if defined(OS_LINUX) || defined(OS_ANDROID)
+TEST_F(ProcessUtilTest, ParseProcStatCPU) {
+ // /proc/self/stat for a process running "top".
+ const char kTopStat[] = "960 (top) S 16230 960 16230 34818 960 "
+ "4202496 471 0 0 0 "
+ "12 16 0 0 " // <- These are the goods.
+ "20 0 1 0 121946157 15077376 314 18446744073709551615 4194304 "
+ "4246868 140733983044336 18446744073709551615 140244213071219 "
+ "0 0 0 138047495 0 0 0 17 1 0 0 0 0 0";
+ EXPECT_EQ(12 + 16, base::ParseProcStatCPU(kTopStat));
+
+ // cat /proc/self/stat on a random other machine I have.
+ const char kSelfStat[] = "5364 (cat) R 5354 5364 5354 34819 5364 "
+ "0 142 0 0 0 "
+ "0 0 0 0 " // <- No CPU, apparently.
+ "16 0 1 0 1676099790 2957312 114 4294967295 134512640 134528148 "
+ "3221224832 3221224344 3086339742 0 0 0 0 0 0 0 17 0 0 0";
+
+ EXPECT_EQ(0, base::ParseProcStatCPU(kSelfStat));
+}
+
+// Disable on Android because base_unittests runs inside a Dalvik VM that
+// starts and stop threads (crbug.com/175563).
+#if !defined(OS_ANDROID)
+TEST_F(ProcessUtilTest, GetNumberOfThreads) {
+ const base::ProcessHandle current = base::GetCurrentProcessHandle();
+ const int initial_threads = base::GetNumberOfThreads(current);
+ ASSERT_GT(initial_threads, 0);
+ const int kNumAdditionalThreads = 10;
+ {
+ scoped_ptr<base::Thread> my_threads[kNumAdditionalThreads];
+ for (int i = 0; i < kNumAdditionalThreads; ++i) {
+ my_threads[i].reset(new base::Thread("GetNumberOfThreadsTest"));
+ my_threads[i]->Start();
+ ASSERT_EQ(base::GetNumberOfThreads(current), initial_threads + 1 + i);
+ }
+ }
+ // The Thread destructor will stop them.
+ ASSERT_EQ(initial_threads, base::GetNumberOfThreads(current));
+}
+#endif // !defined(OS_ANDROID)
+
+#endif // defined(OS_LINUX) || defined(OS_ANDROID)
+
+// TODO(port): port those unit tests.
+bool IsProcessDead(base::ProcessHandle child) {
+ // waitpid() will actually reap the process which is exactly NOT what we
+ // want to test for. The good thing is that if it can't find the process
+ // we'll get a nice value for errno which we can test for.
+ const pid_t result = HANDLE_EINTR(waitpid(child, NULL, WNOHANG));
+ return result == -1 && errno == ECHILD;
+}
+
+TEST_F(ProcessUtilTest, DelayedTermination) {
+ base::ProcessHandle child_process =
+ SpawnChild("process_util_test_never_die", false);
+ ASSERT_TRUE(child_process);
+ base::EnsureProcessTerminated(child_process);
+ base::WaitForSingleProcess(child_process, base::TimeDelta::FromSeconds(5));
+
+ // Check that process was really killed.
+ EXPECT_TRUE(IsProcessDead(child_process));
+ base::CloseProcessHandle(child_process);
+}
+
+MULTIPROCESS_TEST_MAIN(process_util_test_never_die) {
+ while (1) {
+ sleep(500);
+ }
+ return 0;
+}
+
+TEST_F(ProcessUtilTest, ImmediateTermination) {
+ base::ProcessHandle child_process =
+ SpawnChild("process_util_test_die_immediately", false);
+ ASSERT_TRUE(child_process);
+ // Give it time to die.
+ sleep(2);
+ base::EnsureProcessTerminated(child_process);
+
+ // Check that process was really killed.
+ EXPECT_TRUE(IsProcessDead(child_process));
+ base::CloseProcessHandle(child_process);
+}
+
+MULTIPROCESS_TEST_MAIN(process_util_test_die_immediately) {
+ return 0;
+}
+
+#endif // defined(OS_POSIX)
diff --git a/chromium/base/process/process_util_unittest_ios.cc b/chromium/base/process/process_util_unittest_ios.cc
new file mode 100644
index 00000000000..cad0f1b09f0
--- /dev/null
+++ b/chromium/base/process/process_util_unittest_ios.cc
@@ -0,0 +1,15 @@
+// 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/memory/scoped_ptr.h"
+#include "base/process/process_metrics.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+TEST(ProcessUtilTestIos, Memory) {
+ scoped_ptr<base::ProcessMetrics> process_metrics(
+ base::ProcessMetrics::CreateProcessMetrics(
+ base::GetCurrentProcessHandle()));
+
+ ASSERT_NE(0u, process_metrics->GetWorkingSetSize());
+}
diff --git a/chromium/base/process/process_win.cc b/chromium/base/process/process_win.cc
new file mode 100644
index 00000000000..1217b509896
--- /dev/null
+++ b/chromium/base/process/process_win.cc
@@ -0,0 +1,92 @@
+// Copyright (c) 2011 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.h"
+
+#include "base/logging.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/win/windows_version.h"
+
+namespace base {
+
+void Process::Close() {
+ if (!process_)
+ return;
+
+ // Don't call CloseHandle on a pseudo-handle.
+ if (process_ != ::GetCurrentProcess())
+ ::CloseHandle(process_);
+
+ process_ = NULL;
+}
+
+void Process::Terminate(int result_code) {
+ if (!process_)
+ return;
+
+ // Call NtTerminateProcess directly, without going through the import table,
+ // which might have been hooked with a buggy replacement by third party
+ // software. http://crbug.com/81449.
+ HMODULE module = GetModuleHandle(L"ntdll.dll");
+ typedef UINT (WINAPI *TerminateProcessPtr)(HANDLE handle, UINT code);
+ TerminateProcessPtr terminate_process = reinterpret_cast<TerminateProcessPtr>(
+ GetProcAddress(module, "NtTerminateProcess"));
+ terminate_process(process_, result_code);
+}
+
+bool Process::IsProcessBackgrounded() const {
+ if (!process_)
+ return false; // Failure case.
+ DWORD priority = GetPriority();
+ if (priority == 0)
+ return false; // Failure case.
+ return ((priority == BELOW_NORMAL_PRIORITY_CLASS) ||
+ (priority == IDLE_PRIORITY_CLASS));
+}
+
+bool Process::SetProcessBackgrounded(bool value) {
+ if (!process_)
+ return false;
+ // Vista and above introduce a real background mode, which not only
+ // sets the priority class on the threads but also on the IO generated
+ // by it. Unfortunately it can only be set for the calling process.
+ DWORD priority;
+ if ((base::win::GetVersion() >= base::win::VERSION_VISTA) &&
+ (process_ == ::GetCurrentProcess())) {
+ priority = value ? PROCESS_MODE_BACKGROUND_BEGIN :
+ PROCESS_MODE_BACKGROUND_END;
+ } else {
+ priority = value ? BELOW_NORMAL_PRIORITY_CLASS : NORMAL_PRIORITY_CLASS;
+ }
+
+ return (::SetPriorityClass(process_, priority) != 0);
+}
+
+ProcessId Process::pid() const {
+ if (process_ == 0)
+ return 0;
+
+ return GetProcId(process_);
+}
+
+bool Process::is_current() const {
+ return process_ == GetCurrentProcess();
+}
+
+// static
+Process Process::Current() {
+ return Process(::GetCurrentProcess());
+}
+
+// static
+bool Process::CanBackgroundProcesses() {
+ return true;
+}
+
+int Process::GetPriority() const {
+ DCHECK(process_);
+ return ::GetPriorityClass(process_);
+}
+
+} // namespace base