summaryrefslogtreecommitdiffstats
path: root/chromium/base/process
diff options
context:
space:
mode:
authorAllan Sandfeld Jensen <allan.jensen@theqtcompany.com>2015-06-18 14:10:49 +0200
committerOswald Buddenhagen <oswald.buddenhagen@theqtcompany.com>2015-06-18 13:53:24 +0000
commit813fbf95af77a531c57a8c497345ad2c61d475b3 (patch)
tree821b2c8de8365f21b6c9ba17a236fb3006a1d506 /chromium/base/process
parentaf6588f8d723931a298c995fa97259bb7f7deb55 (diff)
BASELINE: Update chromium to 44.0.2403.47
Change-Id: Ie056fedba95cf5e5c76b30c4b2c80fca4764aa2f Reviewed-by: Oswald Buddenhagen <oswald.buddenhagen@theqtcompany.com>
Diffstat (limited to 'chromium/base/process')
-rw-r--r--chromium/base/process/BUILD.gn108
-rw-r--r--chromium/base/process/internal_linux.cc6
-rw-r--r--chromium/base/process/internal_linux.h6
-rw-r--r--chromium/base/process/kill.cc7
-rw-r--r--chromium/base/process/kill.h45
-rw-r--r--chromium/base/process/kill_mac.cc8
-rw-r--r--chromium/base/process/kill_posix.cc286
-rw-r--r--chromium/base/process/kill_win.cc98
-rw-r--r--chromium/base/process/launch.cc4
-rw-r--r--chromium/base/process/launch.h90
-rw-r--r--chromium/base/process/launch_posix.cc165
-rw-r--r--chromium/base/process/launch_win.cc63
-rw-r--r--chromium/base/process/memory.cc20
-rw-r--r--chromium/base/process/memory.h4
-rw-r--r--chromium/base/process/memory_mac.mm248
-rw-r--r--chromium/base/process/memory_unittest.cc110
-rw-r--r--chromium/base/process/memory_win.cc18
-rw-r--r--chromium/base/process/process.h74
-rw-r--r--chromium/base/process/process_handle.h37
-rw-r--r--chromium/base/process/process_handle_posix.cc26
-rw-r--r--chromium/base/process/process_handle_win.cc98
-rw-r--r--chromium/base/process/process_info.h25
-rw-r--r--chromium/base/process/process_info_win.cc56
-rw-r--r--chromium/base/process/process_iterator.h37
-rw-r--r--chromium/base/process/process_linux.cc2
-rw-r--r--chromium/base/process/process_mac.cc128
-rw-r--r--chromium/base/process/process_metrics.cc6
-rw-r--r--chromium/base/process/process_metrics.h24
-rw-r--r--chromium/base/process/process_metrics_ios.cc17
-rw-r--r--chromium/base/process/process_metrics_linux.cc39
-rw-r--r--chromium/base/process/process_metrics_mac.cc23
-rw-r--r--chromium/base/process/process_metrics_posix.cc7
-rw-r--r--chromium/base/process/process_metrics_unittest.cc63
-rw-r--r--chromium/base/process/process_metrics_win.cc4
-rw-r--r--chromium/base/process/process_posix.cc312
-rw-r--r--chromium/base/process/process_unittest.cc77
-rw-r--r--chromium/base/process/process_util_unittest.cc316
-rw-r--r--chromium/base/process/process_win.cc96
38 files changed, 1563 insertions, 1190 deletions
diff --git a/chromium/base/process/BUILD.gn b/chromium/base/process/BUILD.gn
new file mode 100644
index 00000000000..814459b13e0
--- /dev/null
+++ b/chromium/base/process/BUILD.gn
@@ -0,0 +1,108 @@
+# Copyright (c) 2015 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.
+
+source_set("process") {
+ sources = [
+ "internal_linux.cc",
+ "internal_linux.h",
+ "kill.cc",
+ "kill.h",
+ "kill_mac.cc",
+ "kill_posix.cc",
+ "kill_win.cc",
+ "launch.cc",
+ "launch.h",
+ "launch_ios.cc",
+ "launch_mac.cc",
+ "launch_posix.cc",
+ "launch_win.cc",
+ "memory.cc",
+ "memory.h",
+ "memory_linux.cc",
+ "memory_mac.mm",
+ "memory_win.cc",
+ "process.h",
+ "process_handle_freebsd.cc",
+ "process_handle_linux.cc",
+ "process_handle_mac.cc",
+ "process_handle_openbsd.cc",
+ "process_handle_posix.cc",
+ "process_handle_win.cc",
+ "process_info.h",
+ "process_info_linux.cc",
+ "process_info_mac.cc",
+ "process_info_win.cc",
+ "process_iterator.cc",
+ "process_iterator.h",
+ "process_iterator_freebsd.cc",
+ "process_iterator_linux.cc",
+ "process_iterator_mac.cc",
+ "process_iterator_openbsd.cc",
+ "process_iterator_win.cc",
+ "process_linux.cc",
+ "process_mac.cc",
+ "process_metrics.cc",
+ "process_metrics.h",
+ "process_metrics_freebsd.cc",
+ "process_metrics_ios.cc",
+ "process_metrics_linux.cc",
+ "process_metrics_mac.cc",
+ "process_metrics_openbsd.cc",
+ "process_metrics_posix.cc",
+ "process_metrics_win.cc",
+ "process_posix.cc",
+ "process_win.cc",
+ ]
+
+ sources -= [
+ "process_handle_freebsd.cc",
+ "process_handle_openbsd.cc",
+ "process_iterator_freebsd.cc",
+ "process_iterator_openbsd.cc",
+ "process_metrics_freebsd.cc",
+ "process_metrics_openbsd.cc",
+ ]
+
+ if (is_android) {
+ # Android uses some Linux sources, put those back.
+ set_sources_assignment_filter([])
+ sources += [
+ "internal_linux.cc",
+ "memory_linux.cc",
+ "process_handle_linux.cc",
+ "process_iterator_linux.cc",
+ "process_metrics_linux.cc",
+ ]
+ set_sources_assignment_filter(sources_assignment_filter)
+ }
+
+ if (is_nacl) {
+ sources -= [
+ "kill.cc",
+ "kill.h",
+ "kill_posix.cc",
+ "launch.cc",
+ "launch.h",
+ "launch_posix.cc",
+ "memory.cc",
+ "memory.h",
+ "process_iterator.cc",
+ "process_iterator.h",
+ "process_metrics.cc",
+ "process_metrics_posix.cc",
+ "process_posix.cc",
+ ]
+ }
+
+ configs += [ "//base:base_implementation" ]
+
+ deps = [
+ "//base/memory",
+ "//base/third_party/dynamic_annotations",
+ ]
+
+ allow_circular_includes_from = [ "//base/memory" ]
+
+ visibility = [ "//base/*" ]
+}
diff --git a/chromium/base/process/internal_linux.cc b/chromium/base/process/internal_linux.cc
index ed7d7f4cf5e..d2e9ec52e53 100644
--- a/chromium/base/process/internal_linux.cc
+++ b/chromium/base/process/internal_linux.cc
@@ -106,12 +106,10 @@ bool ParseProcStats(const std::string& stats_data,
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;
+ base::StringPairs 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);
+ output->insert(key_value_pairs[i]);
}
}
diff --git a/chromium/base/process/internal_linux.h b/chromium/base/process/internal_linux.h
index 5fc33566607..1837f94ce52 100644
--- a/chromium/base/process/internal_linux.h
+++ b/chromium/base/process/internal_linux.h
@@ -5,8 +5,8 @@
// 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_
+#ifndef BASE_PROCESS_INTERNAL_LINUX_H_
+#define BASE_PROCESS_INTERNAL_LINUX_H_
#include <unistd.h>
@@ -87,4 +87,4 @@ TimeDelta ClockTicksToTimeDelta(int clock_ticks);
} // namespace internal
} // namespace base
-#endif // BASE_PROCESS_LINUX_INTERNAL_H_
+#endif // BASE_PROCESS_INTERNAL_LINUX_H_
diff --git a/chromium/base/process/kill.cc b/chromium/base/process/kill.cc
index caca3484a11..5d8ba6a2d75 100644
--- a/chromium/base/process/kill.cc
+++ b/chromium/base/process/kill.cc
@@ -14,11 +14,8 @@ bool KillProcesses(const FilePath::StringType& executable_name,
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
+ Process process = Process::Open(entry->pid());
+ result &= process.Terminate(exit_code, true);
}
return result;
}
diff --git a/chromium/base/process/kill.h b/chromium/base/process/kill.h
index de72d7a8dca..af00b03d984 100644
--- a/chromium/base/process/kill.h
+++ b/chromium/base/process/kill.h
@@ -9,6 +9,7 @@
#define BASE_PROCESS_KILL_H_
#include "base/files/file_path.h"
+#include "base/process/process.h"
#include "base/process/process_handle.h"
#include "base/time/time.h"
@@ -44,24 +45,12 @@ 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
@@ -93,22 +82,6 @@ BASE_EXPORT TerminationStatus GetKnownDeadTerminationStatus(
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.
@@ -118,12 +91,6 @@ BASE_EXPORT bool WaitForProcessesToExit(
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
@@ -146,15 +113,15 @@ BASE_EXPORT bool CleanupProcesses(const FilePath::StringType& executable_name,
// 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.
+// NOTE: The process must have been opened with the PROCESS_TERMINATE and
+// SYNCHRONIZE permissions.
//
-BASE_EXPORT void EnsureProcessTerminated(ProcessHandle process_handle);
+BASE_EXPORT void EnsureProcessTerminated(Process process);
#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);
+// wait for |pid| to finish and then reap it.
+BASE_EXPORT void EnsureProcessGetsReaped(ProcessId pid);
#endif
} // namespace base
diff --git a/chromium/base/process/kill_mac.cc b/chromium/base/process/kill_mac.cc
index 9257ee6929a..a4e0a14cf8f 100644
--- a/chromium/base/process/kill_mac.cc
+++ b/chromium/base/process/kill_mac.cc
@@ -66,8 +66,8 @@ void BlockingReap(pid_t child) {
// 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);
+ DCHECK_GT(child, 0);
+ DCHECK_GT(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
@@ -165,8 +165,8 @@ void WaitForChildToDie(pid_t child, int timeout) {
} // namespace
-void EnsureProcessTerminated(ProcessHandle process) {
- WaitForChildToDie(process, kWaitBeforeKillSeconds);
+void EnsureProcessTerminated(Process process) {
+ WaitForChildToDie(process.Pid(), kWaitBeforeKillSeconds);
}
} // namespace base
diff --git a/chromium/base/process/kill_posix.cc b/chromium/base/process/kill_posix.cc
index d17e9907626..0e303c67d3c 100644
--- a/chromium/base/process/kill_posix.cc
+++ b/chromium/base/process/kill_posix.cc
@@ -15,77 +15,12 @@
#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 {
-#if !defined(OS_NACL_NONSFI)
-bool WaitpidWithTimeout(ProcessHandle handle,
- int* status,
- base::TimeDelta wait) {
- // This POSIX version of this function only guarantees that we wait no less
- // than |wait| 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|, 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.
-
- if (wait.InMilliseconds() == base::kNoTimeout) {
- return HANDLE_EINTR(waitpid(handle, status, 0)) > 0;
- }
-
- 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() + wait;
- 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;
- }
- }
-
- return ret_pid > 0;
-}
-#endif // !defined(OS_NACL_NONSFI)
-
TerminationStatus GetTerminationStatusImpl(ProcessHandle handle,
bool can_block,
int* exit_code) {
@@ -133,61 +68,6 @@ TerminationStatus GetTerminationStatusImpl(ProcessHandle handle,
} // namespace
#if !defined(OS_NACL_NONSFI)
-// 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)
@@ -211,173 +91,29 @@ TerminationStatus GetKnownDeadTerminationStatus(ProcessHandle handle,
}
#if !defined(OS_NACL_NONSFI)
-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) {
- int status;
- if (!WaitpidWithTimeout(handle, &status, timeout))
- 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,
+ 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;
+ TimeTicks end_time = 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());
+ PlatformThread::Sleep(TimeDelta::FromMilliseconds(100));
+ } while ((end_time - TimeTicks::Now()) > 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());
-
- ScopedFD kq(kqueue());
- if (!kq.is_valid()) {
- DPLOG(ERROR) << "kqueue";
- return false;
- }
-
- struct kevent change = {0};
- EV_SET(&change, handle, EVFILT_PROC, EV_ADD, NOTE_EXIT, 0, NULL);
- int result = HANDLE_EINTR(kevent(kq.get(), &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.get(), 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 = GetCurrentProcessHandle();
- 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
- }
-
- int status;
- if (!WaitpidWithTimeout(handle, &status, wait))
- return false;
- return WIFEXITED(status);
-}
-
bool CleanupProcesses(const FilePath::StringType& executable_name,
- base::TimeDelta wait,
+ TimeDelta wait,
int exit_code,
const ProcessFilter* filter) {
bool exited_cleanly = WaitForProcessesToExit(executable_name, wait, filter);
@@ -463,22 +199,22 @@ class BackgroundReaper : public PlatformThread::Delegate {
} // namespace
-void EnsureProcessTerminated(ProcessHandle process) {
+void EnsureProcessTerminated(Process process) {
// If the child is already dead, then there's nothing to do.
- if (IsChildDead(process))
+ if (IsChildDead(process.Pid()))
return;
const unsigned timeout = 2; // seconds
- BackgroundReaper* reaper = new BackgroundReaper(process, timeout);
+ BackgroundReaper* reaper = new BackgroundReaper(process.Pid(), timeout);
PlatformThread::CreateNonJoinable(0, reaper);
}
-void EnsureProcessGetsReaped(ProcessHandle process) {
+void EnsureProcessGetsReaped(ProcessId pid) {
// If the child is already dead, then there's nothing to do.
- if (IsChildDead(process))
+ if (IsChildDead(pid))
return;
- BackgroundReaper* reaper = new BackgroundReaper(process, 0);
+ BackgroundReaper* reaper = new BackgroundReaper(pid, 0);
PlatformThread::CreateNonJoinable(0, reaper);
}
diff --git a/chromium/base/process/kill_win.cc b/chromium/base/process/kill_win.cc
index b102a8781d0..0da3a26ae4f 100644
--- a/chromium/base/process/kill_win.cc
+++ b/chromium/base/process/kill_win.cc
@@ -12,7 +12,6 @@
#include "base/logging.h"
#include "base/message_loop/message_loop.h"
#include "base/process/process_iterator.h"
-#include "base/profiler/scoped_tracker.h"
#include "base/win/object_watcher.h"
namespace base {
@@ -38,46 +37,40 @@ static const int kWaitInterval = 2000;
class TimerExpiredTask : public win::ObjectWatcher::Delegate {
public:
- explicit TimerExpiredTask(ProcessHandle process);
- ~TimerExpiredTask();
+ explicit TimerExpiredTask(Process process);
+ ~TimerExpiredTask() override;
void TimedOut();
// MessageLoop::Watcher -----------------------------------------------------
- virtual void OnObjectSignaled(HANDLE object);
+ void OnObjectSignaled(HANDLE object) override;
private:
void KillProcess();
// The process that we are watching.
- ProcessHandle process_;
+ Process process_;
win::ObjectWatcher watcher_;
DISALLOW_COPY_AND_ASSIGN(TimerExpiredTask);
};
-TimerExpiredTask::TimerExpiredTask(ProcessHandle process) : process_(process) {
- watcher_.StartWatching(process_, this);
+TimerExpiredTask::TimerExpiredTask(Process process) : process_(process.Pass()) {
+ watcher_.StartWatching(process_.Handle(), this);
}
TimerExpiredTask::~TimerExpiredTask() {
TimedOut();
- DCHECK(!process_) << "Make sure to close the handle.";
}
void TimerExpiredTask::TimedOut() {
- if (process_)
+ if (process_.IsValid())
KillProcess();
}
void TimerExpiredTask::OnObjectSignaled(HANDLE object) {
- // TODO(vadimt): Remove ScopedTracker below once crbug.com/418183 is fixed.
- tracked_objects::ScopedTracker tracking_profile(
- FROM_HERE_WITH_EXPLICIT_FUNCTION("TimerExpiredTask_OnObjectSignaled"));
-
- CloseHandle(process_);
- process_ = NULL;
+ process_.Close();
}
void TimerExpiredTask::KillProcess() {
@@ -88,42 +81,14 @@ void TimerExpiredTask::KillProcess() {
// 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);
+ process_.Terminate(kProcessKilledExitCode, false);
// Now, just cleanup as if the process exited normally.
- OnObjectSignaled(process_);
+ OnObjectSignaled(process_.Handle());
}
} // 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))
- DPLOG(ERROR) << "Error waiting for process exit";
- } else if (!result) {
- DPLOG(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) {
- DPLOG(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;
@@ -182,29 +147,8 @@ TerminationStatus GetTerminationStatus(ProcessHandle handle, int* exit_code) {
}
}
-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, static_cast<DWORD>(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,
+ TimeDelta wait,
const ProcessFilter* filter) {
bool result = true;
DWORD start_time = GetTickCount();
@@ -226,13 +170,8 @@ bool WaitForProcessesToExit(const FilePath::StringType& executable_name,
return result;
}
-bool WaitForSingleProcess(ProcessHandle handle, base::TimeDelta wait) {
- int exit_code;
- return WaitForExitCodeWithTimeout(handle, &exit_code, wait) && exit_code == 0;
-}
-
bool CleanupProcesses(const FilePath::StringType& executable_name,
- base::TimeDelta wait,
+ TimeDelta wait,
int exit_code,
const ProcessFilter* filter) {
if (WaitForProcessesToExit(executable_name, wait, filter))
@@ -241,20 +180,19 @@ bool CleanupProcesses(const FilePath::StringType& executable_name,
return false;
}
-void EnsureProcessTerminated(ProcessHandle process) {
- DCHECK(process != GetCurrentProcess());
+void EnsureProcessTerminated(Process process) {
+ DCHECK(!process.is_current());
// If already signaled, then we are done!
- if (WaitForSingleObject(process, 0) == WAIT_OBJECT_0) {
- CloseHandle(process);
+ if (WaitForSingleObject(process.Handle(), 0) == WAIT_OBJECT_0) {
return;
}
MessageLoop::current()->PostDelayedTask(
FROM_HERE,
- base::Bind(&TimerExpiredTask::TimedOut,
- base::Owned(new TimerExpiredTask(process))),
- base::TimeDelta::FromMilliseconds(kWaitInterval));
+ Bind(&TimerExpiredTask::TimedOut,
+ Owned(new TimerExpiredTask(process.Pass()))),
+ TimeDelta::FromMilliseconds(kWaitInterval));
}
} // namespace base
diff --git a/chromium/base/process/launch.cc b/chromium/base/process/launch.cc
index a1c4d2159cb..c179b2f5f37 100644
--- a/chromium/base/process/launch.cc
+++ b/chromium/base/process/launch.cc
@@ -27,7 +27,11 @@ LaunchOptions::LaunchOptions()
#if defined(OS_LINUX)
, clone_flags(0)
, allow_new_privs(false)
+ , kill_on_parent_death(false)
#endif // OS_LINUX
+#if defined(OS_POSIX)
+ , pre_exec_delegate(NULL)
+#endif // OS_POSIX
#if defined(OS_CHROMEOS)
, ctrl_terminal_fd(-1)
#endif // OS_CHROMEOS
diff --git a/chromium/base/process/launch.h b/chromium/base/process/launch.h
index 261019b138f..56f27a82109 100644
--- a/chromium/base/process/launch.h
+++ b/chromium/base/process/launch.h
@@ -14,6 +14,7 @@
#include "base/base_export.h"
#include "base/basictypes.h"
#include "base/environment.h"
+#include "base/process/process.h"
#include "base/process/process_handle.h"
#include "base/strings/string_piece.h"
@@ -21,7 +22,6 @@
#include "base/posix/file_descriptor_shuffle.h"
#elif defined(OS_WIN)
#include <windows.h>
-#include "base/win/scoped_handle.h"
#endif
namespace base {
@@ -37,6 +37,24 @@ 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 BASE_EXPORT LaunchOptions {
+#if defined(OS_POSIX)
+ // Delegate to be run in between fork and exec in the subprocess (see
+ // pre_exec_delegate below)
+ class BASE_EXPORT PreExecDelegate {
+ public:
+ PreExecDelegate() {}
+ virtual ~PreExecDelegate() {}
+
+ // Since this is to be run between fork and exec, and fork may have happened
+ // while multiple threads were running, this function needs to be async
+ // safe.
+ virtual void RunAsyncSafe() = 0;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(PreExecDelegate);
+ };
+#endif // defined(OS_POSIX)
+
LaunchOptions();
~LaunchOptions();
@@ -115,13 +133,32 @@ struct BASE_EXPORT LaunchOptions {
#if defined(OS_LINUX)
// If non-zero, start the process using clone(), using flags as provided.
+ // Unlike in clone, clone_flags may not contain a custom termination signal
+ // that is sent to the parent when the child dies. The termination signal will
+ // always be set to SIGCHLD.
int clone_flags;
// By default, child processes will have the PR_SET_NO_NEW_PRIVS bit set. If
// true, then this bit will not be set in the new child process.
bool allow_new_privs;
+
+ // Sets parent process death signal to SIGKILL.
+ bool kill_on_parent_death;
#endif // defined(OS_LINUX)
+#if defined(OS_POSIX)
+ // If not empty, change to this directory before execing the new process.
+ base::FilePath current_directory;
+
+ // If non-null, a delegate to be run immediately prior to executing the new
+ // program in the child process.
+ //
+ // WARNING: If LaunchProcess is called in the presence of multiple threads,
+ // code running in this delegate essentially needs to be async-signal safe
+ // (see man 7 signal for a list of allowed functions).
+ PreExecDelegate* pre_exec_delegate;
+#endif // defined(OS_POSIX)
+
#if defined(OS_CHROMEOS)
// If non-negative, the specified file descriptor will be set as the launched
// process' controlling terminal.
@@ -143,12 +180,7 @@ struct BASE_EXPORT LaunchOptions {
// 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.
+// Returns a valid Process upon success.
//
// Unix-specific notes:
// - All file descriptors open in the parent process will be closed in the
@@ -158,9 +190,8 @@ struct BASE_EXPORT LaunchOptions {
// 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);
+BASE_EXPORT Process LaunchProcess(const CommandLine& cmdline,
+ const LaunchOptions& options);
#if defined(OS_WIN)
// Windows-specific LaunchProcess that takes the command line as a
@@ -173,28 +204,24 @@ BASE_EXPORT bool LaunchProcess(const CommandLine& cmdline,
//
// Example (including literal quotes)
// cmdline = "c:\windows\explorer.exe" -foo "c:\bar\"
-BASE_EXPORT bool LaunchProcess(const string16& cmdline,
- const LaunchOptions& options,
- win::ScopedHandle* process_handle);
+BASE_EXPORT Process LaunchProcess(const string16& cmdline,
+ const LaunchOptions& options);
// Launches a process with elevated privileges. This does not behave exactly
// like LaunchProcess as it uses ShellExecuteEx instead of CreateProcess to
// create the process. This means the process will have elevated privileges
-// and thus some common operations like OpenProcess will fail. The process will
-// be available through the |process_handle| argument. Currently the only
-// supported LaunchOptions are |start_hidden| and |wait|.
-BASE_EXPORT bool LaunchElevatedProcess(const CommandLine& cmdline,
- const LaunchOptions& options,
- ProcessHandle* process_handle);
+// and thus some common operations like OpenProcess will fail. Currently the
+// only supported LaunchOptions are |start_hidden| and |wait|.
+BASE_EXPORT Process LaunchElevatedProcess(const CommandLine& cmdline,
+ const LaunchOptions& options);
#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);
+BASE_EXPORT Process LaunchProcess(const std::vector<std::string>& argv,
+ const LaunchOptions& options);
// 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
@@ -270,6 +297,25 @@ void ReplaceBootstrapPort(const std::string& replacement_bootstrap_name);
// binary. This should not be called in production/released code.
BASE_EXPORT LaunchOptions LaunchOptionsForTest();
+#if defined(OS_LINUX)
+// A wrapper for clone with fork-like behavior, meaning that it returns the
+// child's pid in the parent and 0 in the child. |flags|, |ptid|, and |ctid| are
+// as in the clone system call (the CLONE_VM flag is not supported).
+//
+// This function uses the libc clone wrapper (which updates libc's pid cache)
+// internally, so callers may expect things like getpid() to work correctly
+// after in both the child and parent. An exception is when this code is run
+// under Valgrind. Valgrind does not support the libc clone wrapper, so the libc
+// pid cache may be incorrect after this function is called under Valgrind.
+//
+// As with fork(), callers should be extremely careful when calling this while
+// multiple threads are running, since at the time the fork happened, the
+// threads could have been in any state (potentially holding locks, etc.).
+// Callers should most likely call execve() in the child soon after calling
+// this.
+BASE_EXPORT pid_t ForkWithFlags(unsigned long flags, pid_t* ptid, pid_t* ctid);
+#endif
+
} // namespace base
#endif // BASE_PROCESS_LAUNCH_H_
diff --git a/chromium/base/process/launch_posix.cc b/chromium/base/process/launch_posix.cc
index bc6294b2dad..77edc128319 100644
--- a/chromium/base/process/launch_posix.cc
+++ b/chromium/base/process/launch_posix.cc
@@ -7,9 +7,12 @@
#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
+#include <sched.h>
+#include <setjmp.h>
#include <signal.h>
#include <stdlib.h>
#include <sys/resource.h>
+#include <sys/syscall.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/wait.h>
@@ -30,13 +33,15 @@
#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.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/third_party/valgrind/valgrind.h"
#include "base/threading/platform_thread.h"
#include "base/threading/thread_restrictions.h"
+#include "build/build_config.h"
#if defined(OS_LINUX)
#include <sys/prctl.h>
@@ -184,6 +189,54 @@ void ResetChildSignalHandlersToDefaults(void) {
#endif // !defined(OS_LINUX) ||
// (!defined(__i386__) && !defined(__x86_64__) && !defined(__arm__))
+#if defined(OS_LINUX)
+bool IsRunningOnValgrind() {
+ return RUNNING_ON_VALGRIND;
+}
+
+// This function runs on the stack specified on the clone call. It uses longjmp
+// to switch back to the original stack so the child can return from sys_clone.
+int CloneHelper(void* arg) {
+ jmp_buf* env_ptr = reinterpret_cast<jmp_buf*>(arg);
+ longjmp(*env_ptr, 1);
+
+ // Should not be reached.
+ RAW_CHECK(false);
+ return 1;
+}
+
+// This function is noinline to ensure that stack_buf is below the stack pointer
+// that is saved when setjmp is called below. This is needed because when
+// compiled with FORTIFY_SOURCE, glibc's longjmp checks that the stack is moved
+// upwards. See crbug.com/442912 for more details.
+#if defined(ADDRESS_SANITIZER)
+// Disable AddressSanitizer instrumentation for this function to make sure
+// |stack_buf| is allocated on thread stack instead of ASan's fake stack.
+// Under ASan longjmp() will attempt to clean up the area between the old and
+// new stack pointers and print a warning that may confuse the user.
+__attribute__((no_sanitize_address))
+#endif
+NOINLINE pid_t CloneAndLongjmpInChild(unsigned long flags,
+ pid_t* ptid,
+ pid_t* ctid,
+ jmp_buf* env) {
+ // We use the libc clone wrapper instead of making the syscall
+ // directly because making the syscall may fail to update the libc's
+ // internal pid cache. The libc interface unfortunately requires
+ // specifying a new stack, so we use setjmp/longjmp to emulate
+ // fork-like behavior.
+ char stack_buf[PTHREAD_STACK_MIN];
+#if defined(ARCH_CPU_X86_FAMILY) || defined(ARCH_CPU_ARM_FAMILY) || \
+ defined(ARCH_CPU_MIPS64_FAMILY) || defined(ARCH_CPU_MIPS_FAMILY)
+ // The stack grows downward.
+ void* stack = stack_buf + sizeof(stack_buf);
+#else
+#error "Unsupported architecture"
+#endif
+ return clone(&CloneHelper, stack, flags, env, ptid, nullptr, ctid);
+}
+#endif // defined(OS_LINUX)
+
} // anonymous namespace
// Functor for |ScopedDIR| (below).
@@ -277,9 +330,13 @@ void CloseSuperfluousFds(const base::InjectiveMultimap& saved_mapping) {
}
}
-bool LaunchProcess(const std::vector<std::string>& argv,
- const LaunchOptions& options,
- ProcessHandle* process_handle) {
+Process LaunchProcess(const CommandLine& cmdline,
+ const LaunchOptions& options) {
+ return LaunchProcess(cmdline.argv(), options);
+}
+
+Process LaunchProcess(const std::vector<std::string>& argv,
+ const LaunchOptions& options) {
size_t fd_shuffle_size = 0;
if (options.fds_to_remap) {
fd_shuffle_size = options.fds_to_remap->size();
@@ -290,7 +347,12 @@ bool LaunchProcess(const std::vector<std::string>& argv,
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* []> argv_cstr(new char* [argv.size() + 1]);
+ for (size_t i = 0; i < argv.size(); i++) {
+ argv_cstr[i] = const_cast<char*>(argv[i].c_str());
+ }
+ argv_cstr[argv.size()] = NULL;
+
scoped_ptr<char*[]> new_environ;
char* const empty_environ = NULL;
char* const* old_environ = GetEnvironment();
@@ -303,6 +365,11 @@ bool LaunchProcess(const std::vector<std::string>& argv,
sigfillset(&full_sigset);
const sigset_t orig_sigmask = SetSignalMask(full_sigset);
+ const char* current_directory = nullptr;
+ if (!options.current_directory.empty()) {
+ current_directory = options.current_directory.value().c_str();
+ }
+
pid_t pid;
#if defined(OS_LINUX)
if (options.clone_flags) {
@@ -311,7 +378,17 @@ bool LaunchProcess(const std::vector<std::string>& argv,
// 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);
+
+ // We specify a null ptid and ctid.
+ RAW_CHECK(
+ !(options.clone_flags &
+ (CLONE_CHILD_CLEARTID | CLONE_CHILD_SETTID | CLONE_PARENT_SETTID)));
+
+ // Since we use waitpid, we do not support custom termination signals in the
+ // clone flags.
+ RAW_CHECK((options.clone_flags & 0xff) == 0);
+
+ pid = ForkWithFlags(options.clone_flags | SIGCHLD, nullptr, nullptr);
} else
#endif
{
@@ -325,7 +402,7 @@ bool LaunchProcess(const std::vector<std::string>& argv,
if (pid < 0) {
DPLOG(ERROR) << "fork";
- return false;
+ return Process();
} else if (pid == 0) {
// Child process
@@ -446,11 +523,23 @@ bool LaunchProcess(const std::vector<std::string>& argv,
RAW_LOG(FATAL, "prctl(PR_SET_NO_NEW_PRIVS) failed");
}
}
+
+ if (options.kill_on_parent_death) {
+ if (prctl(PR_SET_PDEATHSIG, SIGKILL) != 0) {
+ RAW_LOG(ERROR, "prctl(PR_SET_PDEATHSIG) failed");
+ _exit(127);
+ }
+ }
#endif
- 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 (current_directory != nullptr) {
+ RAW_CHECK(chdir(current_directory) == 0);
+ }
+
+ if (options.pre_exec_delegate != nullptr) {
+ options.pre_exec_delegate->RunAsyncSafe();
+ }
+
execvp(argv_cstr[0], argv_cstr.get());
RAW_LOG(ERROR, "LaunchProcess: failed to execvp:");
@@ -465,19 +554,9 @@ bool LaunchProcess(const std::vector<std::string>& argv,
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);
+ return Process(pid);
}
void RaiseProcessToHighPriority() {
@@ -612,7 +691,8 @@ static GetAppOutputInternalResult GetAppOutputInternal(
// Always wait for exit code (even if we know we'll declare
// GOT_MAX_OUTPUT).
- bool success = WaitForExitCode(pid, exit_code);
+ Process process(pid);
+ bool success = process.WaitForExit(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|).
@@ -661,4 +741,45 @@ bool GetAppOutputWithExitCode(const CommandLine& cl,
return result == EXECUTE_SUCCESS;
}
+#if defined(OS_LINUX)
+pid_t ForkWithFlags(unsigned long flags, pid_t* ptid, pid_t* ctid) {
+ const bool clone_tls_used = flags & CLONE_SETTLS;
+ const bool invalid_ctid =
+ (flags & (CLONE_CHILD_SETTID | CLONE_CHILD_CLEARTID)) && !ctid;
+ const bool invalid_ptid = (flags & CLONE_PARENT_SETTID) && !ptid;
+
+ // We do not support CLONE_VM.
+ const bool clone_vm_used = flags & CLONE_VM;
+
+ if (clone_tls_used || invalid_ctid || invalid_ptid || clone_vm_used) {
+ RAW_LOG(FATAL, "Invalid usage of ForkWithFlags");
+ }
+
+ // Valgrind's clone implementation does not support specifiying a child_stack
+ // without CLONE_VM, so we cannot use libc's clone wrapper when running under
+ // Valgrind. As a result, the libc pid cache may be incorrect under Valgrind.
+ // See crbug.com/442817 for more details.
+ if (IsRunningOnValgrind()) {
+ // See kernel/fork.c in Linux. There is different ordering of sys_clone
+ // parameters depending on CONFIG_CLONE_BACKWARDS* configuration options.
+#if defined(ARCH_CPU_X86_64)
+ return syscall(__NR_clone, flags, nullptr, ptid, ctid, nullptr);
+#elif defined(ARCH_CPU_X86) || defined(ARCH_CPU_ARM_FAMILY) || \
+ defined(ARCH_CPU_MIPS_FAMILY) || defined(ARCH_CPU_MIPS64_FAMILY)
+ // CONFIG_CLONE_BACKWARDS defined.
+ return syscall(__NR_clone, flags, nullptr, ptid, nullptr, ctid);
+#else
+#error "Unsupported architecture"
+#endif
+ }
+
+ jmp_buf env;
+ if (setjmp(env) == 0) {
+ return CloneAndLongjmpInChild(flags, ptid, ctid, &env);
+ }
+
+ return 0;
+}
+#endif // defined(OS_LINUX)
+
} // namespace base
diff --git a/chromium/base/process/launch_win.cc b/chromium/base/process/launch_win.cc
index a3303a5e085..ebc19b83130 100644
--- a/chromium/base/process/launch_win.cc
+++ b/chromium/base/process/launch_win.cc
@@ -105,9 +105,13 @@ void RouteStdioToConsole() {
std::ios::sync_with_stdio();
}
-bool LaunchProcess(const string16& cmdline,
- const LaunchOptions& options,
- win::ScopedHandle* process_handle) {
+Process LaunchProcess(const CommandLine& cmdline,
+ const LaunchOptions& options) {
+ return LaunchProcess(cmdline.GetCommandLineString(), options);
+}
+
+Process LaunchProcess(const string16& cmdline,
+ const LaunchOptions& options) {
win::StartupInformation startup_info_wrapper;
STARTUPINFO* startup_info = startup_info_wrapper.startup_info();
@@ -119,18 +123,18 @@ bool LaunchProcess(const string16& cmdline,
} else {
if (base::win::GetVersion() < base::win::VERSION_VISTA) {
DLOG(ERROR) << "Specifying handles to inherit requires Vista or later.";
- return false;
+ return Process();
}
if (options.handles_to_inherit->size() >
std::numeric_limits<DWORD>::max() / sizeof(HANDLE)) {
DLOG(ERROR) << "Too many handles to inherit.";
- return false;
+ return Process();
}
if (!startup_info_wrapper.InitializeProcThreadAttributeList(1)) {
DPLOG(ERROR);
- return false;
+ return Process();
}
if (!startup_info_wrapper.UpdateProcThreadAttribute(
@@ -139,7 +143,7 @@ bool LaunchProcess(const string16& cmdline,
static_cast<DWORD>(options.handles_to_inherit->size() *
sizeof(HANDLE)))) {
DPLOG(ERROR);
- return false;
+ return Process();
}
inherit_handles = true;
@@ -184,7 +188,7 @@ bool LaunchProcess(const string16& cmdline,
if (!CreateEnvironmentBlock(&enviroment_block, options.as_user, FALSE)) {
DPLOG(ERROR);
- return false;
+ return Process();
}
BOOL launched =
@@ -197,7 +201,7 @@ bool LaunchProcess(const string16& cmdline,
if (!launched) {
DPLOG(ERROR) << "Command line:" << std::endl << UTF16ToUTF8(cmdline)
<< std::endl;;
- return false;
+ return Process();
}
} else {
if (!CreateProcess(NULL,
@@ -206,7 +210,7 @@ bool LaunchProcess(const string16& cmdline,
startup_info, &temp_process_info)) {
DPLOG(ERROR) << "Command line:" << std::endl << UTF16ToUTF8(cmdline)
<< std::endl;;
- return false;
+ return Process();
}
}
base::win::ScopedProcessInformation process_info(temp_process_info);
@@ -215,8 +219,9 @@ bool LaunchProcess(const string16& cmdline,
if (0 == AssignProcessToJobObject(options.job_handle,
process_info.process_handle())) {
DLOG(ERROR) << "Could not AssignProcessToObject.";
- KillProcess(process_info.process_handle(), kProcessKilledExitCode, true);
- return false;
+ Process scoped_process(process_info.TakeProcessHandle());
+ scoped_process.Terminate(kProcessKilledExitCode, true);
+ return Process();
}
ResumeThread(process_info.thread_handle());
@@ -225,28 +230,11 @@ bool LaunchProcess(const string16& cmdline,
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->Set(process_info.TakeProcessHandle());
-
- return true;
-}
-
-bool LaunchProcess(const CommandLine& cmdline,
- const LaunchOptions& options,
- ProcessHandle* process_handle) {
- if (!process_handle)
- return LaunchProcess(cmdline.GetCommandLineString(), options, NULL);
-
- win::ScopedHandle process;
- bool rv = LaunchProcess(cmdline.GetCommandLineString(), options, &process);
- *process_handle = process.Take();
- return rv;
+ return Process(process_info.TakeProcessHandle());
}
-bool LaunchElevatedProcess(const CommandLine& cmdline,
- const LaunchOptions& options,
- ProcessHandle* process_handle) {
+Process LaunchElevatedProcess(const CommandLine& cmdline,
+ const LaunchOptions& options) {
const string16 file = cmdline.GetProgram().value();
const string16 arguments = cmdline.GetArgumentsString();
@@ -263,20 +251,13 @@ bool LaunchElevatedProcess(const CommandLine& cmdline,
if (!ShellExecuteEx(&shex_info)) {
DPLOG(ERROR);
- return false;
+ return Process();
}
if (options.wait)
WaitForSingleObject(shex_info.hProcess, INFINITE);
- // If the caller wants the process handle give it to them, otherwise just
- // close it. Closing it does not terminate the process.
- if (process_handle)
- *process_handle = shex_info.hProcess;
- else
- CloseHandle(shex_info.hProcess);
-
- return true;
+ return Process(shex_info.hProcess);
}
bool SetJobObjectLimitFlags(HANDLE job_object, DWORD limit_flags) {
diff --git a/chromium/base/process/memory.cc b/chromium/base/process/memory.cc
index 1dbc3630703..133a72a0f75 100644
--- a/chromium/base/process/memory.cc
+++ b/chromium/base/process/memory.cc
@@ -2,10 +2,28 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
+#include "base/debug/alias.h"
+#include "base/logging.h"
#include "base/process/memory.h"
namespace base {
+namespace {
+
+// Breakpad server classifies base::`anonymous namespace'::OnNoMemory as
+// out-of-memory crash.
+NOINLINE void OnNoMemory(size_t size) {
+ size_t tmp_size = size;
+ base::debug::Alias(&tmp_size);
+ LOG(FATAL) << "Out of memory. size=" << tmp_size;
+}
+
+} // namespace
+
+void TerminateBecauseOutOfMemory(size_t size) {
+ OnNoMemory(size);
+}
+
// Defined in memory_mac.mm for Mac.
#if !defined(OS_MACOSX)
@@ -27,4 +45,4 @@ bool UncheckedCalloc(size_t num_items, size_t size, void** result) {
#endif
-}
+} // namespace base
diff --git a/chromium/base/process/memory.h b/chromium/base/process/memory.h
index 100d9c72f43..da27151d1b1 100644
--- a/chromium/base/process/memory.h
+++ b/chromium/base/process/memory.h
@@ -39,6 +39,10 @@ BASE_EXPORT void EnableTerminationOnHeapCorruption();
// Turns on process termination if memory runs out.
BASE_EXPORT void EnableTerminationOnOutOfMemory();
+// Terminates process. Should be called only for out of memory errors.
+// Crash reporting classifies such crashes as OOM.
+BASE_EXPORT void TerminateBecauseOutOfMemory(size_t size);
+
#if defined(OS_WIN)
// Returns the module handle to which an address belongs. The reference count
// of the module is not incremented.
diff --git a/chromium/base/process/memory_mac.mm b/chromium/base/process/memory_mac.mm
index e59e63fa87f..4d719f8054e 100644
--- a/chromium/base/process/memory_mac.mm
+++ b/chromium/base/process/memory_mac.mm
@@ -4,12 +4,6 @@
#include "base/process/memory.h"
-// AddressSanitizer handles heap corruption, and on 64 bit Macs, the malloc
-// system automatically abort()s on heap corruption.
-#if !defined(ADDRESS_SANITIZER) && ARCH_CPU_32_BITS
-#define HANDLE_MEMORY_CORRUPTION_MANUALLY
-#endif
-
#include <CoreFoundation/CoreFoundation.h>
#include <errno.h>
#include <mach/mach.h>
@@ -27,170 +21,12 @@
#include "third_party/apple_apsl/CFBase.h"
#include "third_party/apple_apsl/malloc.h"
-#if defined(HANDLE_MEMORY_CORRUPTION_MANUALLY)
-#include <dlfcn.h>
-#include <mach-o/nlist.h>
-
-#include "base/threading/thread_local.h"
-#include "third_party/mach_override/mach_override.h"
-#endif // defined(HANDLE_MEMORY_CORRUPTION_MANUALLY)
-
namespace base {
-// These are helpers for EnableTerminationOnHeapCorruption, which is a no-op
-// on 64 bit Macs.
-#if defined(HANDLE_MEMORY_CORRUPTION_MANUALLY)
-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_alloc = 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.
- if (errno == ENOMEM)
- return;
-
- // The malloc library attempts to log to ASL (syslog) before calling this
- // code, which fails accessing a Unix-domain socket when sandboxed. The
- // failed socket results in writing to a -1 fd, leaving EBADF in errno. If
- // UncheckedMalloc() is on the stack, for large allocations (15k and up) only
- // an OOM failure leads here. Smaller allocations could also arrive here due
- // to freelist corruption, but there is no way to distinguish that from OOM at
- // this point.
- //
- // NOTE(shess): I hypothesize that EPERM case in 10.9 is the same root cause
- // as EBADF. Unfortunately, 10.9's opensource releases don't include malloc
- // source code at this time.
- // <http://crbug.com/312234>
- if ((errno == EBADF || errno == EPERM) && g_unchecked_alloc.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 // defined(HANDLE_MEMORY_CORRUPTION_MANUALLY)
-
void EnableTerminationOnHeapCorruption() {
-#if defined(HANDLE_MEMORY_CORRUPTION_MANUALLY)
- // 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(HANDLE_MEMORY_CORRUPTION_MANUALLY)
+#if !ARCH_CPU_64_BITS
+ DLOG(WARNING) << "EnableTerminationOnHeapCorruption only works on 64-bit";
+#endif
}
// ------------------------------------------------------------------------
@@ -293,142 +129,106 @@ memalign_type g_old_memalign_purgeable;
void* oom_killer_malloc(struct _malloc_zone_t* zone,
size_t size) {
-#if defined(HANDLE_MEMORY_CORRUPTION_MANUALLY)
- ScopedClearErrno clear_errno;
-#endif // defined(HANDLE_MEMORY_CORRUPTION_MANUALLY)
void* result = g_old_malloc(zone, size);
if (!result && size)
- debug::BreakDebugger();
+ TerminateBecauseOutOfMemory(size);
return result;
}
void* oom_killer_calloc(struct _malloc_zone_t* zone,
size_t num_items,
size_t size) {
-#if defined(HANDLE_MEMORY_CORRUPTION_MANUALLY)
- ScopedClearErrno clear_errno;
-#endif // defined(HANDLE_MEMORY_CORRUPTION_MANUALLY)
void* result = g_old_calloc(zone, num_items, size);
if (!result && num_items && size)
- debug::BreakDebugger();
+ TerminateBecauseOutOfMemory(num_items * size);
return result;
}
void* oom_killer_valloc(struct _malloc_zone_t* zone,
size_t size) {
-#if defined(HANDLE_MEMORY_CORRUPTION_MANUALLY)
- ScopedClearErrno clear_errno;
-#endif // defined(HANDLE_MEMORY_CORRUPTION_MANUALLY)
void* result = g_old_valloc(zone, size);
if (!result && size)
- debug::BreakDebugger();
+ TerminateBecauseOutOfMemory(size);
return result;
}
void oom_killer_free(struct _malloc_zone_t* zone,
void* ptr) {
-#if defined(HANDLE_MEMORY_CORRUPTION_MANUALLY)
- ScopedClearErrno clear_errno;
-#endif // defined(HANDLE_MEMORY_CORRUPTION_MANUALLY)
g_old_free(zone, ptr);
}
void* oom_killer_realloc(struct _malloc_zone_t* zone,
void* ptr,
size_t size) {
-#if defined(HANDLE_MEMORY_CORRUPTION_MANUALLY)
- ScopedClearErrno clear_errno;
-#endif // defined(HANDLE_MEMORY_CORRUPTION_MANUALLY)
void* result = g_old_realloc(zone, ptr, size);
if (!result && size)
- debug::BreakDebugger();
+ TerminateBecauseOutOfMemory(size);
return result;
}
void* oom_killer_memalign(struct _malloc_zone_t* zone,
size_t alignment,
size_t size) {
-#if defined(HANDLE_MEMORY_CORRUPTION_MANUALLY)
- ScopedClearErrno clear_errno;
-#endif // defined(HANDLE_MEMORY_CORRUPTION_MANUALLY)
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();
+ if (!result && size && alignment >= sizeof(void*) &&
+ (alignment & (alignment - 1)) == 0) {
+ TerminateBecauseOutOfMemory(size);
}
return result;
}
void* oom_killer_malloc_purgeable(struct _malloc_zone_t* zone,
size_t size) {
-#if defined(HANDLE_MEMORY_CORRUPTION_MANUALLY)
- ScopedClearErrno clear_errno;
-#endif // defined(HANDLE_MEMORY_CORRUPTION_MANUALLY)
void* result = g_old_malloc_purgeable(zone, size);
if (!result && size)
- debug::BreakDebugger();
+ TerminateBecauseOutOfMemory(size);
return result;
}
void* oom_killer_calloc_purgeable(struct _malloc_zone_t* zone,
size_t num_items,
size_t size) {
-#if defined(HANDLE_MEMORY_CORRUPTION_MANUALLY)
- ScopedClearErrno clear_errno;
-#endif // defined(HANDLE_MEMORY_CORRUPTION_MANUALLY)
void* result = g_old_calloc_purgeable(zone, num_items, size);
if (!result && num_items && size)
- debug::BreakDebugger();
+ TerminateBecauseOutOfMemory(num_items * size);
return result;
}
void* oom_killer_valloc_purgeable(struct _malloc_zone_t* zone,
size_t size) {
-#if defined(HANDLE_MEMORY_CORRUPTION_MANUALLY)
- ScopedClearErrno clear_errno;
-#endif // defined(HANDLE_MEMORY_CORRUPTION_MANUALLY)
void* result = g_old_valloc_purgeable(zone, size);
if (!result && size)
- debug::BreakDebugger();
+ TerminateBecauseOutOfMemory(size);
return result;
}
void oom_killer_free_purgeable(struct _malloc_zone_t* zone,
void* ptr) {
-#if defined(HANDLE_MEMORY_CORRUPTION_MANUALLY)
- ScopedClearErrno clear_errno;
-#endif // defined(HANDLE_MEMORY_CORRUPTION_MANUALLY)
g_old_free_purgeable(zone, ptr);
}
void* oom_killer_realloc_purgeable(struct _malloc_zone_t* zone,
void* ptr,
size_t size) {
-#if defined(HANDLE_MEMORY_CORRUPTION_MANUALLY)
- ScopedClearErrno clear_errno;
-#endif // defined(HANDLE_MEMORY_CORRUPTION_MANUALLY)
void* result = g_old_realloc_purgeable(zone, ptr, size);
if (!result && size)
- debug::BreakDebugger();
+ TerminateBecauseOutOfMemory(size);
return result;
}
void* oom_killer_memalign_purgeable(struct _malloc_zone_t* zone,
size_t alignment,
size_t size) {
-#if defined(HANDLE_MEMORY_CORRUPTION_MANUALLY)
- ScopedClearErrno clear_errno;
-#endif // defined(HANDLE_MEMORY_CORRUPTION_MANUALLY)
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();
+ TerminateBecauseOutOfMemory(size);
}
return result;
}
@@ -438,7 +238,7 @@ void* oom_killer_memalign_purgeable(struct _malloc_zone_t* zone,
// === C++ operator new ===
void oom_killer_new() {
- debug::BreakDebugger();
+ TerminateBecauseOutOfMemory(0);
}
#if !defined(ADDRESS_SANITIZER)
@@ -477,7 +277,7 @@ void* oom_killer_cfallocator_system_default(CFIndex alloc_size,
void* info) {
void* result = g_old_cfallocator_system_default(alloc_size, hint, info);
if (!result)
- debug::BreakDebugger();
+ TerminateBecauseOutOfMemory(alloc_size);
return result;
}
@@ -486,7 +286,7 @@ void* oom_killer_cfallocator_malloc(CFIndex alloc_size,
void* info) {
void* result = g_old_cfallocator_malloc(alloc_size, hint, info);
if (!result)
- debug::BreakDebugger();
+ TerminateBecauseOutOfMemory(alloc_size);
return result;
}
@@ -495,7 +295,7 @@ void* oom_killer_cfallocator_malloc_zone(CFIndex alloc_size,
void* info) {
void* result = g_old_cfallocator_malloc_zone(alloc_size, hint, info);
if (!result)
- debug::BreakDebugger();
+ TerminateBecauseOutOfMemory(alloc_size);
return result;
}
@@ -510,7 +310,7 @@ id oom_killer_allocWithZone(id self, SEL _cmd, NSZone* zone)
{
id result = g_old_allocWithZone(self, _cmd, zone);
if (!result)
- debug::BreakDebugger();
+ TerminateBecauseOutOfMemory(0);
return result;
}
@@ -521,10 +321,6 @@ bool UncheckedMalloc(size_t size, void** result) {
*result = malloc(size);
#else
if (g_old_malloc) {
-#if defined(HANDLE_MEMORY_CORRUPTION_MANUALLY)
- ScopedClearErrno clear_errno;
- ThreadLocalBooleanAutoReset flag(g_unchecked_alloc.Pointer(), true);
-#endif // defined(HANDLE_MEMORY_CORRUPTION_MANUALLY)
*result = g_old_malloc(malloc_default_zone(), size);
} else {
*result = malloc(size);
@@ -539,10 +335,6 @@ bool UncheckedCalloc(size_t num_items, size_t size, void** result) {
*result = calloc(num_items, size);
#else
if (g_old_calloc) {
-#if defined(HANDLE_MEMORY_CORRUPTION_MANUALLY)
- ScopedClearErrno clear_errno;
- ThreadLocalBooleanAutoReset flag(g_unchecked_alloc.Pointer(), true);
-#endif // defined(HANDLE_MEMORY_CORRUPTION_MANUALLY)
*result = g_old_calloc(malloc_default_zone(), num_items, size);
} else {
*result = calloc(num_items, size);
diff --git a/chromium/base/process/memory_unittest.cc b/chromium/base/process/memory_unittest.cc
index afbf5c629c2..0276b495915 100644
--- a/chromium/base/process/memory_unittest.cc
+++ b/chromium/base/process/memory_unittest.cc
@@ -26,6 +26,7 @@
#endif
#if defined(OS_LINUX)
#include <malloc.h>
+#include "base/test/malloc_wrapper.h"
#endif
#if defined(OS_WIN)
@@ -105,37 +106,6 @@ TEST(ProcessMemoryTest, EnableLFH) {
// 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) {
-#if ARCH_CPU_32_BITS
- // The Mavericks malloc library changed in a way which breaks the tricks used
- // to implement EnableTerminationOnOutOfMemory() with UncheckedMalloc() under
- // 32-bit. Under 64-bit the oom_killer code handles this.
- if (base::mac::IsOSMavericksOrLater())
- return;
-#endif
-
- // 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.*\\n?.*");
-
- base::debug::Alias(buf);
-}
-#endif // !defined(ADDRESS_SANITIZER)
-
TEST(ProcessMemoryTest, MacTerminateOnHeapCorruption) {
// Assert that freeing an unallocated pointer will crash the process.
char buf[9];
@@ -150,20 +120,19 @@ TEST(ProcessMemoryTest, MacTerminateOnHeapCorruption) {
ASSERT_DEATH(free(buf), "attempting free on address which "
"was not malloc\\(\\)-ed");
#else
- ASSERT_DEATH(free(buf), "being freed.*\\n?\\.*"
- "\\*\\*\\* set a breakpoint in malloc_error_break to debug.*\\n?.*"
- "Terminating process due to a potential for future heap corruption");
-#endif // ARCH_CPU_64_BITS || defined(ADDRESS_SANITIZER)
+ ADD_FAILURE() << "This test is not supported in this build configuration.";
+#endif
}
#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.
+// OutOfMemoryTest cases. OpenBSD does not support these tests either.
+// Don't test these on ASan/TSan/MSan configurations: only test the real
+// allocator.
// TODO(vandebo) make this work on Windows too.
-#if !defined(OS_ANDROID) && !defined(OS_OPENBSD) && \
- !defined(OS_WIN)
+#if !defined(OS_ANDROID) && !defined(OS_OPENBSD) && !defined(OS_WIN) && \
+ !defined(MEMORY_TOOL_REPLACES_ALLOCATOR)
#if defined(USE_TCMALLOC)
extern "C" {
@@ -171,6 +140,10 @@ int tc_set_new_mode(int mode);
}
#endif // defined(USE_TCMALLOC)
+namespace {
+const char *kOomRegex = "Out of memory";
+} // namespace
+
class OutOfMemoryTest : public testing::Test {
public:
OutOfMemoryTest()
@@ -182,13 +155,9 @@ class OutOfMemoryTest : public testing::Test {
}
#if defined(USE_TCMALLOC)
- virtual void SetUp() override {
- tc_set_new_mode(1);
- }
+ void SetUp() override { tc_set_new_mode(1); }
- virtual void TearDown() override {
- tc_set_new_mode(0);
- }
+ void TearDown() override { tc_set_new_mode(0); }
#endif // defined(USE_TCMALLOC)
protected:
@@ -213,42 +182,42 @@ TEST_F(OutOfMemoryDeathTest, New) {
ASSERT_DEATH({
SetUpInDeathAssert();
value_ = operator new(test_size_);
- }, "");
+ }, kOomRegex);
}
TEST_F(OutOfMemoryDeathTest, NewArray) {
ASSERT_DEATH({
SetUpInDeathAssert();
value_ = new char[test_size_];
- }, "");
+ }, kOomRegex);
}
TEST_F(OutOfMemoryDeathTest, Malloc) {
ASSERT_DEATH({
SetUpInDeathAssert();
value_ = malloc(test_size_);
- }, "");
+ }, kOomRegex);
}
TEST_F(OutOfMemoryDeathTest, Realloc) {
ASSERT_DEATH({
SetUpInDeathAssert();
value_ = realloc(NULL, test_size_);
- }, "");
+ }, kOomRegex);
}
TEST_F(OutOfMemoryDeathTest, Calloc) {
ASSERT_DEATH({
SetUpInDeathAssert();
value_ = calloc(1024, test_size_ / 1024L);
- }, "");
+ }, kOomRegex);
}
TEST_F(OutOfMemoryDeathTest, Valloc) {
ASSERT_DEATH({
SetUpInDeathAssert();
value_ = valloc(test_size_);
- }, "");
+ }, kOomRegex);
}
#if defined(OS_LINUX)
@@ -258,7 +227,7 @@ TEST_F(OutOfMemoryDeathTest, Pvalloc) {
ASSERT_DEATH({
SetUpInDeathAssert();
value_ = pvalloc(test_size_);
- }, "");
+ }, kOomRegex);
}
#endif // PVALLOC_AVAILABLE == 1
@@ -266,18 +235,16 @@ TEST_F(OutOfMemoryDeathTest, Memalign) {
ASSERT_DEATH({
SetUpInDeathAssert();
value_ = memalign(4, test_size_);
- }, "");
+ }, kOomRegex);
}
TEST_F(OutOfMemoryDeathTest, ViaSharedLibraries) {
// This tests that the run-time symbol resolution is overriding malloc for
- // shared libraries (including libc itself) as well as for our code.
- std::string format = base::StringPrintf("%%%zud", test_size_);
- char *value = NULL;
+ // shared libraries as well as for our code.
ASSERT_DEATH({
- SetUpInDeathAssert();
- EXPECT_EQ(-1, asprintf(&value, format.c_str(), 0));
- }, "");
+ SetUpInDeathAssert();
+ value_ = MallocWrapper(test_size_);
+ }, kOomRegex);
}
#endif // OS_LINUX
@@ -290,7 +257,7 @@ TEST_F(OutOfMemoryDeathTest, Posix_memalign) {
ASSERT_DEATH({
SetUpInDeathAssert();
EXPECT_EQ(ENOMEM, posix_memalign(&value_, 8, test_size_));
- }, "");
+ }, kOomRegex);
}
#endif // defined(OS_POSIX) && !defined(OS_ANDROID)
@@ -303,7 +270,7 @@ TEST_F(OutOfMemoryDeathTest, MallocPurgeable) {
ASSERT_DEATH({
SetUpInDeathAssert();
value_ = malloc_zone_malloc(zone, test_size_);
- }, "");
+ }, kOomRegex);
}
TEST_F(OutOfMemoryDeathTest, ReallocPurgeable) {
@@ -311,7 +278,7 @@ TEST_F(OutOfMemoryDeathTest, ReallocPurgeable) {
ASSERT_DEATH({
SetUpInDeathAssert();
value_ = malloc_zone_realloc(zone, NULL, test_size_);
- }, "");
+ }, kOomRegex);
}
TEST_F(OutOfMemoryDeathTest, CallocPurgeable) {
@@ -319,7 +286,7 @@ TEST_F(OutOfMemoryDeathTest, CallocPurgeable) {
ASSERT_DEATH({
SetUpInDeathAssert();
value_ = malloc_zone_calloc(zone, 1024, test_size_ / 1024L);
- }, "");
+ }, kOomRegex);
}
TEST_F(OutOfMemoryDeathTest, VallocPurgeable) {
@@ -327,7 +294,7 @@ TEST_F(OutOfMemoryDeathTest, VallocPurgeable) {
ASSERT_DEATH({
SetUpInDeathAssert();
value_ = malloc_zone_valloc(zone, test_size_);
- }, "");
+ }, kOomRegex);
}
TEST_F(OutOfMemoryDeathTest, PosixMemalignPurgeable) {
@@ -335,7 +302,7 @@ TEST_F(OutOfMemoryDeathTest, PosixMemalignPurgeable) {
ASSERT_DEATH({
SetUpInDeathAssert();
value_ = malloc_zone_memalign(zone, 8, test_size_);
- }, "");
+ }, kOomRegex);
}
// Since these allocation functions take a signed size, it's possible that
@@ -350,7 +317,7 @@ TEST_F(OutOfMemoryDeathTest, CFAllocatorSystemDefault) {
SetUpInDeathAssert();
while ((value_ =
base::AllocateViaCFAllocatorSystemDefault(signed_test_size_))) {}
- }, "");
+ }, kOomRegex);
}
TEST_F(OutOfMemoryDeathTest, CFAllocatorMalloc) {
@@ -358,7 +325,7 @@ TEST_F(OutOfMemoryDeathTest, CFAllocatorMalloc) {
SetUpInDeathAssert();
while ((value_ =
base::AllocateViaCFAllocatorMalloc(signed_test_size_))) {}
- }, "");
+ }, kOomRegex);
}
TEST_F(OutOfMemoryDeathTest, CFAllocatorMallocZone) {
@@ -366,7 +333,7 @@ TEST_F(OutOfMemoryDeathTest, CFAllocatorMallocZone) {
SetUpInDeathAssert();
while ((value_ =
base::AllocateViaCFAllocatorMallocZone(signed_test_size_))) {}
- }, "");
+ }, kOomRegex);
}
#if !defined(ARCH_CPU_64_BITS)
@@ -378,7 +345,7 @@ TEST_F(OutOfMemoryDeathTest, PsychoticallyBigObjCObject) {
ASSERT_DEATH({
SetUpInDeathAssert();
while ((value_ = base::AllocatePsychoticallyBigObjCObject())) {}
- }, "");
+ }, kOomRegex);
}
#endif // !ARCH_CPU_64_BITS
@@ -390,7 +357,7 @@ class OutOfMemoryHandledTest : public OutOfMemoryTest {
static const size_t kSafeCallocSize = 128;
static const size_t kSafeCallocItems = 4;
- virtual void SetUp() {
+ void SetUp() override {
OutOfMemoryTest::SetUp();
// We enable termination on OOM - just as Chrome does at early
@@ -448,4 +415,5 @@ TEST_F(OutOfMemoryHandledTest, UncheckedCalloc) {
EXPECT_TRUE(value_ == NULL);
}
#endif // !defined(MEMORY_TOOL_REPLACES_ALLOCATOR)
-#endif // !defined(OS_ANDROID) && !defined(OS_OPENBSD) && !defined(OS_WIN)
+#endif // !defined(OS_ANDROID) && !defined(OS_OPENBSD) && !defined(OS_WIN) &&
+ // !defined(ADDRESS_SANITIZER)
diff --git a/chromium/base/process/memory_win.cc b/chromium/base/process/memory_win.cc
index 668214ceaf0..fc57b48f1f7 100644
--- a/chromium/base/process/memory_win.cc
+++ b/chromium/base/process/memory_win.cc
@@ -4,6 +4,7 @@
#include "base/process/memory.h"
+#include <new.h>
#include <psapi.h>
#include "base/logging.h"
@@ -13,15 +14,19 @@ 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.
+#pragma warning(push)
+#pragma warning(disable: 4702)
+
+int OnNoMemory(size_t) {
+ // Kill the process. This is important for security since most of code
+ // does not check the result of memory allocation.
__debugbreak();
_exit(1);
+ return 0;
}
+#pragma warning(pop)
+
// HeapSetInformation function pointer.
typedef BOOL (WINAPI* HeapSetFn)(HANDLE, HEAP_INFORMATION_CLASS, PVOID, SIZE_T);
@@ -68,7 +73,8 @@ void EnableTerminationOnHeapCorruption() {
}
void EnableTerminationOnOutOfMemory() {
- std::set_new_handler(&OnNoMemory);
+ _set_new_handler(&OnNoMemory);
+ _set_new_mode(1);
}
HMODULE GetModuleFromAddress(void* address) {
diff --git a/chromium/base/process/process.h b/chromium/base/process/process.h
index 701947491d7..7fb1b69954c 100644
--- a/chromium/base/process/process.h
+++ b/chromium/base/process/process.h
@@ -1,14 +1,15 @@
-// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Copyright 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_
+#ifndef BASE_PROCESS_PROCESS_H_
+#define BASE_PROCESS_PROCESS_H_
#include "base/base_export.h"
#include "base/basictypes.h"
#include "base/move.h"
#include "base/process/process_handle.h"
+#include "base/time/time.h"
#include "build/build_config.h"
#if defined(OS_WIN)
@@ -40,7 +41,7 @@ class BASE_EXPORT Process {
Process(RValue other);
// The destructor does not terminate the process.
- ~Process() {}
+ ~Process();
// Move operator= for C++03 move emulation of this type.
Process& operator=(RValue other);
@@ -48,6 +49,26 @@ class BASE_EXPORT Process {
// Returns an object for the current process.
static Process Current();
+ // Returns a Process for the given |pid|.
+ static Process Open(ProcessId pid);
+
+ // Returns a Process for the given |pid|. On Windows the handle is opened
+ // with more access rights and must only be used by trusted code (can read the
+ // address space and duplicate handles).
+ static Process OpenWithExtraPrivileges(ProcessId pid);
+
+#if defined(OS_WIN)
+ // Returns a Process for the given |pid|, using some |desired_access|.
+ // See ::OpenProcess documentation for valid |desired_access|.
+ static Process OpenWithAccess(ProcessId pid, DWORD desired_access);
+#endif
+
+ // Creates an object from a |handle| owned by someone else.
+ // Don't use this for new code. It is only intended to ease the migration to
+ // a strict ownership model.
+ // TODO(rvargas) crbug.com/417532: Remove this code.
+ static Process DeprecatedGetProcessFromHandle(ProcessHandle handle);
+
// Returns true if processes can be backgrounded.
static bool CanBackgroundProcesses();
@@ -62,7 +83,7 @@ class BASE_EXPORT Process {
Process Duplicate() const;
// Get the PID for this process.
- ProcessId pid() const;
+ ProcessId Pid() const;
// Returns true if this process is the current process.
bool is_current() const;
@@ -70,11 +91,40 @@ class BASE_EXPORT Process {
// 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.
- // NOTE: On POSIX |result_code| is ignored.
- void Terminate(int result_code);
-
+ // Terminates the process with extreme prejudice. The given |exit_code| will
+ // be the exit code of the process. If |wait| is true, this method will wait
+ // for up to one minute for the process to actually terminate.
+ // Returns true if the process terminates within the allowed time.
+ // NOTE: On POSIX |exit_code| is ignored.
+ bool Terminate(int exit_code, bool wait) const;
+
+ // Waits for the process to exit. Returns true on success.
+ // On POSIX, if the process has been signaled then |exit_code| is set to -1.
+ // On Linux this must be a child process, however on Mac and Windows it can be
+ // any process.
+ bool WaitForExit(int* exit_code);
+
+ // Same as WaitForExit() but only waits for up to |timeout|.
+ bool WaitForExitWithTimeout(TimeDelta timeout, int* exit_code);
+
+#if defined(OS_MACOSX)
+ // The Mac needs a Mach port in order to manipulate a process's priority,
+ // and there's no good way to get that from base given the pid. These Mac
+ // variants of the IsProcessBackgrounded and SetProcessBackgrounded API take
+ // the Mach port for this reason. See crbug.com/460102
+ //
+ // A process is backgrounded when its priority is lower than normal.
+ // Return true if the process with mach port |task_port| is backgrounded,
+ // false otherwise.
+ bool IsProcessBackgrounded(mach_port_t task_port) const;
+
+ // Set the process with the specified mach port 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(mach_port_t task_port, bool value);
+#else
// A process is backgrounded when it's priority is lower than normal.
// Return true if this process is backgrounded, false otherwise.
bool IsProcessBackgrounded() const;
@@ -84,7 +134,7 @@ class BASE_EXPORT Process {
// will be made "normal" - equivalent to default process priority.
// Returns true if the priority was changed, false otherwise.
bool SetProcessBackgrounded(bool value);
-
+#endif // defined(OS_MACOSX)
// Returns an integer representing the priority of a process. The meaning
// of this value is OS dependent.
int GetPriority() const;
@@ -100,4 +150,4 @@ class BASE_EXPORT Process {
} // namespace base
-#endif // BASE_PROCESS_PROCESS_PROCESS_H_
+#endif // BASE_PROCESS_PROCESS_H_
diff --git a/chromium/base/process/process_handle.h b/chromium/base/process/process_handle.h
index 6f8094ee80b..77f2c585cfc 100644
--- a/chromium/base/process/process_handle.h
+++ b/chromium/base/process/process_handle.h
@@ -40,47 +40,12 @@ 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.
+// DEPRECATED. New code should be using Process::Pid() instead.
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_POSIX)
// Returns the path to the executable of the given process.
BASE_EXPORT FilePath GetProcessExecutablePath(ProcessHandle process);
diff --git a/chromium/base/process/process_handle_posix.cc b/chromium/base/process/process_handle_posix.cc
index 4013254a5c2..4e332df1a0f 100644
--- a/chromium/base/process/process_handle_posix.cc
+++ b/chromium/base/process/process_handle_posix.cc
@@ -16,32 +16,6 @@ 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;
}
diff --git a/chromium/base/process/process_handle_win.cc b/chromium/base/process/process_handle_win.cc
index 3bc3a125e0d..f2ffff8e882 100644
--- a/chromium/base/process/process_handle_win.cc
+++ b/chromium/base/process/process_handle_win.cc
@@ -20,107 +20,9 @@ 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
index e9e7b4e8151..85f204d04cf 100644
--- a/chromium/base/process/process_info.h
+++ b/chromium/base/process/process_info.h
@@ -2,11 +2,12 @@
// 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_
+#ifndef BASE_PROCESS_PROCESS_INFO_H_
+#define BASE_PROCESS_PROCESS_INFO_H_
#include "base/base_export.h"
#include "base/basictypes.h"
+#include "build/build_config.h"
namespace base {
@@ -20,6 +21,24 @@ class BASE_EXPORT CurrentProcessInfo {
static const Time CreationTime();
};
+#if defined(OS_WIN)
+
+enum IntegrityLevel {
+ INTEGRITY_UNKNOWN,
+ LOW_INTEGRITY,
+ MEDIUM_INTEGRITY,
+ HIGH_INTEGRITY,
+};
+
+// Returns the integrity level of the process. Returns INTEGRITY_UNKNOWN if the
+// system does not support integrity levels (pre-Vista) or in the case of an
+// underlying system failure.
+BASE_EXPORT IntegrityLevel GetCurrentProcessIntegrityLevel();
+
+#endif // defined(OS_WIN)
+
+
+
} // namespace base
-#endif // BASE_PROCESS_PROCESS_PROCESS_INFO_H_
+#endif // BASE_PROCESS_PROCESS_INFO_H_
diff --git a/chromium/base/process/process_info_win.cc b/chromium/base/process/process_info_win.cc
index b930ae6dd88..2b9c40653fd 100644
--- a/chromium/base/process/process_info_win.cc
+++ b/chromium/base/process/process_info_win.cc
@@ -7,11 +7,14 @@
#include <windows.h>
#include "base/basictypes.h"
+#include "base/memory/scoped_ptr.h"
#include "base/time/time.h"
+#include "base/win/scoped_handle.h"
+#include "base/win/windows_version.h"
namespace base {
-//static
+// static
const Time CurrentProcessInfo::CreationTime() {
FILETIME creation_time = {};
FILETIME ignore = {};
@@ -22,4 +25,55 @@ const Time CurrentProcessInfo::CreationTime() {
return Time::FromFileTime(creation_time);
}
+IntegrityLevel GetCurrentProcessIntegrityLevel() {
+ if (win::GetVersion() < base::win::VERSION_VISTA)
+ return INTEGRITY_UNKNOWN;
+
+ HANDLE process_token;
+ if (!::OpenProcessToken(::GetCurrentProcess(),
+ TOKEN_QUERY | TOKEN_QUERY_SOURCE, &process_token)) {
+ return INTEGRITY_UNKNOWN;
+ }
+ 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 INTEGRITY_UNKNOWN;
+ }
+
+ scoped_ptr<char[]> token_label_bytes(new char[token_info_length]);
+ if (!token_label_bytes.get())
+ return INTEGRITY_UNKNOWN;
+
+ TOKEN_MANDATORY_LABEL* token_label =
+ reinterpret_cast<TOKEN_MANDATORY_LABEL*>(token_label_bytes.get());
+ if (!token_label)
+ return INTEGRITY_UNKNOWN;
+
+ if (!::GetTokenInformation(process_token, TokenIntegrityLevel, token_label,
+ token_info_length, &token_info_length)) {
+ return INTEGRITY_UNKNOWN;
+ }
+
+ DWORD integrity_level = *::GetSidSubAuthority(
+ token_label->Label.Sid,
+ static_cast<DWORD>(*::GetSidSubAuthorityCount(token_label->Label.Sid)-1));
+
+ if (integrity_level < SECURITY_MANDATORY_MEDIUM_RID)
+ return LOW_INTEGRITY;
+
+ if (integrity_level >= SECURITY_MANDATORY_MEDIUM_RID &&
+ integrity_level < SECURITY_MANDATORY_HIGH_RID) {
+ return MEDIUM_INTEGRITY;
+ }
+
+ if (integrity_level >= SECURITY_MANDATORY_HIGH_RID)
+ return HIGH_INTEGRITY;
+
+ NOTREACHED();
+ return INTEGRITY_UNKNOWN;
+}
+
} // namespace base
diff --git a/chromium/base/process/process_iterator.h b/chromium/base/process/process_iterator.h
index bdd07c698de..ec6500e653c 100644
--- a/chromium/base/process/process_iterator.h
+++ b/chromium/base/process/process_iterator.h
@@ -36,26 +36,6 @@ struct ProcessEntry : public PROCESSENTRY32 {
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();
@@ -75,23 +55,6 @@ struct BASE_EXPORT ProcessEntry {
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.
diff --git a/chromium/base/process/process_linux.cc b/chromium/base/process/process_linux.cc
index 59ee288f880..88a310eccdb 100644
--- a/chromium/base/process/process_linux.cc
+++ b/chromium/base/process/process_linux.cc
@@ -103,7 +103,7 @@ bool Process::IsProcessBackgrounded() const {
&proc)) {
std::vector<std::string> proc_parts;
base::SplitString(proc, ':', &proc_parts);
- DCHECK(proc_parts.size() == 3);
+ DCHECK_EQ(proc_parts.size(), 3u);
bool ret = proc_parts[2] == std::string(kBackground);
return ret;
} else {
diff --git a/chromium/base/process/process_mac.cc b/chromium/base/process/process_mac.cc
new file mode 100644
index 00000000000..1913cc378e8
--- /dev/null
+++ b/chromium/base/process/process_mac.cc
@@ -0,0 +1,128 @@
+// Copyright 2015 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/mac/mac_util.h"
+#include "base/mac/mach_logging.h"
+
+#include <mach/mach.h>
+
+// The following was added to <mach/task_policy.h> after 10.8.
+// TODO(shrike): Remove the TASK_OVERRIDE_QOS_POLICY ifndef once builders
+// reach 10.9 or higher.
+#ifndef TASK_OVERRIDE_QOS_POLICY
+
+#define TASK_OVERRIDE_QOS_POLICY 9
+
+typedef struct task_category_policy task_category_policy_data_t;
+typedef struct task_category_policy* task_category_policy_t;
+
+enum task_latency_qos {
+ LATENCY_QOS_TIER_UNSPECIFIED = 0x0,
+ LATENCY_QOS_TIER_0 = ((0xFF << 16) | 1),
+ LATENCY_QOS_TIER_1 = ((0xFF << 16) | 2),
+ LATENCY_QOS_TIER_2 = ((0xFF << 16) | 3),
+ LATENCY_QOS_TIER_3 = ((0xFF << 16) | 4),
+ LATENCY_QOS_TIER_4 = ((0xFF << 16) | 5),
+ LATENCY_QOS_TIER_5 = ((0xFF << 16) | 6)
+};
+typedef integer_t task_latency_qos_t;
+enum task_throughput_qos {
+ THROUGHPUT_QOS_TIER_UNSPECIFIED = 0x0,
+ THROUGHPUT_QOS_TIER_0 = ((0xFE << 16) | 1),
+ THROUGHPUT_QOS_TIER_1 = ((0xFE << 16) | 2),
+ THROUGHPUT_QOS_TIER_2 = ((0xFE << 16) | 3),
+ THROUGHPUT_QOS_TIER_3 = ((0xFE << 16) | 4),
+ THROUGHPUT_QOS_TIER_4 = ((0xFE << 16) | 5),
+ THROUGHPUT_QOS_TIER_5 = ((0xFE << 16) | 6),
+};
+
+#define LATENCY_QOS_LAUNCH_DEFAULT_TIER LATENCY_QOS_TIER_3
+#define THROUGHPUT_QOS_LAUNCH_DEFAULT_TIER THROUGHPUT_QOS_TIER_3
+
+typedef integer_t task_throughput_qos_t;
+
+struct task_qos_policy {
+ task_latency_qos_t task_latency_qos_tier;
+ task_throughput_qos_t task_throughput_qos_tier;
+};
+
+typedef struct task_qos_policy* task_qos_policy_t;
+#define TASK_QOS_POLICY_COUNT \
+ ((mach_msg_type_number_t)(sizeof(struct task_qos_policy) / sizeof(integer_t)))
+
+#endif // TASK_OVERRIDE_QOS_POLICY
+
+namespace base {
+
+bool Process::CanBackgroundProcesses() {
+ return true;
+}
+
+bool Process::IsProcessBackgrounded(mach_port_t task_port) const {
+ // See SetProcessBackgrounded().
+ DCHECK(IsValid());
+ DCHECK_NE(task_port, TASK_NULL);
+
+ task_category_policy_data_t category_policy;
+ mach_msg_type_number_t task_info_count = TASK_CATEGORY_POLICY_COUNT;
+ boolean_t get_default = FALSE;
+
+ kern_return_t result =
+ task_policy_get(task_port, TASK_CATEGORY_POLICY,
+ reinterpret_cast<task_policy_t>(&category_policy),
+ &task_info_count, &get_default);
+ MACH_LOG_IF(ERROR, result != KERN_SUCCESS, result) <<
+ "task_policy_get TASK_CATEGORY_POLICY";
+
+ if (result == KERN_SUCCESS && get_default == FALSE) {
+ return category_policy.role == TASK_BACKGROUND_APPLICATION;
+ }
+ return false;
+}
+
+bool Process::SetProcessBackgrounded(mach_port_t task_port, bool background) {
+ DCHECK(IsValid());
+ DCHECK_NE(task_port, TASK_NULL);
+
+ if (!CanBackgroundProcesses()) {
+ return false;
+ } else if (IsProcessBackgrounded(task_port) == background) {
+ return true;
+ }
+
+ task_category_policy category_policy;
+ category_policy.role =
+ background ? TASK_BACKGROUND_APPLICATION : TASK_FOREGROUND_APPLICATION;
+ kern_return_t result =
+ task_policy_set(task_port, TASK_CATEGORY_POLICY,
+ reinterpret_cast<task_policy_t>(&category_policy),
+ TASK_CATEGORY_POLICY_COUNT);
+
+ if (result != KERN_SUCCESS) {
+ MACH_LOG(ERROR, result) << "task_policy_set TASK_CATEGORY_POLICY";
+ return false;
+ } else if (!mac::IsOSMavericksOrLater()) {
+ return true;
+ }
+
+ // Latency QoS regulates timer throttling/accuracy. Select default tier
+ // on foreground because precise timer firing isn't needed.
+ struct task_qos_policy qos_policy = {
+ background ? LATENCY_QOS_TIER_5 : LATENCY_QOS_TIER_UNSPECIFIED,
+ background ? THROUGHPUT_QOS_TIER_5 : THROUGHPUT_QOS_TIER_UNSPECIFIED
+ };
+ result = task_policy_set(task_port, TASK_OVERRIDE_QOS_POLICY,
+ reinterpret_cast<task_policy_t>(&qos_policy),
+ TASK_QOS_POLICY_COUNT);
+ if (result != KERN_SUCCESS) {
+ MACH_LOG(ERROR, result) << "task_policy_set TASK_OVERRIDE_QOS_POLICY";
+ return false;
+ }
+
+ return true;
+}
+
+} // namespace base
diff --git a/chromium/base/process/process_metrics.cc b/chromium/base/process/process_metrics.cc
index 2edd9c755de..e4863391076 100644
--- a/chromium/base/process/process_metrics.cc
+++ b/chromium/base/process/process_metrics.cc
@@ -33,11 +33,11 @@ scoped_ptr<Value> SystemMetrics::ToValue() const {
res->SetInteger("committed_memory", static_cast<int>(committed_memory_));
#if defined(OS_LINUX) || defined(OS_ANDROID)
- res->Set("meminfo", memory_info_.ToValue().release());
- res->Set("diskinfo", disk_info_.ToValue().release());
+ res->Set("meminfo", memory_info_.ToValue());
+ res->Set("diskinfo", disk_info_.ToValue());
#endif
#if defined(OS_CHROMEOS)
- res->Set("swapinfo", swap_info_.ToValue().release());
+ res->Set("swapinfo", swap_info_.ToValue());
#endif
return res.Pass();
diff --git a/chromium/base/process/process_metrics.h b/chromium/base/process/process_metrics.h
index 3eb3604b7a3..5916b94148c 100644
--- a/chromium/base/process/process_metrics.h
+++ b/chromium/base/process/process_metrics.h
@@ -85,17 +85,6 @@ struct CommittedKBytes {
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);
@@ -154,6 +143,14 @@ class BASE_EXPORT ProcessMetrics {
// usage in bytes, as per definition of WorkingSetBytes.
bool GetWorkingSetKBytes(WorkingSetKBytes* ws_usage) const;
+#if defined(OS_MACOSX)
+ // Fills both CommitedKBytes and WorkingSetKBytes in a single operation. This
+ // is more efficient on Mac OS X, as the two can be retrieved with a single
+ // system call.
+ bool GetCommittedAndWorkingSetKBytes(CommittedKBytes* usage,
+ WorkingSetKBytes* ws_usage) const;
+#endif
+
// Returns the CPU usage in percent since the last time this method or
// GetPlatformIndependentCPUUsage() was called. The first time this method
// is called it returns 0 and will return the actual CPU info on subsequent
@@ -231,10 +228,13 @@ class BASE_EXPORT ProcessMetrics {
// Returns 0 if it can't compute the commit charge.
BASE_EXPORT size_t GetSystemCommitCharge();
+// Returns the number of bytes in a memory page.
+BASE_EXPORT size_t GetPageSize();
+
#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();
+BASE_EXPORT size_t GetMaxFds();
// Sets the file descriptor soft limit to |max_descriptors| or the OS hard
// limit, whichever is lower.
diff --git a/chromium/base/process/process_metrics_ios.cc b/chromium/base/process/process_metrics_ios.cc
index 405c373c9fd..07f2c8de1d4 100644
--- a/chromium/base/process/process_metrics_ios.cc
+++ b/chromium/base/process/process_metrics_ios.cc
@@ -6,6 +6,8 @@
#include <mach/task.h>
+#include "base/logging.h"
+
namespace base {
namespace {
@@ -30,6 +32,11 @@ ProcessMetrics* ProcessMetrics::CreateProcessMetrics(ProcessHandle process) {
return new ProcessMetrics(process);
}
+double ProcessMetrics::GetCPUUsage() {
+ NOTIMPLEMENTED();
+ return 0;
+}
+
size_t ProcessMetrics::GetPagefileUsage() const {
task_basic_info_64 task_info_data;
if (!GetTaskInfo(&task_info_data))
@@ -65,4 +72,14 @@ void SetFdLimit(unsigned int max_descriptors) {
// Unimplemented.
}
+size_t GetPageSize() {
+ return getpagesize();
+}
+
+// Bytes committed by the system.
+size_t GetSystemCommitCharge() {
+ NOTIMPLEMENTED();
+ return 0;
+}
+
} // namespace base
diff --git a/chromium/base/process/process_metrics_linux.cc b/chromium/base/process/process_metrics_linux.cc
index e8db571a317..2d54c4c7265 100644
--- a/chromium/base/process/process_metrics_linux.cc
+++ b/chromium/base/process/process_metrics_linux.cc
@@ -399,17 +399,36 @@ size_t GetSystemCommitCharge() {
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))
+ // |input| may be empty if the process disappeared somehow.
+ // e.g. http://crbug.com/145811.
+ if (input.empty())
return -1;
- if (proc_stats.size() <= internal::VM_STIME)
+ size_t start = input.find_last_of(')');
+ if (start == input.npos)
return -1;
- int utime = GetProcStatsFieldAsInt64(proc_stats, internal::VM_UTIME);
- int stime = GetProcStatsFieldAsInt64(proc_stats, internal::VM_STIME);
- return utime + stime;
+
+ // Number of spaces remaining until reaching utime's index starting after the
+ // last ')'.
+ int num_spaces_remaining = internal::VM_UTIME - 1;
+
+ size_t i = start;
+ while ((i = input.find(' ', i + 1)) != input.npos) {
+ // Validate the assumption that there aren't any contiguous spaces
+ // in |input| before utime.
+ DCHECK_NE(input[i - 1], ' ');
+ if (--num_spaces_remaining == 0) {
+ int utime = 0;
+ int stime = 0;
+ if (sscanf(&input.data()[i], "%d %d", &utime, &stime) != 2)
+ return -1;
+
+ return utime + stime;
+ }
+ }
+
+ return -1;
}
const char kProcSelfExe[] = "/proc/self/exe";
@@ -650,13 +669,13 @@ bool GetSystemMemoryInfo(SystemMemoryInfoKB* meminfo) {
}
#if defined(OS_CHROMEOS)
- // Report on Chrome OS GEM object graphics memory. /var/run/debugfs_gpu is a
+ // Report on Chrome OS GEM object graphics memory. /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");
+ FilePath geminfo_file("/run/debugfs_gpu/exynos_gem_objects");
#else
- FilePath geminfo_file("/var/run/debugfs_gpu/i915_gem_objects");
+ FilePath geminfo_file("/run/debugfs_gpu/i915_gem_objects");
#endif
std::string geminfo_data;
meminfo->gem_objects = -1;
diff --git a/chromium/base/process/process_metrics_mac.cc b/chromium/base/process/process_metrics_mac.cc
index a07d3cd1049..f84b435a109 100644
--- a/chromium/base/process/process_metrics_mac.cc
+++ b/chromium/base/process/process_metrics_mac.cc
@@ -209,15 +209,32 @@ bool ProcessMetrics::GetMemoryBytes(size_t* private_bytes,
}
void ProcessMetrics::GetCommittedKBytes(CommittedKBytes* usage) const {
+ WorkingSetKBytes unused;
+ if (!GetCommittedAndWorkingSetKBytes(usage, &unused)) {
+ *usage = CommittedKBytes();
+ }
}
bool ProcessMetrics::GetWorkingSetKBytes(WorkingSetKBytes* ws_usage) const {
- size_t priv = GetWorkingSetSize();
- if (!priv)
+ CommittedKBytes unused;
+ return GetCommittedAndWorkingSetKBytes(&unused, ws_usage);
+}
+
+bool ProcessMetrics::GetCommittedAndWorkingSetKBytes(
+ CommittedKBytes* usage,
+ WorkingSetKBytes* ws_usage) const {
+ task_basic_info_64 task_info_data;
+ if (!GetTaskInfo(TaskForPid(process_), &task_info_data))
return false;
- ws_usage->priv = priv / 1024;
+
+ usage->priv = task_info_data.virtual_size / 1024;
+ usage->mapped = 0;
+ usage->image = 0;
+
+ ws_usage->priv = task_info_data.resident_size / 1024;
ws_usage->shareable = 0;
ws_usage->shared = 0;
+
return true;
}
diff --git a/chromium/base/process/process_metrics_posix.cc b/chromium/base/process/process_metrics_posix.cc
index ea79d7348ff..42b3f2d6655 100644
--- a/chromium/base/process/process_metrics_posix.cc
+++ b/chromium/base/process/process_metrics_posix.cc
@@ -12,9 +12,8 @@
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 *= Time::kMicrosecondsPerSecond;
ret += tv.tv_usec;
return ret;
}
@@ -69,4 +68,8 @@ void SetFdLimit(unsigned int max_descriptors) {
}
}
+size_t GetPageSize() {
+ return getpagesize();
+}
+
} // namespace base
diff --git a/chromium/base/process/process_metrics_unittest.cc b/chromium/base/process/process_metrics_unittest.cc
index 69f5e837cb9..76767b09a95 100644
--- a/chromium/base/process/process_metrics_unittest.cc
+++ b/chromium/base/process/process_metrics_unittest.cc
@@ -143,29 +143,29 @@ TEST_F(SystemMetricsTest, ParseMeminfo) {
"Hugepagesize: 4096 kB\n";
EXPECT_TRUE(ParseProcMeminfo(valid_input1, &meminfo));
- EXPECT_TRUE(meminfo.total == 3981504);
- EXPECT_TRUE(meminfo.free == 140764);
- EXPECT_TRUE(meminfo.buffers == 116480);
- EXPECT_TRUE(meminfo.cached == 406160);
- EXPECT_TRUE(meminfo.active_anon == 2972352);
- EXPECT_TRUE(meminfo.active_file == 179688);
- EXPECT_TRUE(meminfo.inactive_anon == 270108);
- EXPECT_TRUE(meminfo.inactive_file == 202748);
- EXPECT_TRUE(meminfo.swap_total == 5832280);
- EXPECT_TRUE(meminfo.swap_free == 3672368);
- EXPECT_TRUE(meminfo.dirty == 184);
+ EXPECT_EQ(meminfo.total, 3981504);
+ EXPECT_EQ(meminfo.free, 140764);
+ EXPECT_EQ(meminfo.buffers, 116480);
+ EXPECT_EQ(meminfo.cached, 406160);
+ EXPECT_EQ(meminfo.active_anon, 2972352);
+ EXPECT_EQ(meminfo.active_file, 179688);
+ EXPECT_EQ(meminfo.inactive_anon, 270108);
+ EXPECT_EQ(meminfo.inactive_file, 202748);
+ EXPECT_EQ(meminfo.swap_total, 5832280);
+ EXPECT_EQ(meminfo.swap_free, 3672368);
+ EXPECT_EQ(meminfo.dirty, 184);
#if defined(OS_CHROMEOS)
- EXPECT_TRUE(meminfo.shmem == 140204);
- EXPECT_TRUE(meminfo.slab == 54212);
+ EXPECT_EQ(meminfo.shmem, 140204);
+ EXPECT_EQ(meminfo.slab, 54212);
#endif
EXPECT_TRUE(ParseProcMeminfo(valid_input2, &meminfo));
- EXPECT_TRUE(meminfo.total == 255908);
- EXPECT_TRUE(meminfo.free == 69936);
- EXPECT_TRUE(meminfo.buffers == 15812);
- EXPECT_TRUE(meminfo.cached == 115124);
- EXPECT_TRUE(meminfo.swap_total == 524280);
- EXPECT_TRUE(meminfo.swap_free == 524200);
- EXPECT_TRUE(meminfo.dirty == 4);
+ EXPECT_EQ(meminfo.total, 255908);
+ EXPECT_EQ(meminfo.free, 69936);
+ EXPECT_EQ(meminfo.buffers, 15812);
+ EXPECT_EQ(meminfo.cached, 115124);
+ EXPECT_EQ(meminfo.swap_total, 524280);
+ EXPECT_EQ(meminfo.swap_free, 524200);
+ EXPECT_EQ(meminfo.dirty, 4);
}
TEST_F(SystemMetricsTest, ParseVmstat) {
@@ -260,13 +260,13 @@ TEST_F(SystemMetricsTest, ParseVmstat) {
"pgrefill_high 0\n"
"pgrefill_movable 0\n";
EXPECT_TRUE(ParseProcVmstat(valid_input1, &meminfo));
- EXPECT_TRUE(meminfo.pswpin == 179);
- EXPECT_TRUE(meminfo.pswpout == 406);
- EXPECT_TRUE(meminfo.pgmajfault == 487192);
+ EXPECT_EQ(meminfo.pswpin, 179);
+ EXPECT_EQ(meminfo.pswpout, 406);
+ EXPECT_EQ(meminfo.pgmajfault, 487192);
EXPECT_TRUE(ParseProcVmstat(valid_input2, &meminfo));
- EXPECT_TRUE(meminfo.pswpin == 12);
- EXPECT_TRUE(meminfo.pswpout == 901);
- EXPECT_TRUE(meminfo.pgmajfault == 2023);
+ EXPECT_EQ(meminfo.pswpin, 12);
+ EXPECT_EQ(meminfo.pswpout, 901);
+ EXPECT_EQ(meminfo.pgmajfault, 2023);
}
#endif // defined(OS_LINUX) || defined(OS_ANDROID)
@@ -323,6 +323,17 @@ TEST(ProcessMetricsTest, ParseProcStatCPU) {
"3221224832 3221224344 3086339742 0 0 0 0 0 0 0 17 0 0 0";
EXPECT_EQ(0, base::ParseProcStatCPU(kSelfStat));
+
+ // Some weird long-running process with a weird name that I created for the
+ // purposes of this test.
+ const char kWeirdNameStat[] = "26115 (Hello) You ())) ) R 24614 26115 24614"
+ " 34839 26115 4218880 227 0 0 0 "
+ "5186 11 0 0 "
+ "20 0 1 0 36933953 4296704 90 18446744073709551615 4194304 4196116 "
+ "140735857761568 140735857761160 4195644 0 0 0 0 0 0 0 17 14 0 0 0 0 0 "
+ "6295056 6295616 16519168 140735857770710 140735857770737 "
+ "140735857770737 140735857774557 0";
+ EXPECT_EQ(5186 + 11, base::ParseProcStatCPU(kWeirdNameStat));
}
#endif // defined(OS_LINUX) || defined(OS_ANDROID)
diff --git a/chromium/base/process/process_metrics_win.cc b/chromium/base/process/process_metrics_win.cc
index 16db44f63f6..1dd97e629cf 100644
--- a/chromium/base/process/process_metrics_win.cc
+++ b/chromium/base/process/process_metrics_win.cc
@@ -284,4 +284,8 @@ size_t GetSystemCommitCharge() {
return (info.CommitTotal * system_info.dwPageSize) / 1024;
}
+size_t GetPageSize() {
+ return PAGESIZE_KB * 1024;
+}
+
} // namespace base
diff --git a/chromium/base/process/process_posix.cc b/chromium/base/process/process_posix.cc
index ea8fd8cea1e..8037da3ac6e 100644
--- a/chromium/base/process/process_posix.cc
+++ b/chromium/base/process/process_posix.cc
@@ -1,20 +1,217 @@
-// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Copyright 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 <sys/wait.h>
+#include "base/files/scoped_file.h"
#include "base/logging.h"
+#include "base/posix/eintr_wrapper.h"
#include "base/process/kill.h"
+#include "base/third_party/dynamic_annotations/dynamic_annotations.h"
+
+#if defined(OS_MACOSX)
+#include <sys/event.h>
+#endif
+
+namespace {
+
+#if !defined(OS_NACL_NONSFI)
+
+bool WaitpidWithTimeout(base::ProcessHandle handle,
+ int* status,
+ base::TimeDelta wait) {
+ // This POSIX version of this function only guarantees that we wait no less
+ // than |wait| 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|, 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.
+
+ if (wait == base::TimeDelta::Max()) {
+ return HANDLE_EINTR(waitpid(handle, status, 0)) > 0;
+ }
+
+ 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.
+ base::TimeTicks wakeup_time = base::TimeTicks::Now() + wait;
+ while (ret_pid == 0) {
+ base::TimeTicks now = base::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;
+ }
+ }
+
+ return ret_pid > 0;
+}
+
+#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(base::ProcessHandle handle,
+ base::TimeDelta wait) {
+ DCHECK_GT(handle, 0);
+ DCHECK_GT(wait, base::TimeDelta());
+
+ base::ScopedFD kq(kqueue());
+ if (!kq.is_valid()) {
+ DPLOG(ERROR) << "kqueue";
+ return false;
+ }
+
+ struct kevent change = {0};
+ EV_SET(&change, handle, EVFILT_PROC, EV_ADD, NOTE_EXIT, 0, NULL);
+ int result = HANDLE_EINTR(kevent(kq.get(), &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 == base::TimeDelta::Max());
+ 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.get(), 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 WaitForExitWithTimeoutImpl(base::ProcessHandle handle,
+ int* exit_code,
+ base::TimeDelta timeout) {
+ base::ProcessHandle parent_pid = base::GetParentProcessId(handle);
+ base::ProcessHandle our_pid = base::GetCurrentProcessHandle();
+ if (parent_pid != our_pid) {
+#if defined(OS_MACOSX)
+ // On Mac we can wait on non child processes.
+ return WaitForSingleNonChildProcess(handle, timeout);
+#else
+ // Currently on Linux we can't handle non child processes.
+ NOTIMPLEMENTED();
+#endif // OS_MACOSX
+ }
+
+ int status;
+ if (!WaitpidWithTimeout(handle, &status, timeout))
+ return false;
+ if (WIFSIGNALED(status)) {
+ *exit_code = -1;
+ return true;
+ }
+ if (WIFEXITED(status)) {
+ *exit_code = WEXITSTATUS(status);
+ return true;
+ }
+ return false;
+}
+#endif // !defined(OS_NACL_NONSFI)
+
+} // namespace
namespace base {
Process::Process(ProcessHandle handle) : process_(handle) {
- CHECK_NE(handle, GetCurrentProcessHandle());
+}
+
+Process::~Process() {
}
Process::Process(RValue other)
@@ -32,17 +229,36 @@ Process& Process::operator=(RValue other) {
// static
Process Process::Current() {
- Process process;
- process.process_ = GetCurrentProcessHandle();
- return process.Pass();
+ return Process(GetCurrentProcessHandle());
+}
+
+// static
+Process Process::Open(ProcessId pid) {
+ if (pid == GetCurrentProcId())
+ return Current();
+
+ // On POSIX process handles are the same as PIDs.
+ return Process(pid);
+}
+
+// static
+Process Process::OpenWithExtraPrivileges(ProcessId pid) {
+ // On POSIX there are no privileges to set.
+ return Open(pid);
}
-#if !defined(OS_LINUX)
+// static
+Process Process::DeprecatedGetProcessFromHandle(ProcessHandle handle) {
+ DCHECK_NE(handle, GetCurrentProcessHandle());
+ return Process(handle);
+}
+
+#if !defined(OS_LINUX) && !defined(OS_MACOSX)
// static
bool Process::CanBackgroundProcesses() {
return false;
}
-#endif // !defined(OS_LINUX)
+#endif // !defined(OS_LINUX) && !defined(OS_MACOSX)
bool Process::IsValid() const {
return process_ != kNullProcessHandle;
@@ -59,7 +275,7 @@ Process Process::Duplicate() const {
return Process(process_);
}
-ProcessId Process::pid() const {
+ProcessId Process::Pid() const {
DCHECK(IsValid());
return GetProcId(process_);
}
@@ -75,15 +291,69 @@ void Process::Close() {
// end up w/ a zombie when it does finally exit.
}
-void Process::Terminate(int result_code) {
+#if !defined(OS_NACL_NONSFI)
+bool Process::Terminate(int exit_code, bool wait) const {
// result_code isn't supportable.
DCHECK(IsValid());
- // We don't wait here. It's the responsibility of other code to reap the
- // child.
- KillProcess(process_, result_code, false);
+ DCHECK_GT(process_, 1);
+ bool result = kill(process_, 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_, NULL, WNOHANG));
+ if (pid == process_) {
+ 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_;
+ }
+
+ 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_, SIGKILL) == 0;
+ }
+
+ if (!result)
+ DPLOG(ERROR) << "Unable to terminate process " << process_;
+
+ return result;
+}
+#endif // !defined(OS_NACL_NONSFI)
+
+bool Process::WaitForExit(int* exit_code) {
+ return WaitForExitWithTimeout(TimeDelta::Max(), exit_code);
+}
+
+bool Process::WaitForExitWithTimeout(TimeDelta timeout, int* exit_code) {
+ return WaitForExitWithTimeoutImpl(Handle(), exit_code, timeout);
}
-#if !defined(OS_LINUX)
+#if !defined(OS_LINUX) && !defined(OS_MACOSX)
bool Process::IsProcessBackgrounded() const {
// See SetProcessBackgrounded().
DCHECK(IsValid());
@@ -91,17 +361,17 @@ bool Process::IsProcessBackgrounded() const {
}
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.
- DCHECK(IsValid());
+ // Not implemented for POSIX systems other than Mac and Linux. With POSIX, if
+ // we were to lower the process priority we wouldn't be able to raise it back
+ // to its initial priority.
+ NOTIMPLEMENTED();
return false;
}
-#endif // !defined(OS_LINUX)
+#endif // !defined(OS_LINUX) && !defined(OS_MACOSX)
int Process::GetPriority() const {
DCHECK(IsValid());
return getpriority(PRIO_PROCESS, process_);
}
-} // namspace base
+} // namespace base
diff --git a/chromium/base/process/process_unittest.cc b/chromium/base/process/process_unittest.cc
index 66d6e634661..e094c032f3b 100644
--- a/chromium/base/process/process_unittest.cc
+++ b/chromium/base/process/process_unittest.cc
@@ -11,6 +11,9 @@
#include "testing/gtest/include/gtest/gtest.h"
#include "testing/multiprocess_func_list.h"
+#if defined(OS_MACOSX)
+#include <mach/mach.h>
+#endif // OS_MACOSX
namespace {
@@ -69,7 +72,7 @@ TEST_F(ProcessTest, Duplicate) {
Process process2 = process1.Duplicate();
ASSERT_TRUE(process1.IsValid());
ASSERT_TRUE(process2.IsValid());
- EXPECT_EQ(process1.pid(), process2.pid());
+ EXPECT_EQ(process1.Pid(), process2.Pid());
EXPECT_FALSE(process1.is_current());
EXPECT_FALSE(process2.is_current());
@@ -84,7 +87,7 @@ TEST_F(ProcessTest, DuplicateCurrent) {
Process process2 = process1.Duplicate();
ASSERT_TRUE(process1.IsValid());
ASSERT_TRUE(process2.IsValid());
- EXPECT_EQ(process1.pid(), process2.pid());
+ EXPECT_EQ(process1.Pid(), process2.Pid());
EXPECT_TRUE(process1.is_current());
EXPECT_TRUE(process2.is_current());
@@ -92,6 +95,21 @@ TEST_F(ProcessTest, DuplicateCurrent) {
ASSERT_TRUE(process2.IsValid());
}
+TEST_F(ProcessTest, DeprecatedGetProcessFromHandle) {
+ Process process1(SpawnChild("SimpleChildProcess"));
+ ASSERT_TRUE(process1.IsValid());
+
+ Process process2 = Process::DeprecatedGetProcessFromHandle(process1.Handle());
+ ASSERT_TRUE(process1.IsValid());
+ ASSERT_TRUE(process2.IsValid());
+ EXPECT_EQ(process1.Pid(), process2.Pid());
+ EXPECT_FALSE(process1.is_current());
+ EXPECT_FALSE(process2.is_current());
+
+ process1.Close();
+ ASSERT_TRUE(process2.IsValid());
+}
+
MULTIPROCESS_TEST_MAIN(SleepyChildProcess) {
PlatformThread::Sleep(TestTimeouts::action_max_timeout());
return 0;
@@ -109,8 +127,9 @@ TEST_F(ProcessTest, Terminate) {
exit_code = kDummyExitCode;
int kExpectedExitCode = 250;
- process.Terminate(kExpectedExitCode);
- WaitForSingleProcess(process.Handle(), TestTimeouts::action_max_timeout());
+ process.Terminate(kExpectedExitCode, false);
+ process.WaitForExitWithTimeout(TestTimeouts::action_max_timeout(),
+ &exit_code);
EXPECT_NE(TERMINATION_STATUS_STILL_RUNNING,
GetTerminationStatus(process.Handle(), &exit_code));
@@ -120,6 +139,34 @@ TEST_F(ProcessTest, Terminate) {
#endif
}
+MULTIPROCESS_TEST_MAIN(FastSleepyChildProcess) {
+ PlatformThread::Sleep(TestTimeouts::tiny_timeout() * 10);
+ return 0;
+}
+
+TEST_F(ProcessTest, WaitForExit) {
+ Process process(SpawnChild("FastSleepyChildProcess"));
+ ASSERT_TRUE(process.IsValid());
+
+ const int kDummyExitCode = 42;
+ int exit_code = kDummyExitCode;
+ EXPECT_TRUE(process.WaitForExit(&exit_code));
+ EXPECT_EQ(0, exit_code);
+}
+
+TEST_F(ProcessTest, WaitForExitWithTimeout) {
+ Process process(SpawnChild("SleepyChildProcess"));
+ ASSERT_TRUE(process.IsValid());
+
+ const int kDummyExitCode = 42;
+ int exit_code = kDummyExitCode;
+ TimeDelta timeout = TestTimeouts::tiny_timeout();
+ EXPECT_FALSE(process.WaitForExitWithTimeout(timeout, &exit_code));
+ EXPECT_EQ(kDummyExitCode, exit_code);
+
+ process.Terminate(kDummyExitCode, false);
+}
+
// 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
@@ -127,7 +174,19 @@ TEST_F(ProcessTest, Terminate) {
TEST_F(ProcessTest, SetProcessBackgrounded) {
Process process(SpawnChild("SimpleChildProcess"));
int old_priority = process.GetPriority();
-#if defined(OS_WIN)
+#if defined(OS_MACOSX)
+ // On the Mac, backgrounding a process requires a port to that process.
+ // In the browser it's available through the MachBroker class, which is not
+ // part of base. Additionally, there is an indefinite amount of time between
+ // spawning a process and receiving its port. Because this test just checks
+ // the ability to background/foreground a process, we can use the current
+ // process's port instead.
+ mach_port_t process_port = mach_task_self();
+ EXPECT_TRUE(process.SetProcessBackgrounded(process_port, true));
+ EXPECT_TRUE(process.IsProcessBackgrounded(process_port));
+ EXPECT_TRUE(process.SetProcessBackgrounded(process_port, false));
+ EXPECT_FALSE(process.IsProcessBackgrounded(process_port));
+#elif defined(OS_WIN)
EXPECT_TRUE(process.SetProcessBackgrounded(true));
EXPECT_TRUE(process.IsProcessBackgrounded());
EXPECT_TRUE(process.SetProcessBackgrounded(false));
@@ -145,7 +204,13 @@ TEST_F(ProcessTest, SetProcessBackgrounded) {
TEST_F(ProcessTest, SetProcessBackgroundedSelf) {
Process process = Process::Current();
int old_priority = process.GetPriority();
-#if defined(OS_WIN)
+#if defined(OS_MACOSX)
+ mach_port_t process_port = mach_task_self();
+ EXPECT_TRUE(process.SetProcessBackgrounded(process_port, true));
+ EXPECT_TRUE(process.IsProcessBackgrounded(process_port));
+ EXPECT_TRUE(process.SetProcessBackgrounded(process_port, false));
+ EXPECT_FALSE(process.IsProcessBackgrounded(process_port));
+#elif defined(OS_WIN)
EXPECT_TRUE(process.SetProcessBackgrounded(true));
EXPECT_TRUE(process.IsProcessBackgrounded());
EXPECT_TRUE(process.SetProcessBackgrounded(false));
diff --git a/chromium/base/process/process_util_unittest.cc b/chromium/base/process/process_util_unittest.cc
index d846d1acfae..11d8874a52a 100644
--- a/chromium/base/process/process_util_unittest.cc
+++ b/chromium/base/process/process_util_unittest.cc
@@ -10,6 +10,8 @@
#include "base/debug/alias.h"
#include "base/debug/stack_trace.h"
#include "base/files/file_path.h"
+#include "base/files/file_util.h"
+#include "base/files/scoped_file.h"
#include "base/logging.h"
#include "base/memory/scoped_ptr.h"
#include "base/path_service.h"
@@ -27,21 +29,26 @@
#include "base/third_party/dynamic_annotations/dynamic_annotations.h"
#include "base/threading/platform_thread.h"
#include "base/threading/thread.h"
+#include "build/build_config.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "testing/multiprocess_func_list.h"
#if defined(OS_LINUX)
#include <malloc.h>
#include <sched.h>
+#include <sys/syscall.h>
#endif
#if defined(OS_POSIX)
#include <dlfcn.h>
#include <errno.h>
#include <fcntl.h>
+#include <sched.h>
#include <signal.h>
#include <sys/resource.h>
#include <sys/socket.h>
+#include <sys/types.h>
#include <sys/wait.h>
+#include <unistd.h>
#endif
#if defined(OS_WIN)
#include <windows.h>
@@ -142,11 +149,11 @@ MULTIPROCESS_TEST_MAIN(SimpleChildProcess) {
// TODO(viettrungluu): This should be in a "MultiProcessTestTest".
TEST_F(ProcessUtilTest, SpawnChild) {
- base::ProcessHandle handle = SpawnChild("SimpleChildProcess");
- ASSERT_NE(base::kNullProcessHandle, handle);
- EXPECT_TRUE(base::WaitForSingleProcess(
- handle, TestTimeouts::action_max_timeout()));
- base::CloseProcessHandle(handle);
+ base::Process process = SpawnChild("SimpleChildProcess");
+ ASSERT_TRUE(process.IsValid());
+ int exit_code;
+ EXPECT_TRUE(process.WaitForExitWithTimeout(
+ TestTimeouts::action_max_timeout(), &exit_code));
}
MULTIPROCESS_TEST_MAIN(SlowChildProcess) {
@@ -158,12 +165,12 @@ TEST_F(ProcessUtilTest, KillSlowChild) {
const std::string signal_file =
ProcessUtilTest::GetSignalFilePath(kSignalFileSlow);
remove(signal_file.c_str());
- base::ProcessHandle handle = SpawnChild("SlowChildProcess");
- ASSERT_NE(base::kNullProcessHandle, handle);
+ base::Process process = SpawnChild("SlowChildProcess");
+ ASSERT_TRUE(process.IsValid());
SignalChildren(signal_file.c_str());
- EXPECT_TRUE(base::WaitForSingleProcess(
- handle, TestTimeouts::action_max_timeout()));
- base::CloseProcessHandle(handle);
+ int exit_code;
+ EXPECT_TRUE(process.WaitForExitWithTimeout(
+ TestTimeouts::action_max_timeout(), &exit_code));
remove(signal_file.c_str());
}
@@ -172,21 +179,20 @@ TEST_F(ProcessUtilTest, DISABLED_GetTerminationStatusExit) {
const std::string signal_file =
ProcessUtilTest::GetSignalFilePath(kSignalFileSlow);
remove(signal_file.c_str());
- base::ProcessHandle handle = SpawnChild("SlowChildProcess");
- ASSERT_NE(base::kNullProcessHandle, handle);
+ base::Process process = SpawnChild("SlowChildProcess");
+ ASSERT_TRUE(process.IsValid());
int exit_code = 42;
EXPECT_EQ(base::TERMINATION_STATUS_STILL_RUNNING,
- base::GetTerminationStatus(handle, &exit_code));
+ base::GetTerminationStatus(process.Handle(), &exit_code));
EXPECT_EQ(kExpectedStillRunningExitCode, exit_code);
SignalChildren(signal_file.c_str());
exit_code = 42;
base::TerminationStatus status =
- WaitForChildTermination(handle, &exit_code);
+ WaitForChildTermination(process.Handle(), &exit_code);
EXPECT_EQ(base::TERMINATION_STATUS_NORMAL_TERMINATION, status);
EXPECT_EQ(0, exit_code);
- base::CloseProcessHandle(handle);
remove(signal_file.c_str());
}
@@ -195,12 +201,11 @@ TEST_F(ProcessUtilTest, DISABLED_GetTerminationStatusExit) {
TEST_F(ProcessUtilTest, GetProcId) {
base::ProcessId id1 = base::GetProcId(GetCurrentProcess());
EXPECT_NE(0ul, id1);
- base::ProcessHandle handle = SpawnChild("SimpleChildProcess");
- ASSERT_NE(base::kNullProcessHandle, handle);
- base::ProcessId id2 = base::GetProcId(handle);
+ base::Process process = SpawnChild("SimpleChildProcess");
+ ASSERT_TRUE(process.IsValid());
+ base::ProcessId id2 = process.Pid();
EXPECT_NE(0ul, id2);
EXPECT_NE(id1, id2);
- base::CloseProcessHandle(handle);
}
#endif
@@ -239,18 +244,18 @@ TEST_F(ProcessUtilTest, MAYBE_GetTerminationStatusCrash) {
const std::string signal_file =
ProcessUtilTest::GetSignalFilePath(kSignalFileCrash);
remove(signal_file.c_str());
- base::ProcessHandle handle = SpawnChild("CrashingChildProcess");
- ASSERT_NE(base::kNullProcessHandle, handle);
+ base::Process process = SpawnChild("CrashingChildProcess");
+ ASSERT_TRUE(process.IsValid());
int exit_code = 42;
EXPECT_EQ(base::TERMINATION_STATUS_STILL_RUNNING,
- base::GetTerminationStatus(handle, &exit_code));
+ base::GetTerminationStatus(process.Handle(), &exit_code));
EXPECT_EQ(kExpectedStillRunningExitCode, exit_code);
SignalChildren(signal_file.c_str());
exit_code = 42;
base::TerminationStatus status =
- WaitForChildTermination(handle, &exit_code);
+ WaitForChildTermination(process.Handle(), &exit_code);
EXPECT_EQ(base::TERMINATION_STATUS_PROCESS_CRASHED, status);
#if defined(OS_WIN)
@@ -261,7 +266,6 @@ TEST_F(ProcessUtilTest, MAYBE_GetTerminationStatusCrash) {
int signal = WTERMSIG(exit_code);
EXPECT_EQ(SIGSEGV, signal);
#endif
- base::CloseProcessHandle(handle);
// Reset signal handlers back to "normal".
base::debug::EnableInProcessStackDumping();
@@ -286,18 +290,18 @@ TEST_F(ProcessUtilTest, GetTerminationStatusKill) {
const std::string signal_file =
ProcessUtilTest::GetSignalFilePath(kSignalFileKill);
remove(signal_file.c_str());
- base::ProcessHandle handle = SpawnChild("KilledChildProcess");
- ASSERT_NE(base::kNullProcessHandle, handle);
+ base::Process process = SpawnChild("KilledChildProcess");
+ ASSERT_TRUE(process.IsValid());
int exit_code = 42;
EXPECT_EQ(base::TERMINATION_STATUS_STILL_RUNNING,
- base::GetTerminationStatus(handle, &exit_code));
+ base::GetTerminationStatus(process.Handle(), &exit_code));
EXPECT_EQ(kExpectedStillRunningExitCode, exit_code);
SignalChildren(signal_file.c_str());
exit_code = 42;
base::TerminationStatus status =
- WaitForChildTermination(handle, &exit_code);
+ WaitForChildTermination(process.Handle(), &exit_code);
EXPECT_EQ(base::TERMINATION_STATUS_PROCESS_WAS_KILLED, status);
#if defined(OS_WIN)
EXPECT_EQ(kExpectedKilledExitCode, exit_code);
@@ -307,7 +311,6 @@ TEST_F(ProcessUtilTest, GetTerminationStatusKill) {
int signal = WTERMSIG(exit_code);
EXPECT_EQ(SIGKILL, signal);
#endif
- base::CloseProcessHandle(handle);
remove(signal_file.c_str());
}
@@ -325,7 +328,7 @@ TEST_F(ProcessUtilTest, GetAppOutput) {
expected += "\r\n";
FilePath cmd(L"cmd.exe");
- CommandLine cmd_line(cmd);
+ base::CommandLine cmd_line(cmd);
cmd_line.AppendArg("/c");
cmd_line.AppendArg("echo " + message + "");
std::string output;
@@ -333,7 +336,7 @@ TEST_F(ProcessUtilTest, GetAppOutput) {
EXPECT_EQ(expected, output);
// Let's make sure stderr is ignored.
- CommandLine other_cmd_line(cmd);
+ base::CommandLine other_cmd_line(cmd);
other_cmd_line.AppendArg("/c");
// http://msdn.microsoft.com/library/cc772622.aspx
cmd_line.AppendArg("echo " + message + " >&2");
@@ -348,22 +351,23 @@ TEST_F(ProcessUtilTest, LaunchAsUser) {
ASSERT_TRUE(OpenProcessToken(GetCurrentProcess(), TOKEN_ALL_ACCESS, &token));
base::LaunchOptions options;
options.as_user = token;
- EXPECT_TRUE(base::LaunchProcess(MakeCmdLine("SimpleChildProcess"), options,
- NULL));
+ EXPECT_TRUE(base::LaunchProcess(MakeCmdLine("SimpleChildProcess"),
+ options).IsValid());
}
static const char kEventToTriggerHandleSwitch[] = "event-to-trigger-handle";
MULTIPROCESS_TEST_MAIN(TriggerEventChildProcess) {
std::string handle_value_string =
- CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
+ base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
kEventToTriggerHandleSwitch);
CHECK(!handle_value_string.empty());
uint64 handle_value_uint64;
CHECK(base::StringToUint64(handle_value_string, &handle_value_uint64));
// Give ownership of the handle to |event|.
- base::WaitableEvent event(reinterpret_cast<HANDLE>(handle_value_uint64));
+ base::WaitableEvent event(base::win::ScopedHandle(
+ reinterpret_cast<HANDLE>(handle_value_uint64)));
event.Signal();
@@ -378,26 +382,26 @@ TEST_F(ProcessUtilTest, InheritSpecifiedHandles) {
security_attributes.bInheritHandle = true;
// Takes ownership of the event handle.
- base::WaitableEvent event(
- CreateEvent(&security_attributes, true, false, NULL));
+ base::WaitableEvent event(base::win::ScopedHandle(
+ CreateEvent(&security_attributes, true, false, NULL)));
base::HandlesToInheritVector handles_to_inherit;
handles_to_inherit.push_back(event.handle());
base::LaunchOptions options;
options.handles_to_inherit = &handles_to_inherit;
- CommandLine cmd_line = MakeCmdLine("TriggerEventChildProcess");
+ base::CommandLine cmd_line = MakeCmdLine("TriggerEventChildProcess");
cmd_line.AppendSwitchASCII(kEventToTriggerHandleSwitch,
base::Uint64ToString(reinterpret_cast<uint64>(event.handle())));
// This functionality actually requires Vista or later. Make sure that it
// fails properly on XP.
if (base::win::GetVersion() < base::win::VERSION_VISTA) {
- EXPECT_FALSE(base::LaunchProcess(cmd_line, options, NULL));
+ EXPECT_FALSE(base::LaunchProcess(cmd_line, options).IsValid());
return;
}
// Launch the process and wait for it to trigger the event.
- ASSERT_TRUE(base::LaunchProcess(cmd_line, options, NULL));
+ ASSERT_TRUE(base::LaunchProcess(cmd_line, options).IsValid());
EXPECT_TRUE(event.TimedWait(TestTimeouts::action_max_timeout()));
}
#endif // defined(OS_WIN)
@@ -534,9 +538,9 @@ int ProcessUtilTest::CountOpenFDsInChild() {
fd_mapping_vec.push_back(std::pair<int, int>(fds[1], kChildPipe));
base::LaunchOptions options;
options.fds_to_remap = &fd_mapping_vec;
- base::ProcessHandle handle =
+ base::Process process =
SpawnChildWithOptions("ProcessUtilsLeakFDChildProcess", options);
- CHECK(handle);
+ CHECK(process.IsValid());
int ret = IGNORE_EINTR(close(fds[1]));
DPCHECK(ret == 0);
@@ -548,11 +552,12 @@ int ProcessUtilTest::CountOpenFDsInChild() {
#if defined(THREAD_SANITIZER)
// Compiler-based ThreadSanitizer makes this test slow.
- CHECK(base::WaitForSingleProcess(handle, base::TimeDelta::FromSeconds(3)));
+ base::TimeDelta timeout = base::TimeDelta::FromSeconds(3);
#else
- CHECK(base::WaitForSingleProcess(handle, base::TimeDelta::FromSeconds(1)));
+ base::TimeDelta timeout = base::TimeDelta::FromSeconds(1);
#endif
- base::CloseProcessHandle(handle);
+ int exit_code;
+ CHECK(process.WaitForExitWithTimeout(timeout, &exit_code));
ret = IGNORE_EINTR(close(fds[0]));
DPCHECK(ret == 0);
@@ -611,7 +616,7 @@ std::string TestLaunchProcess(const std::vector<std::string>& args,
#else
CHECK_EQ(0, clone_flags);
#endif // OS_LINUX
- EXPECT_TRUE(base::LaunchProcess(args, options, NULL));
+ EXPECT_TRUE(base::LaunchProcess(args, options).IsValid());
PCHECK(IGNORE_EINTR(close(fds[1])) == 0);
char buf[512];
@@ -683,10 +688,8 @@ TEST_F(ProcessUtilTest, LaunchProcess) {
// 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(
- echo_base_test, env_changes, no_clear_environ, CLONE_FS | SIGCHLD));
+ EXPECT_EQ("wibble\n", TestLaunchProcess(echo_base_test, env_changes,
+ no_clear_environ, CLONE_FS));
}
EXPECT_EQ(
@@ -710,27 +713,29 @@ TEST_F(ProcessUtilTest, GetAppOutput) {
argv.push_back("-c");
argv.push_back("exit 0");
- EXPECT_TRUE(base::GetAppOutput(CommandLine(argv), &output));
+ EXPECT_TRUE(base::GetAppOutput(base::CommandLine(argv), &output));
EXPECT_STREQ("", output.c_str());
argv[2] = "exit 1";
- EXPECT_FALSE(base::GetAppOutput(CommandLine(argv), &output));
+ EXPECT_FALSE(base::GetAppOutput(base::CommandLine(argv), &output));
EXPECT_STREQ("", output.c_str());
argv[2] = "echo foobar42";
- EXPECT_TRUE(base::GetAppOutput(CommandLine(argv), &output));
+ EXPECT_TRUE(base::GetAppOutput(base::CommandLine(argv), &output));
EXPECT_STREQ("foobar42\n", output.c_str());
#else
- EXPECT_TRUE(base::GetAppOutput(CommandLine(FilePath("true")), &output));
+ EXPECT_TRUE(base::GetAppOutput(base::CommandLine(FilePath("true")),
+ &output));
EXPECT_STREQ("", output.c_str());
- EXPECT_FALSE(base::GetAppOutput(CommandLine(FilePath("false")), &output));
+ EXPECT_FALSE(base::GetAppOutput(base::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_TRUE(base::GetAppOutput(base::CommandLine(argv), &output));
EXPECT_STREQ("foobar42", output.c_str());
#endif // defined(OS_ANDROID)
}
@@ -754,34 +759,39 @@ TEST_F(ProcessUtilTest, MAYBE_GetAppOutputRestricted) {
// 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_TRUE(base::GetAppOutputRestricted(base::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_FALSE(base::GetAppOutputRestricted(base::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_TRUE(base::GetAppOutputRestricted(base::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_TRUE(base::GetAppOutputRestricted(base::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_TRUE(base::GetAppOutputRestricted(base::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_TRUE(base::GetAppOutputRestricted(base::CommandLine(argv), &output,
+ 0));
EXPECT_STREQ("", output.c_str());
}
@@ -796,11 +806,13 @@ TEST_F(ProcessUtilTest, GetAppOutputRestrictedSIGPIPE) {
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_TRUE(base::GetAppOutputRestricted(base::CommandLine(argv), &output,
+ 10));
EXPECT_STREQ("1234567890", output.c_str());
#else
argv.push_back("yes");
- EXPECT_TRUE(base::GetAppOutputRestricted(CommandLine(argv), &output, 10));
+ EXPECT_TRUE(base::GetAppOutputRestricted(base::CommandLine(argv), &output,
+ 10));
EXPECT_STREQ("y\ny\ny\ny\ny\n", output.c_str());
#endif
}
@@ -826,14 +838,16 @@ TEST_F(ProcessUtilTest, MAYBE_GetAppOutputRestrictedNoZombies) {
// 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_TRUE(base::GetAppOutputRestricted(base::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_TRUE(base::GetAppOutputRestricted(base::CommandLine(argv), &output,
+ 10));
EXPECT_STREQ("1234567890", output.c_str());
}
}
@@ -846,7 +860,7 @@ TEST_F(ProcessUtilTest, GetAppOutputWithExitCode) {
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,
+ EXPECT_TRUE(base::GetAppOutputWithExitCode(base::CommandLine(argv), &output,
&exit_code));
EXPECT_STREQ("foo\n", output.c_str());
EXPECT_EQ(exit_code, 0);
@@ -855,7 +869,7 @@ TEST_F(ProcessUtilTest, GetAppOutputWithExitCode) {
// code.
output.clear();
argv[2] = "echo foo; exit 2";
- EXPECT_TRUE(base::GetAppOutputWithExitCode(CommandLine(argv), &output,
+ EXPECT_TRUE(base::GetAppOutputWithExitCode(base::CommandLine(argv), &output,
&exit_code));
EXPECT_STREQ("foo\n", output.c_str());
EXPECT_EQ(exit_code, 2);
@@ -876,14 +890,15 @@ bool IsProcessDead(base::ProcessHandle child) {
}
TEST_F(ProcessUtilTest, DelayedTermination) {
- base::ProcessHandle child_process = SpawnChild("process_util_test_never_die");
- ASSERT_TRUE(child_process);
- base::EnsureProcessTerminated(child_process);
- base::WaitForSingleProcess(child_process, base::TimeDelta::FromSeconds(5));
+ base::Process child_process = SpawnChild("process_util_test_never_die");
+ ASSERT_TRUE(child_process.IsValid());
+ base::EnsureProcessTerminated(child_process.Duplicate());
+ int exit_code;
+ child_process.WaitForExitWithTimeout(base::TimeDelta::FromSeconds(5),
+ &exit_code);
// Check that process was really killed.
- EXPECT_TRUE(IsProcessDead(child_process));
- base::CloseProcessHandle(child_process);
+ EXPECT_TRUE(IsProcessDead(child_process.Handle()));
}
MULTIPROCESS_TEST_MAIN(process_util_test_never_die) {
@@ -894,20 +909,159 @@ MULTIPROCESS_TEST_MAIN(process_util_test_never_die) {
}
TEST_F(ProcessUtilTest, ImmediateTermination) {
- base::ProcessHandle child_process =
- SpawnChild("process_util_test_die_immediately");
- ASSERT_TRUE(child_process);
+ base::Process child_process = SpawnChild("process_util_test_die_immediately");
+ ASSERT_TRUE(child_process.IsValid());
// Give it time to die.
sleep(2);
- base::EnsureProcessTerminated(child_process);
+ base::EnsureProcessTerminated(child_process.Duplicate());
// Check that process was really killed.
- EXPECT_TRUE(IsProcessDead(child_process));
- base::CloseProcessHandle(child_process);
+ EXPECT_TRUE(IsProcessDead(child_process.Handle()));
}
MULTIPROCESS_TEST_MAIN(process_util_test_die_immediately) {
return 0;
}
+#if !defined(OS_ANDROID)
+const char kPipeValue = '\xcc';
+
+class ReadFromPipeDelegate : public base::LaunchOptions::PreExecDelegate {
+ public:
+ explicit ReadFromPipeDelegate(int fd) : fd_(fd) {}
+ ~ReadFromPipeDelegate() override {}
+ void RunAsyncSafe() override {
+ char c;
+ RAW_CHECK(HANDLE_EINTR(read(fd_, &c, 1)) == 1);
+ RAW_CHECK(IGNORE_EINTR(close(fd_)) == 0);
+ RAW_CHECK(c == kPipeValue);
+ }
+
+ private:
+ int fd_;
+ DISALLOW_COPY_AND_ASSIGN(ReadFromPipeDelegate);
+};
+
+TEST_F(ProcessUtilTest, PreExecHook) {
+ int pipe_fds[2];
+ ASSERT_EQ(0, pipe(pipe_fds));
+
+ base::ScopedFD read_fd(pipe_fds[0]);
+ base::ScopedFD write_fd(pipe_fds[1]);
+ base::FileHandleMappingVector fds_to_remap;
+ fds_to_remap.push_back(std::make_pair(read_fd.get(), read_fd.get()));
+
+ ReadFromPipeDelegate read_from_pipe_delegate(read_fd.get());
+ base::LaunchOptions options;
+ options.fds_to_remap = &fds_to_remap;
+ options.pre_exec_delegate = &read_from_pipe_delegate;
+ base::Process process(SpawnChildWithOptions("SimpleChildProcess", options));
+ ASSERT_TRUE(process.IsValid());
+
+ read_fd.reset();
+ ASSERT_EQ(1, HANDLE_EINTR(write(write_fd.get(), &kPipeValue, 1)));
+
+ int exit_code = 42;
+ EXPECT_TRUE(process.WaitForExit(&exit_code));
+ EXPECT_EQ(0, exit_code);
+}
+#endif // !defined(OS_ANDROID)
+
#endif // defined(OS_POSIX)
+
+#if defined(OS_LINUX)
+const int kSuccess = 0;
+
+MULTIPROCESS_TEST_MAIN(CheckPidProcess) {
+ const pid_t kInitPid = 1;
+ const pid_t pid = syscall(__NR_getpid);
+ CHECK(pid == kInitPid);
+ CHECK(getpid() == pid);
+ return kSuccess;
+}
+
+TEST_F(ProcessUtilTest, CloneFlags) {
+ if (RunningOnValgrind() ||
+ !base::PathExists(FilePath("/proc/self/ns/user")) ||
+ !base::PathExists(FilePath("/proc/self/ns/pid"))) {
+ // User or PID namespaces are not supported.
+ return;
+ }
+
+ base::LaunchOptions options;
+ options.clone_flags = CLONE_NEWUSER | CLONE_NEWPID;
+
+ base::Process process(SpawnChildWithOptions("CheckPidProcess", options));
+ ASSERT_TRUE(process.IsValid());
+
+ int exit_code = 42;
+ EXPECT_TRUE(process.WaitForExit(&exit_code));
+ EXPECT_EQ(kSuccess, exit_code);
+}
+
+TEST(ForkWithFlagsTest, UpdatesPidCache) {
+ // The libc clone function, which allows ForkWithFlags to keep the pid cache
+ // up to date, does not work on Valgrind.
+ if (RunningOnValgrind()) {
+ return;
+ }
+
+ // Warm up the libc pid cache, if there is one.
+ ASSERT_EQ(syscall(__NR_getpid), getpid());
+
+ pid_t ctid = 0;
+ const pid_t pid =
+ base::ForkWithFlags(SIGCHLD | CLONE_CHILD_SETTID, nullptr, &ctid);
+ if (pid == 0) {
+ // In child. Check both the raw getpid syscall and the libc getpid wrapper
+ // (which may rely on a pid cache).
+ RAW_CHECK(syscall(__NR_getpid) == ctid);
+ RAW_CHECK(getpid() == ctid);
+ _exit(kSuccess);
+ }
+
+ ASSERT_NE(-1, pid);
+ int status = 42;
+ ASSERT_EQ(pid, HANDLE_EINTR(waitpid(pid, &status, 0)));
+ ASSERT_TRUE(WIFEXITED(status));
+ EXPECT_EQ(kSuccess, WEXITSTATUS(status));
+}
+
+MULTIPROCESS_TEST_MAIN(CheckCwdProcess) {
+ base::FilePath expected;
+ CHECK(base::GetTempDir(&expected));
+ base::FilePath actual;
+ CHECK(base::GetCurrentDirectory(&actual));
+ CHECK(actual == expected);
+ return kSuccess;
+}
+
+TEST_F(ProcessUtilTest, CurrentDirectory) {
+ // TODO(rickyz): Add support for passing arguments to multiprocess children,
+ // then create a special directory for this test.
+ base::FilePath tmp_dir;
+ ASSERT_TRUE(base::GetTempDir(&tmp_dir));
+
+ base::LaunchOptions options;
+ options.current_directory = tmp_dir;
+
+ base::Process process(SpawnChildWithOptions("CheckCwdProcess", options));
+ ASSERT_TRUE(process.IsValid());
+
+ int exit_code = 42;
+ EXPECT_TRUE(process.WaitForExit(&exit_code));
+ EXPECT_EQ(kSuccess, exit_code);
+}
+
+TEST_F(ProcessUtilTest, InvalidCurrentDirectory) {
+ base::LaunchOptions options;
+ options.current_directory = base::FilePath("/dev/null");
+
+ base::Process process(SpawnChildWithOptions("SimpleChildProcess", options));
+ ASSERT_TRUE(process.IsValid());
+
+ int exit_code = kSuccess;
+ EXPECT_TRUE(process.WaitForExit(&exit_code));
+ EXPECT_NE(kSuccess, exit_code);
+}
+#endif
diff --git a/chromium/base/process/process_win.cc b/chromium/base/process/process_win.cc
index 05041b20dda..0d312a35990 100644
--- a/chromium/base/process/process_win.cc
+++ b/chromium/base/process/process_win.cc
@@ -6,8 +6,18 @@
#include "base/logging.h"
#include "base/memory/scoped_ptr.h"
+#include "base/metrics/field_trial.h"
+#include "base/numerics/safe_conversions.h"
+#include "base/process/kill.h"
#include "base/win/windows_version.h"
+namespace {
+
+DWORD kBasicProcessAccess =
+ PROCESS_TERMINATE | PROCESS_QUERY_INFORMATION | SYNCHRONIZE;
+
+} // namespace
+
namespace base {
Process::Process(ProcessHandle handle)
@@ -22,6 +32,9 @@ Process::Process(RValue other)
other.object->Close();
}
+Process::~Process() {
+}
+
Process& Process::operator=(RValue other) {
if (this != other.object) {
process_.Set(other.object->process_.Take());
@@ -39,6 +52,34 @@ Process Process::Current() {
}
// static
+Process Process::Open(ProcessId pid) {
+ return Process(::OpenProcess(kBasicProcessAccess, FALSE, pid));
+}
+
+// static
+Process Process::OpenWithExtraPrivileges(ProcessId pid) {
+ DWORD access = kBasicProcessAccess | PROCESS_DUP_HANDLE | PROCESS_VM_READ;
+ return Process(::OpenProcess(access, FALSE, pid));
+}
+
+// static
+Process Process::OpenWithAccess(ProcessId pid, DWORD desired_access) {
+ return Process(::OpenProcess(desired_access, FALSE, pid));
+}
+
+// static
+Process Process::DeprecatedGetProcessFromHandle(ProcessHandle handle) {
+ DCHECK_NE(handle, ::GetCurrentProcess());
+ ProcessHandle out_handle;
+ if (!::DuplicateHandle(GetCurrentProcess(), handle,
+ GetCurrentProcess(), &out_handle,
+ 0, FALSE, DUPLICATE_SAME_ACCESS)) {
+ return Process();
+ }
+ return Process(out_handle);
+}
+
+// static
bool Process::CanBackgroundProcesses() {
return true;
}
@@ -68,7 +109,7 @@ Process Process::Duplicate() const {
return Process(out_handle);
}
-ProcessId Process::pid() const {
+ProcessId Process::Pid() const {
DCHECK(IsValid());
return GetProcId(Handle());
}
@@ -85,17 +126,36 @@ void Process::Close() {
process_.Close();
}
-void Process::Terminate(int result_code) {
+bool Process::Terminate(int exit_code, bool wait) const {
DCHECK(IsValid());
+ bool result = (::TerminateProcess(Handle(), exit_code) != FALSE);
+ if (result && wait) {
+ // The process may not end immediately due to pending I/O
+ if (::WaitForSingleObject(Handle(), 60 * 1000) != WAIT_OBJECT_0)
+ DPLOG(ERROR) << "Error waiting for process exit";
+ } else if (!result) {
+ DPLOG(ERROR) << "Unable to terminate process";
+ }
+ return result;
+}
+
+bool Process::WaitForExit(int* exit_code) {
+ return WaitForExitWithTimeout(TimeDelta::FromMilliseconds(INFINITE),
+ exit_code);
+}
- // 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(Handle(), result_code);
+bool Process::WaitForExitWithTimeout(TimeDelta timeout, int* exit_code) {
+ // Limit timeout to INFINITE.
+ DWORD timeout_ms = saturated_cast<DWORD>(timeout.InMilliseconds());
+ if (::WaitForSingleObject(Handle(), timeout_ms) != 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 Process::IsProcessBackgrounded() const {
@@ -117,7 +177,21 @@ bool Process::SetProcessBackgrounded(bool value) {
priority = value ? PROCESS_MODE_BACKGROUND_BEGIN :
PROCESS_MODE_BACKGROUND_END;
} else {
- priority = value ? BELOW_NORMAL_PRIORITY_CLASS : NORMAL_PRIORITY_CLASS;
+ // Experiment (http://crbug.com/458594) with using IDLE_PRIORITY_CLASS as a
+ // background priority for background renderers (this code path is
+ // technically for more than just the renderers but they're the only use
+ // case in practice and experimenting here direclty is thus easier -- plus
+ // it doesn't really hurt as above we already state our intent of using
+ // PROCESS_MODE_BACKGROUND_BEGIN if available which is essentially
+ // IDLE_PRIORITY_CLASS plus lowered IO priority). Enabled by default in the
+ // asbence of field trials to get coverage on the perf waterfall.
+ DWORD background_priority = IDLE_PRIORITY_CLASS;
+ base::FieldTrial* trial =
+ base::FieldTrialList::Find("BackgroundRendererProcesses");
+ if (trial && trial->group_name() == "AllowBelowNormalFromBrowser")
+ background_priority = BELOW_NORMAL_PRIORITY_CLASS;
+
+ priority = value ? background_priority : NORMAL_PRIORITY_CLASS;
}
return (::SetPriorityClass(Handle(), priority) != 0);