summaryrefslogtreecommitdiff
path: root/base/process
diff options
context:
space:
mode:
Diffstat (limited to 'base/process')
-rw-r--r--base/process/internal_linux.cc41
-rw-r--r--base/process/internal_linux.h8
-rw-r--r--base/process/kill.h11
-rw-r--r--base/process/kill_posix.cc2
-rw-r--r--base/process/launch.cc36
-rw-r--r--base/process/launch.h53
-rw-r--r--base/process/launch_mac.cc153
-rw-r--r--base/process/launch_posix.cc21
-rw-r--r--base/process/memory.cc54
-rw-r--r--base/process/memory.h83
-rw-r--r--base/process/memory_linux.cc212
-rw-r--r--base/process/memory_stubs.cc49
-rw-r--r--base/process/port_provider_mac.cc3
-rw-r--r--base/process/process.h42
-rw-r--r--base/process/process_metrics.cc5
-rw-r--r--base/process/process_metrics.h32
-rw-r--r--base/process/process_metrics_linux.cc33
-rw-r--r--base/process/process_metrics_mac.cc46
-rw-r--r--base/process/process_metrics_posix.cc2
-rw-r--r--base/process/process_metrics_unittest.cc19
-rw-r--r--base/process/process_posix.cc33
21 files changed, 821 insertions, 117 deletions
diff --git a/base/process/internal_linux.cc b/base/process/internal_linux.cc
index d286f4e753..c7820040ce 100644
--- a/base/process/internal_linux.cc
+++ b/base/process/internal_linux.cc
@@ -133,9 +133,10 @@ size_t GetProcStatsFieldAsSizeT(const std::vector<std::string>& proc_stats,
return StringToSizeT(proc_stats[field_num], &value) ? value : 0;
}
-int64_t ReadProcStatsAndGetFieldAsInt64(pid_t pid, ProcStatsFields field_num) {
+int64_t ReadStatFileAndGetFieldAsInt64(const FilePath& stat_file,
+ ProcStatsFields field_num) {
std::string stats_data;
- if (!ReadProcStats(pid, &stats_data))
+ if (!ReadProcFile(stat_file, &stats_data))
return 0;
std::vector<std::string> proc_stats;
if (!ParseProcStats(stats_data, &proc_stats))
@@ -143,6 +144,16 @@ int64_t ReadProcStatsAndGetFieldAsInt64(pid_t pid, ProcStatsFields field_num) {
return GetProcStatsFieldAsInt64(proc_stats, field_num);
}
+int64_t ReadProcStatsAndGetFieldAsInt64(pid_t pid, ProcStatsFields field_num) {
+ FilePath stat_file = internal::GetProcPidDir(pid).Append(kStatFile);
+ return ReadStatFileAndGetFieldAsInt64(stat_file, field_num);
+}
+
+int64_t ReadProcSelfStatsAndGetFieldAsInt64(ProcStatsFields field_num) {
+ FilePath stat_file = FilePath(kProcDir).Append("self").Append(kStatFile);
+ return ReadStatFileAndGetFieldAsInt64(stat_file, field_num);
+}
+
size_t ReadProcStatsAndGetFieldAsSizeT(pid_t pid,
ProcStatsFields field_num) {
std::string stats_data;
@@ -170,6 +181,32 @@ Time GetBootTime() {
return Time::FromTimeT(btime);
}
+TimeDelta GetUserCpuTimeSinceBoot() {
+ FilePath path("/proc/stat");
+ std::string contents;
+ if (!ReadProcFile(path, &contents))
+ return TimeDelta();
+
+ ProcStatMap proc_stat;
+ ParseProcStat(contents, &proc_stat);
+ ProcStatMap::const_iterator cpu_it = proc_stat.find("cpu");
+ if (cpu_it == proc_stat.end())
+ return TimeDelta();
+
+ std::vector<std::string> cpu = SplitString(
+ cpu_it->second, kWhitespaceASCII, TRIM_WHITESPACE, SPLIT_WANT_NONEMPTY);
+
+ if (cpu.size() < 2 || cpu[0] != "cpu")
+ return TimeDelta();
+
+ uint64_t user;
+ uint64_t nice;
+ if (!StringToUint64(cpu[0], &user) || !StringToUint64(cpu[1], &nice))
+ return TimeDelta();
+
+ return ClockTicksToTimeDelta(user + nice);
+}
+
TimeDelta ClockTicksToTimeDelta(int clock_ticks) {
// This queries the /proc-specific scaling factor which is
// conceptually the system hertz. To dump this value on another
diff --git a/base/process/internal_linux.h b/base/process/internal_linux.h
index ba793f7cc7..99d0fd5af1 100644
--- a/base/process/internal_linux.h
+++ b/base/process/internal_linux.h
@@ -72,9 +72,12 @@ int64_t GetProcStatsFieldAsInt64(const std::vector<std::string>& proc_stats,
size_t GetProcStatsFieldAsSizeT(const std::vector<std::string>& proc_stats,
ProcStatsFields field_num);
-// Convenience wrapper around GetProcStatsFieldAsInt64(), ParseProcStats() and
+// Convenience wrappers around GetProcStatsFieldAsInt64(), ParseProcStats() and
// ReadProcStats(). See GetProcStatsFieldAsInt64() for details.
+int64_t ReadStatsFilendGetFieldAsInt64(const FilePath& stat_file,
+ ProcStatsFields field_num);
int64_t ReadProcStatsAndGetFieldAsInt64(pid_t pid, ProcStatsFields field_num);
+int64_t ReadProcSelfStatsAndGetFieldAsInt64(ProcStatsFields field_num);
// Same as ReadProcStatsAndGetFieldAsInt64() but for size_t values.
size_t ReadProcStatsAndGetFieldAsSizeT(pid_t pid,
@@ -83,6 +86,9 @@ size_t ReadProcStatsAndGetFieldAsSizeT(pid_t pid,
// Returns the time that the OS started. Clock ticks are relative to this.
Time GetBootTime();
+// Returns the amount of time spent in user space since boot across all CPUs.
+TimeDelta GetUserCpuTimeSinceBoot();
+
// Converts Linux clock ticks to a wall time delta.
TimeDelta ClockTicksToTimeDelta(int clock_ticks);
diff --git a/base/process/kill.h b/base/process/kill.h
index c664f33262..6d410e02a0 100644
--- a/base/process/kill.h
+++ b/base/process/kill.h
@@ -18,6 +18,16 @@ namespace base {
class ProcessFilter;
+#if defined(OS_WIN)
+namespace win {
+
+// See definition in sandbox/win/src/sandbox_types.h
+const DWORD kSandboxFatalMemoryExceeded = 7012;
+
+} // namespace win
+
+#endif // OS_WIN
+
// Return status values from GetTerminationStatus. Don't use these as
// exit code arguments to KillProcess*(), use platform/application
// specific values instead.
@@ -39,6 +49,7 @@ enum TerminationStatus {
TERMINATION_STATUS_OOM_PROTECTED, // child was protected from oom kill
#endif
TERMINATION_STATUS_LAUNCH_FAILED, // child process never launched
+ TERMINATION_STATUS_OOM, // Process died due to oom
TERMINATION_STATUS_MAX_ENUM
};
diff --git a/base/process/kill_posix.cc b/base/process/kill_posix.cc
index 85470e05f9..4dc60ef2a2 100644
--- a/base/process/kill_posix.cc
+++ b/base/process/kill_posix.cc
@@ -52,6 +52,8 @@ TerminationStatus GetTerminationStatusImpl(ProcessHandle handle,
case SIGFPE:
case SIGILL:
case SIGSEGV:
+ case SIGTRAP:
+ case SIGSYS:
return TERMINATION_STATUS_PROCESS_CRASHED;
case SIGKILL:
#if defined(OS_CHROMEOS)
diff --git a/base/process/launch.cc b/base/process/launch.cc
index 3ca5155a12..c03e1a75db 100644
--- a/base/process/launch.cc
+++ b/base/process/launch.cc
@@ -7,43 +7,11 @@
namespace base {
-LaunchOptions::LaunchOptions()
- : wait(false),
-#if defined(OS_WIN)
- start_hidden(false),
- handles_to_inherit(NULL),
- inherit_handles(false),
- as_user(NULL),
- empty_desktop_name(false),
- job_handle(NULL),
- stdin_handle(NULL),
- stdout_handle(NULL),
- stderr_handle(NULL),
- force_breakaway_from_job_(false)
-#else
- clear_environ(false),
- fds_to_remap(NULL),
- maximize_rlimits(NULL),
- new_process_group(false)
-#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
-#endif // !defined(OS_WIN)
- {
-}
+LaunchOptions::LaunchOptions() = default;
LaunchOptions::LaunchOptions(const LaunchOptions& other) = default;
-LaunchOptions::~LaunchOptions() {
-}
+LaunchOptions::~LaunchOptions() = default;
LaunchOptions LaunchOptionsForTest() {
LaunchOptions options;
diff --git a/base/process/launch.h b/base/process/launch.h
index b8c02597a6..be8f6e73b9 100644
--- a/base/process/launch.h
+++ b/base/process/launch.h
@@ -63,17 +63,17 @@ struct BASE_EXPORT LaunchOptions {
~LaunchOptions();
// If true, wait for the process to complete.
- bool wait;
+ bool wait = false;
// If not empty, change to this directory before executing the new process.
base::FilePath current_directory;
#if defined(OS_WIN)
- bool start_hidden;
+ bool start_hidden = false;
// If non-null, inherit exactly the list of handles in this vector (these
// handles must be inheritable).
- HandlesToInheritVector* handles_to_inherit;
+ HandlesToInheritVector* handles_to_inherit = nullptr;
// If true, the new process inherits handles from the parent. In production
// code this flag should be used only when running short-lived, trusted
@@ -81,7 +81,7 @@ struct BASE_EXPORT LaunchOptions {
// leak to the child process, causing errors such as open socket hangs.
// Note: If |handles_to_inherit| is non-null, this flag is ignored and only
// those handles will be inherited.
- bool inherit_handles;
+ bool inherit_handles = false;
// If non-null, runs as if the user represented by the token had launched it.
// Whether the application is visible on the interactive desktop depends on
@@ -90,29 +90,29 @@ struct BASE_EXPORT LaunchOptions {
// To avoid hard to diagnose problems, when specified this loads the
// environment variables associated with the user and if this operation fails
// the entire call fails as well.
- UserTokenHandle as_user;
+ UserTokenHandle as_user = nullptr;
// If true, use an empty string for the desktop name.
- bool empty_desktop_name;
+ bool empty_desktop_name = false;
// If non-null, launches the application in that job object. The process will
// be terminated immediately and LaunchProcess() will fail if assignment to
// the job object fails.
- HANDLE job_handle;
+ HANDLE job_handle = nullptr;
// Handles for the redirection of stdin, stdout and stderr. The handles must
// be inheritable. Caller should either set all three of them or none (i.e.
// there is no way to redirect stderr without redirecting stdin). The
// |inherit_handles| flag must be set to true when redirecting stdio stream.
- HANDLE stdin_handle;
- HANDLE stdout_handle;
- HANDLE stderr_handle;
+ HANDLE stdin_handle = nullptr;
+ HANDLE stdout_handle = nullptr;
+ HANDLE stderr_handle = nullptr;
// If set to true, ensures that the child process is launched with the
// CREATE_BREAKAWAY_FROM_JOB flag which allows it to breakout of the parent
// job if any.
- bool force_breakaway_from_job_;
-#else
+ bool force_breakaway_from_job_ = false;
+#else // !defined(OS_WIN)
// Set/unset environment variables. These are applied on top of the parent
// process environment. Empty (the default) means to inherit the same
// environment. See AlterEnvironment().
@@ -120,53 +120,58 @@ struct BASE_EXPORT LaunchOptions {
// Clear the environment for the new process before processing changes from
// |environ|.
- bool clear_environ;
+ bool clear_environ = false;
// If non-null, remap file descriptors according to the mapping of
// src fd->dest fd to propagate FDs into the child process.
// This pointer is owned by the caller and must live through the
// call to LaunchProcess().
- const FileHandleMappingVector* fds_to_remap;
+ const FileHandleMappingVector* fds_to_remap = nullptr;
// Each element is an RLIMIT_* constant that should be raised to its
// rlim_max. This pointer is owned by the caller and must live through
// the call to LaunchProcess().
- const std::vector<int>* maximize_rlimits;
+ const std::vector<int>* maximize_rlimits = nullptr;
// If true, start the process in a new process group, instead of
// inheriting the parent's process group. The pgid of the child process
// will be the same as its pid.
- bool new_process_group;
+ bool new_process_group = false;
#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;
+ int clone_flags = 0;
// 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;
+ bool allow_new_privs = false;
// Sets parent process death signal to SIGKILL.
- bool kill_on_parent_death;
+ bool kill_on_parent_death = false;
#endif // defined(OS_LINUX)
#if defined(OS_POSIX)
+ // If not empty, launch the specified executable instead of
+ // cmdline.GetProgram(). This is useful when it is necessary to pass a custom
+ // argv[0].
+ base::FilePath real_path;
+
// 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;
+ PreExecDelegate* pre_exec_delegate = nullptr;
#endif // defined(OS_POSIX)
#if defined(OS_CHROMEOS)
// If non-negative, the specified file descriptor will be set as the launched
// process' controlling terminal.
- int ctrl_terminal_fd;
+ int ctrl_terminal_fd = -1;
#endif // defined(OS_CHROMEOS)
#endif // !defined(OS_WIN)
};
@@ -270,6 +275,12 @@ BASE_EXPORT bool GetAppOutputWithExitCode(const CommandLine& cl,
BASE_EXPORT void RaiseProcessToHighPriority();
#if defined(OS_MACOSX)
+// An implementation of LaunchProcess() that uses posix_spawn() instead of
+// fork()+exec(). This does not support the |pre_exec_delegate| and
+// |current_directory| options.
+Process LaunchProcessPosixSpawn(const std::vector<std::string>& argv,
+ const LaunchOptions& options);
+
// Restore the default exception handler, setting it to Apple Crash Reporter
// (ReportCrash). When forking and execing a new process, the child will
// inherit the parent's exception ports, which may be set to the Breakpad
diff --git a/base/process/launch_mac.cc b/base/process/launch_mac.cc
index 5895eae435..3732bc1ecc 100644
--- a/base/process/launch_mac.cc
+++ b/base/process/launch_mac.cc
@@ -4,13 +4,75 @@
#include "base/process/launch.h"
+#include <crt_externs.h>
#include <mach/mach.h>
-#include <servers/bootstrap.h>
+#include <spawn.h>
+#include <string.h>
+#include <sys/wait.h>
#include "base/logging.h"
+#include "base/posix/eintr_wrapper.h"
+#include "base/threading/thread_restrictions.h"
namespace base {
+namespace {
+
+// DPSXCHECK is a Debug Posix Spawn Check macro. The posix_spawn* family of
+// functions return an errno value, as opposed to setting errno directly. This
+// macro emulates a DPCHECK().
+#define DPSXCHECK(expr) \
+ do { \
+ int rv = (expr); \
+ DCHECK_EQ(rv, 0) << #expr << ": -" << rv << " " << strerror(rv); \
+ } while (0)
+
+class PosixSpawnAttr {
+ public:
+ PosixSpawnAttr() { DPSXCHECK(posix_spawnattr_init(&attr_)); }
+
+ ~PosixSpawnAttr() { DPSXCHECK(posix_spawnattr_destroy(&attr_)); }
+
+ posix_spawnattr_t* get() { return &attr_; }
+
+ private:
+ posix_spawnattr_t attr_;
+};
+
+class PosixSpawnFileActions {
+ public:
+ PosixSpawnFileActions() {
+ DPSXCHECK(posix_spawn_file_actions_init(&file_actions_));
+ }
+
+ ~PosixSpawnFileActions() {
+ DPSXCHECK(posix_spawn_file_actions_destroy(&file_actions_));
+ }
+
+ void Open(int filedes, const char* path, int mode) {
+ DPSXCHECK(posix_spawn_file_actions_addopen(&file_actions_, filedes, path,
+ mode, 0));
+ }
+
+ void Dup2(int filedes, int newfiledes) {
+ DPSXCHECK(
+ posix_spawn_file_actions_adddup2(&file_actions_, filedes, newfiledes));
+ }
+
+ void Inherit(int filedes) {
+ DPSXCHECK(posix_spawn_file_actions_addinherit_np(&file_actions_, filedes));
+ }
+
+ const posix_spawn_file_actions_t* get() const { return &file_actions_; }
+
+ private:
+ posix_spawn_file_actions_t file_actions_;
+
+ DISALLOW_COPY_AND_ASSIGN(PosixSpawnFileActions);
+};
+
+} // namespace
+
void RestoreDefaultExceptionHandler() {
// This function is tailored to remove the Breakpad exception handler.
// exception_mask matches s_exception_mask in
@@ -28,4 +90,93 @@ void RestoreDefaultExceptionHandler() {
EXCEPTION_DEFAULT, THREAD_STATE_NONE);
}
+Process LaunchProcessPosixSpawn(const std::vector<std::string>& argv,
+ const LaunchOptions& options) {
+ DCHECK(!options.pre_exec_delegate)
+ << "LaunchProcessPosixSpawn does not support PreExecDelegate";
+ DCHECK(options.current_directory.empty())
+ << "LaunchProcessPosixSpawn does not support current_directory";
+
+ PosixSpawnAttr attr;
+
+ short flags = POSIX_SPAWN_CLOEXEC_DEFAULT;
+ if (options.new_process_group) {
+ flags |= POSIX_SPAWN_SETPGROUP;
+ DPSXCHECK(posix_spawnattr_setpgroup(attr.get(), 0));
+ }
+ DPSXCHECK(posix_spawnattr_setflags(attr.get(), flags));
+
+ PosixSpawnFileActions file_actions;
+
+ // Process file descriptors for the child. By default, LaunchProcess will
+ // open stdin to /dev/null and inherit stdout and stderr.
+ bool inherit_stdout = true, inherit_stderr = true;
+ bool null_stdin = true;
+ if (options.fds_to_remap) {
+ for (const auto& dup2_pair : *options.fds_to_remap) {
+ if (dup2_pair.second == STDIN_FILENO) {
+ null_stdin = false;
+ } else if (dup2_pair.second == STDOUT_FILENO) {
+ inherit_stdout = false;
+ } else if (dup2_pair.second == STDERR_FILENO) {
+ inherit_stderr = false;
+ }
+
+ if (dup2_pair.first == dup2_pair.second) {
+ file_actions.Inherit(dup2_pair.second);
+ } else {
+ file_actions.Dup2(dup2_pair.first, dup2_pair.second);
+ }
+ }
+ }
+
+ if (null_stdin) {
+ file_actions.Open(STDIN_FILENO, "/dev/null", O_RDONLY);
+ }
+ if (inherit_stdout) {
+ file_actions.Inherit(STDOUT_FILENO);
+ }
+ if (inherit_stderr) {
+ file_actions.Inherit(STDERR_FILENO);
+ }
+
+ std::unique_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()] = nullptr;
+
+ std::unique_ptr<char* []> owned_environ;
+ char** new_environ = options.clear_environ ? nullptr : *_NSGetEnviron();
+ if (!options.environ.empty()) {
+ owned_environ = AlterEnvironment(new_environ, options.environ);
+ new_environ = owned_environ.get();
+ }
+
+ const char* executable_path = !options.real_path.empty()
+ ? options.real_path.value().c_str()
+ : argv_cstr[0];
+
+ // Use posix_spawnp as some callers expect to have PATH consulted.
+ pid_t pid;
+ int rv = posix_spawnp(&pid, executable_path, file_actions.get(), attr.get(),
+ &argv_cstr[0], new_environ);
+
+ if (rv != 0) {
+ DLOG(ERROR) << "posix_spawnp(" << executable_path << "): -" << rv << " "
+ << strerror(rv);
+ return Process();
+ }
+
+ if (options.wait) {
+ // While this isn't strictly disk IO, waiting for another process to
+ // finish is the sort of thing ThreadRestrictions is trying to prevent.
+ base::ThreadRestrictions::AssertIOAllowed();
+ pid_t ret = HANDLE_EINTR(waitpid(pid, nullptr, 0));
+ DPCHECK(ret > 0);
+ }
+
+ return Process(pid);
+}
+
} // namespace base
diff --git a/base/process/launch_posix.cc b/base/process/launch_posix.cc
index 4fb1018276..19effa2ce5 100644
--- a/base/process/launch_posix.cc
+++ b/base/process/launch_posix.cc
@@ -60,6 +60,8 @@
#if defined(OS_MACOSX)
#include <crt_externs.h>
#include <sys/event.h>
+
+#include "base/feature_list.h"
#else
extern char** environ;
#endif
@@ -70,6 +72,11 @@ namespace base {
namespace {
+#if defined(OS_MACOSX)
+const Feature kMacLaunchProcessPosixSpawn{"MacLaunchProcessPosixSpawn",
+ FEATURE_ENABLED_BY_DEFAULT};
+#endif
+
// Get the process's "environment" (i.e. the thing that setenv/getenv
// work with).
char** GetEnvironment() {
@@ -296,6 +303,15 @@ Process LaunchProcess(const CommandLine& cmdline,
Process LaunchProcess(const std::vector<std::string>& argv,
const LaunchOptions& options) {
+#if defined(OS_MACOSX)
+ if (FeatureList::IsEnabled(kMacLaunchProcessPosixSpawn)) {
+ // TODO(rsesek): Do this unconditionally. There is one user for each of
+ // these two options. https://crbug.com/179923.
+ if (!options.pre_exec_delegate && options.current_directory.empty())
+ return LaunchProcessPosixSpawn(argv, options);
+ }
+#endif
+
size_t fd_shuffle_size = 0;
if (options.fds_to_remap) {
fd_shuffle_size = options.fds_to_remap->size();
@@ -492,7 +508,10 @@ Process LaunchProcess(const std::vector<std::string>& argv,
options.pre_exec_delegate->RunAsyncSafe();
}
- execvp(argv_cstr[0], argv_cstr.get());
+ const char* executable_path = !options.real_path.empty() ?
+ options.real_path.value().c_str() : argv_cstr[0];
+
+ execvp(executable_path, argv_cstr.get());
RAW_LOG(ERROR, "LaunchProcess: failed to execvp:");
RAW_LOG(ERROR, argv_cstr[0]);
diff --git a/base/process/memory.cc b/base/process/memory.cc
new file mode 100644
index 0000000000..6349c08ca0
--- /dev/null
+++ b/base/process/memory.cc
@@ -0,0 +1,54 @@
+// Copyright 2014 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/debug/alias.h"
+#include "base/logging.h"
+#include "base/process/memory.h"
+#include "build/build_config.h"
+
+namespace base {
+
+// Defined in memory_win.cc for Windows.
+#if !defined(OS_WIN)
+
+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);
+}
+
+#endif
+
+// Defined in memory_mac.mm for Mac.
+#if !defined(OS_MACOSX)
+
+bool UncheckedCalloc(size_t num_items, size_t size, void** result) {
+ const size_t alloc_size = num_items * size;
+
+ // Overflow check
+ if (size && ((alloc_size / size) != num_items)) {
+ *result = NULL;
+ return false;
+ }
+
+ if (!UncheckedMalloc(alloc_size, result))
+ return false;
+
+ memset(*result, 0, alloc_size);
+ return true;
+}
+
+#endif
+
+} // namespace base
diff --git a/base/process/memory.h b/base/process/memory.h
new file mode 100644
index 0000000000..77911cfc35
--- /dev/null
+++ b/base/process/memory.h
@@ -0,0 +1,83 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_PROCESS_MEMORY_H_
+#define BASE_PROCESS_MEMORY_H_
+
+#include <stddef.h>
+
+#include "base/base_export.h"
+#include "base/process/process_handle.h"
+#include "build/build_config.h"
+
+#ifdef PVALLOC_AVAILABLE
+// Build config explicitly tells us whether or not pvalloc is available.
+#elif defined(LIBC_GLIBC) && !defined(USE_TCMALLOC)
+#define PVALLOC_AVAILABLE 1
+#else
+#define PVALLOC_AVAILABLE 0
+#endif
+
+namespace base {
+
+// Enables 'terminate on heap corruption' flag. Helps protect against heap
+// overflow. Has no effect if the OS doesn't provide the necessary facility.
+BASE_EXPORT void EnableTerminationOnHeapCorruption();
+
+// Turns on process termination if memory runs out.
+BASE_EXPORT void EnableTerminationOnOutOfMemory();
+
+// 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_LINUX) || defined(OS_ANDROID)
+BASE_EXPORT extern size_t g_oom_size;
+
+// The maximum allowed value for the OOM score.
+const int kMaxOomScore = 1000;
+
+// This adjusts /proc/<pid>/oom_score_adj so the Linux OOM killer will
+// prefer to kill certain process types over others. The range for the
+// adjustment is [-1000, 1000], with [0, 1000] being user accessible.
+// If the Linux system doesn't support the newer oom_score_adj range
+// of [0, 1000], then we revert to using the older oom_adj, and
+// translate the given value into [0, 15]. Some aliasing of values
+// may occur in that case, of course.
+BASE_EXPORT bool AdjustOOMScore(ProcessId process, int score);
+#endif
+
+#if defined(OS_WIN)
+namespace win {
+
+// Custom Windows exception code chosen to indicate an out of memory error.
+// See https://msdn.microsoft.com/en-us/library/het71c37.aspx.
+// "To make sure that you do not define a code that conflicts with an existing
+// exception code" ... "The resulting error code should therefore have the
+// highest four bits set to hexadecimal E."
+// 0xe0000008 was chosen arbitrarily, as 0x00000008 is ERROR_NOT_ENOUGH_MEMORY.
+const DWORD kOomExceptionCode = 0xe0000008;
+
+} // namespace win
+#endif
+
+// Special allocator functions for callers that want to check for OOM.
+// These will not abort if the allocation fails even if
+// EnableTerminationOnOutOfMemory has been called.
+// This can be useful for huge and/or unpredictable size memory allocations.
+// Please only use this if you really handle the case when the allocation
+// fails. Doing otherwise would risk security.
+// These functions may still crash on OOM when running under memory tools,
+// specifically ASan and other sanitizers.
+// Return value tells whether the allocation succeeded. If it fails |result| is
+// set to NULL, otherwise it holds the memory address.
+BASE_EXPORT WARN_UNUSED_RESULT bool UncheckedMalloc(size_t size,
+ void** result);
+BASE_EXPORT WARN_UNUSED_RESULT bool UncheckedCalloc(size_t num_items,
+ size_t size,
+ void** result);
+
+} // namespace base
+
+#endif // BASE_PROCESS_MEMORY_H_
diff --git a/base/process/memory_linux.cc b/base/process/memory_linux.cc
new file mode 100644
index 0000000000..985bc54eb6
--- /dev/null
+++ b/base/process/memory_linux.cc
@@ -0,0 +1,212 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/process/memory.h"
+
+#include <stddef.h>
+
+#include <new>
+
+#include "base/allocator/allocator_shim.h"
+#include "base/allocator/features.h"
+#include "base/files/file_path.h"
+#include "base/files/file_util.h"
+#include "base/logging.h"
+#include "base/process/internal_linux.h"
+#include "base/strings/string_number_conversions.h"
+#include "build/build_config.h"
+
+#if defined(USE_TCMALLOC)
+#include "third_party/tcmalloc/chromium/src/gperftools/tcmalloc.h"
+#endif
+
+namespace base {
+
+size_t g_oom_size = 0U;
+
+namespace {
+
+void OnNoMemorySize(size_t size) {
+ g_oom_size = size;
+
+ if (size != 0)
+ LOG(FATAL) << "Out of memory, size = " << size;
+ LOG(FATAL) << "Out of memory.";
+}
+
+void OnNoMemory() {
+ OnNoMemorySize(0);
+}
+
+} // namespace
+
+// TODO(primiano): Once the unified shim is on by default (crbug.com/550886)
+// get rid of the code in this entire #if section. The whole termination-on-OOM
+// logic is implemented in the shim.
+#if !defined(ADDRESS_SANITIZER) && !defined(MEMORY_SANITIZER) && \
+ !defined(THREAD_SANITIZER) && !defined(LEAK_SANITIZER) && \
+ !BUILDFLAG(USE_EXPERIMENTAL_ALLOCATOR_SHIM)
+
+#if defined(LIBC_GLIBC) && !defined(USE_TCMALLOC)
+
+extern "C" {
+void* __libc_malloc(size_t size);
+void* __libc_realloc(void* ptr, size_t size);
+void* __libc_calloc(size_t nmemb, size_t size);
+void* __libc_valloc(size_t size);
+#if PVALLOC_AVAILABLE == 1
+void* __libc_pvalloc(size_t size);
+#endif
+void* __libc_memalign(size_t alignment, size_t size);
+
+// Overriding the system memory allocation functions:
+//
+// For security reasons, we want malloc failures to be fatal. Too much code
+// doesn't check for a NULL return value from malloc and unconditionally uses
+// the resulting pointer. If the first offset that they try to access is
+// attacker controlled, then the attacker can direct the code to access any
+// part of memory.
+//
+// Thus, we define all the standard malloc functions here and mark them as
+// visibility 'default'. This means that they replace the malloc functions for
+// all Chromium code and also for all code in shared libraries. There are tests
+// for this in process_util_unittest.cc.
+//
+// If we are using tcmalloc, then the problem is moot since tcmalloc handles
+// this for us. Thus this code is in a !defined(USE_TCMALLOC) block.
+//
+// If we are testing the binary with AddressSanitizer, we should not
+// redefine malloc and let AddressSanitizer do it instead.
+//
+// We call the real libc functions in this code by using __libc_malloc etc.
+// Previously we tried using dlsym(RTLD_NEXT, ...) but that failed depending on
+// the link order. Since ld.so needs calloc during symbol resolution, it
+// defines its own versions of several of these functions in dl-minimal.c.
+// Depending on the runtime library order, dlsym ended up giving us those
+// functions and bad things happened. See crbug.com/31809
+//
+// This means that any code which calls __libc_* gets the raw libc versions of
+// these functions.
+
+#define DIE_ON_OOM_1(function_name) \
+ void* function_name(size_t) __attribute__ ((visibility("default"))); \
+ \
+ void* function_name(size_t size) { \
+ void* ret = __libc_##function_name(size); \
+ if (ret == NULL && size != 0) \
+ OnNoMemorySize(size); \
+ return ret; \
+ }
+
+#define DIE_ON_OOM_2(function_name, arg1_type) \
+ void* function_name(arg1_type, size_t) \
+ __attribute__ ((visibility("default"))); \
+ \
+ void* function_name(arg1_type arg1, size_t size) { \
+ void* ret = __libc_##function_name(arg1, size); \
+ if (ret == NULL && size != 0) \
+ OnNoMemorySize(size); \
+ return ret; \
+ }
+
+DIE_ON_OOM_1(malloc)
+DIE_ON_OOM_1(valloc)
+#if PVALLOC_AVAILABLE == 1
+DIE_ON_OOM_1(pvalloc)
+#endif
+
+DIE_ON_OOM_2(calloc, size_t)
+DIE_ON_OOM_2(realloc, void*)
+DIE_ON_OOM_2(memalign, size_t)
+
+// posix_memalign has a unique signature and doesn't have a __libc_ variant.
+int posix_memalign(void** ptr, size_t alignment, size_t size)
+ __attribute__ ((visibility("default")));
+
+int posix_memalign(void** ptr, size_t alignment, size_t size) {
+ // This will use the safe version of memalign, above.
+ *ptr = memalign(alignment, size);
+ return 0;
+}
+
+} // extern C
+
+#else
+
+// TODO(mostynb@opera.com): dlsym dance
+
+#endif // LIBC_GLIBC && !USE_TCMALLOC
+
+#endif // !*_SANITIZER
+
+void EnableTerminationOnHeapCorruption() {
+ // On Linux, there nothing to do AFAIK.
+}
+
+void EnableTerminationOnOutOfMemory() {
+ // Set the new-out of memory handler.
+ std::set_new_handler(&OnNoMemory);
+ // If we're using glibc's allocator, the above functions will override
+ // malloc and friends and make them die on out of memory.
+
+#if BUILDFLAG(USE_EXPERIMENTAL_ALLOCATOR_SHIM)
+ allocator::SetCallNewHandlerOnMallocFailure(true);
+#elif defined(USE_TCMALLOC)
+ // For tcmalloc, we need to tell it to behave like new.
+ tc_set_new_mode(1);
+#endif
+}
+
+// NOTE: This is not the only version of this function in the source:
+// the setuid sandbox (in process_util_linux.c, in the sandbox source)
+// also has its own C version.
+bool AdjustOOMScore(ProcessId process, int score) {
+ if (score < 0 || score > kMaxOomScore)
+ return false;
+
+ FilePath oom_path(internal::GetProcPidDir(process));
+
+ // Attempt to write the newer oom_score_adj file first.
+ FilePath oom_file = oom_path.AppendASCII("oom_score_adj");
+ if (PathExists(oom_file)) {
+ std::string score_str = IntToString(score);
+ DVLOG(1) << "Adjusting oom_score_adj of " << process << " to "
+ << score_str;
+ int score_len = static_cast<int>(score_str.length());
+ return (score_len == WriteFile(oom_file, score_str.c_str(), score_len));
+ }
+
+ // If the oom_score_adj file doesn't exist, then we write the old
+ // style file and translate the oom_adj score to the range 0-15.
+ oom_file = oom_path.AppendASCII("oom_adj");
+ if (PathExists(oom_file)) {
+ // Max score for the old oom_adj range. Used for conversion of new
+ // values to old values.
+ const int kMaxOldOomScore = 15;
+
+ int converted_score = score * kMaxOldOomScore / kMaxOomScore;
+ std::string score_str = IntToString(converted_score);
+ DVLOG(1) << "Adjusting oom_adj of " << process << " to " << score_str;
+ int score_len = static_cast<int>(score_str.length());
+ return (score_len == WriteFile(oom_file, score_str.c_str(), score_len));
+ }
+
+ return false;
+}
+
+bool UncheckedMalloc(size_t size, void** result) {
+#if BUILDFLAG(USE_EXPERIMENTAL_ALLOCATOR_SHIM)
+ *result = allocator::UncheckedAlloc(size);
+#elif defined(MEMORY_TOOL_REPLACES_ALLOCATOR) || \
+ (!defined(LIBC_GLIBC) && !defined(USE_TCMALLOC))
+ *result = malloc(size);
+#elif defined(LIBC_GLIBC) && !defined(USE_TCMALLOC)
+ *result = __libc_malloc(size);
+#elif defined(USE_TCMALLOC)
+ *result = tc_malloc_skip_new_handler(size);
+#endif
+ return *result != NULL;
+}
+
+} // namespace base
diff --git a/base/process/memory_stubs.cc b/base/process/memory_stubs.cc
new file mode 100644
index 0000000000..67deb4f58b
--- /dev/null
+++ b/base/process/memory_stubs.cc
@@ -0,0 +1,49 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/process/memory.h"
+
+#include <stddef.h>
+#include <stdlib.h>
+
+#include "base/compiler_specific.h"
+
+namespace base {
+
+void EnableTerminationOnOutOfMemory() {
+}
+
+void EnableTerminationOnHeapCorruption() {
+}
+
+bool AdjustOOMScore(ProcessId process, int score) {
+ ALLOW_UNUSED_PARAM(process);
+ ALLOW_UNUSED_PARAM(score);
+ return false;
+}
+
+void TerminateBecauseOutOfMemory(size_t size) {
+ ALLOW_UNUSED_PARAM(size);
+ abort();
+}
+
+// UncheckedMalloc and Calloc exist so that platforms making use of
+// EnableTerminationOnOutOfMemory have a way to allocate memory without
+// crashing. This _stubs.cc file is for platforms that do not support
+// EnableTerminationOnOutOfMemory (note the empty implementation above). As
+// such, these two Unchecked.alloc functions need only trivially pass-through to
+// their respective stdlib function since those functions will return null on a
+// failure to allocate.
+
+bool UncheckedMalloc(size_t size, void** result) {
+ *result = malloc(size);
+ return *result != nullptr;
+}
+
+bool UncheckedCalloc(size_t num_items, size_t size, void** result) {
+ *result = calloc(num_items, size);
+ return *result != nullptr;
+}
+
+} // namespace base
diff --git a/base/process/port_provider_mac.cc b/base/process/port_provider_mac.cc
index ac13949ac8..23d214c3f3 100644
--- a/base/process/port_provider_mac.cc
+++ b/base/process/port_provider_mac.cc
@@ -21,7 +21,8 @@ void PortProvider::RemoveObserver(Observer* observer) {
void PortProvider::NotifyObservers(ProcessHandle process) {
base::AutoLock l(lock_);
- FOR_EACH_OBSERVER(Observer, observer_list_, OnReceivedTaskPort(process));
+ for (auto& observer : observer_list_)
+ observer.OnReceivedTaskPort(process);
}
} // namespace base
diff --git a/base/process/process.h b/base/process/process.h
index 70c8260193..fc2add24c5 100644
--- a/base/process/process.h
+++ b/base/process/process.h
@@ -15,8 +15,17 @@
#include "base/win/scoped_handle.h"
#endif
+#if defined(OS_MACOSX)
+#include "base/feature_list.h"
+#include "base/process/port_provider_mac.h"
+#endif
+
namespace base {
+#if defined(OS_MACOSX)
+extern const Feature kMacAllowBackgroundingProcesses;
+#endif
+
// Provides a move-only encapsulation of a process.
//
// This object is not tied to the lifetime of the underlying process: the
@@ -67,6 +76,9 @@ class BASE_EXPORT Process {
// Returns true if processes can be backgrounded.
static bool CanBackgroundProcesses();
+ // Terminates the current process immediately with |exit_code|.
+ static void TerminateCurrentProcessImmediately(int exit_code);
+
// Returns true if this objects represents a valid process.
bool IsValid() const;
@@ -99,13 +111,35 @@ class BASE_EXPORT Process {
// any process.
// NOTE: |exit_code| is optional, nullptr can be passed if the exit code is
// not required.
- bool WaitForExit(int* exit_code);
+ bool WaitForExit(int* exit_code) const;
// Same as WaitForExit() but only waits for up to |timeout|.
// NOTE: |exit_code| is optional, nullptr can be passed if the exit code
// is not required.
- bool WaitForExitWithTimeout(TimeDelta timeout, int* exit_code);
-
+ bool WaitForExitWithTimeout(TimeDelta timeout, int* exit_code) const;
+
+#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
+ // a port provider for this reason. See crbug.com/460102
+ //
+ // A process is backgrounded when its task priority is
+ // |TASK_BACKGROUND_APPLICATION|.
+ //
+ // Returns true if the port_provider can locate a task port for the process
+ // and it is backgrounded. If port_provider is null, returns false.
+ bool IsProcessBackgrounded(PortProvider* port_provider) const;
+
+ // Set the process as backgrounded. If value is
+ // true, the priority of the associated task will be set to
+ // TASK_BACKGROUND_APPLICATION. If value is false, the
+ // priority of the process will be set to TASK_FOREGROUND_APPLICATION.
+ //
+ // Returns true if the priority was changed, false otherwise. If
+ // |port_provider| is null, this is a no-op and it returns false.
+ bool SetProcessBackgrounded(PortProvider* port_provider, 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;
@@ -115,7 +149,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;
diff --git a/base/process/process_metrics.cc b/base/process/process_metrics.cc
index 0b38726431..a38930a208 100644
--- a/base/process/process_metrics.cc
+++ b/base/process/process_metrics.cc
@@ -46,7 +46,7 @@ std::unique_ptr<Value> SystemMetrics::ToValue() const {
return std::move(res);
}
-ProcessMetrics* ProcessMetrics::CreateCurrentProcessMetrics() {
+std::unique_ptr<ProcessMetrics> ProcessMetrics::CreateCurrentProcessMetrics() {
#if !defined(OS_MACOSX) || defined(OS_IOS)
return CreateProcessMetrics(base::GetCurrentProcessHandle());
#else
@@ -84,8 +84,9 @@ int ProcessMetrics::CalculateIdleWakeupsPerSecond(
last_idle_wakeups_time_ = time;
last_absolute_idle_wakeups_ = absolute_idle_wakeups;
- // Round to average wakeups per second.
int64_t wakeups_delta_for_ms = wakeups_delta * Time::kMicrosecondsPerSecond;
+ // Round the result up by adding 1/2 (the second term resolves to 1/2 without
+ // dropping down into floating point).
return (wakeups_delta_for_ms + time_delta / 2) / time_delta;
}
#else
diff --git a/base/process/process_metrics.h b/base/process/process_metrics.h
index 57cb3abec0..71d6042e00 100644
--- a/base/process/process_metrics.h
+++ b/base/process/process_metrics.h
@@ -11,6 +11,7 @@
#include <stddef.h>
#include <stdint.h>
+#include <memory>
#include <string>
#include "base/base_export.h"
@@ -103,22 +104,22 @@ class BASE_EXPORT ProcessMetrics {
~ProcessMetrics();
// Creates a ProcessMetrics for the specified process.
- // The caller owns the returned object.
#if !defined(OS_MACOSX) || defined(OS_IOS)
- static ProcessMetrics* CreateProcessMetrics(ProcessHandle process);
+ static std::unique_ptr<ProcessMetrics> CreateProcessMetrics(
+ ProcessHandle process);
#else
// The port provider needs to outlive the ProcessMetrics object returned by
// this function. If NULL is passed as provider, the returned object
// only returns valid metrics if |process| is the current process.
- static ProcessMetrics* CreateProcessMetrics(ProcessHandle process,
- PortProvider* port_provider);
+ static std::unique_ptr<ProcessMetrics> CreateProcessMetrics(
+ ProcessHandle process,
+ PortProvider* port_provider);
#endif // !defined(OS_MACOSX) || defined(OS_IOS)
// Creates a ProcessMetrics for the current process. This a cross-platform
// convenience wrapper for CreateProcessMetrics().
- // The caller owns the returned object.
- static ProcessMetrics* CreateCurrentProcessMetrics();
+ static std::unique_ptr<ProcessMetrics> CreateCurrentProcessMetrics();
// Returns the current space allocated for the pagefile, in bytes (these pages
// may or may not be in memory). On Linux, this returns the total virtual
@@ -135,8 +136,7 @@ class BASE_EXPORT ProcessMetrics {
// memory currently allocated to a process that cannot be shared. Returns
// false on platform specific error conditions. Note: |private_bytes|
// returns 0 on unsupported OSes: prior to XP SP2.
- bool GetMemoryBytes(size_t* private_bytes,
- size_t* shared_bytes);
+ bool GetMemoryBytes(size_t* private_bytes, size_t* shared_bytes) const;
// Fills a CommittedKBytes with both resident and paged
// memory usage as per definition of CommittedBytes.
void GetCommittedKBytes(CommittedKBytes* usage) const;
@@ -144,6 +144,9 @@ class BASE_EXPORT ProcessMetrics {
// usage in bytes, as per definition of WorkingSetBytes. Note that this
// function is somewhat expensive on Windows (a few ms per process).
bool GetWorkingSetKBytes(WorkingSetKBytes* ws_usage) const;
+ // Computes pss (proportional set size) of a process. Note that this
+ // function is somewhat expensive on Windows (a few ms per process).
+ bool GetProportionalSetSizeBytes(uint64_t* pss_bytes) const;
#if defined(OS_MACOSX)
// Fills both CommitedKBytes and WorkingSetKBytes in a single operation. This
@@ -151,6 +154,10 @@ class BASE_EXPORT ProcessMetrics {
// system call.
bool GetCommittedAndWorkingSetKBytes(CommittedKBytes* usage,
WorkingSetKBytes* ws_usage) const;
+ // Returns private, shared, and total resident bytes.
+ bool GetMemoryBytes(size_t* private_bytes,
+ size_t* shared_bytes,
+ size_t* resident_bytes) const;
#endif
// Returns the CPU usage in percent since the last time this method or
@@ -295,9 +302,9 @@ struct BASE_EXPORT SystemMemoryInfoKB {
int dirty;
// vmstats data.
- int pswpin;
- int pswpout;
- int pgmajfault;
+ unsigned long pswpin;
+ unsigned long pswpout;
+ unsigned long pgmajfault;
#endif // defined(OS_ANDROID) || defined(OS_LINUX)
#if defined(OS_CHROMEOS)
@@ -374,6 +381,9 @@ BASE_EXPORT bool IsValidDiskName(const std::string& candidate);
// Retrieves data from /proc/diskstats about system-wide disk I/O.
// Fills in the provided |diskinfo| structure. Returns true on success.
BASE_EXPORT bool GetSystemDiskInfo(SystemDiskInfo* diskinfo);
+
+// Returns the amount of time spent in user space since boot across all CPUs.
+BASE_EXPORT TimeDelta GetUserCpuTimeSinceBoot();
#endif // defined(OS_LINUX) || defined(OS_ANDROID)
#if defined(OS_CHROMEOS)
diff --git a/base/process/process_metrics_linux.cc b/base/process/process_metrics_linux.cc
index 3d27656d6a..5d542cc675 100644
--- a/base/process/process_metrics_linux.cc
+++ b/base/process/process_metrics_linux.cc
@@ -17,6 +17,7 @@
#include "base/files/dir_reader_posix.h"
#include "base/files/file_util.h"
#include "base/logging.h"
+#include "base/memory/ptr_util.h"
#include "base/process/internal_linux.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_split.h"
@@ -72,8 +73,8 @@ size_t ReadProcStatusAndGetFieldAsSizeT(pid_t pid, const std::string& field) {
const std::string& key = pairs[i].first;
const std::string& value_str = pairs[i].second;
if (key == field) {
- std::vector<StringPiece> split_value_str = SplitStringPiece(
- value_str, " ", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL);
+ std::vector<StringPiece> split_value_str =
+ SplitStringPiece(value_str, " ", TRIM_WHITESPACE, SPLIT_WANT_ALL);
if (split_value_str.size() != 2 || split_value_str[1] != "kB") {
NOTREACHED();
return 0;
@@ -163,8 +164,9 @@ int GetProcessCPU(pid_t pid) {
} // namespace
// static
-ProcessMetrics* ProcessMetrics::CreateProcessMetrics(ProcessHandle process) {
- return new ProcessMetrics(process);
+std::unique_ptr<ProcessMetrics> ProcessMetrics::CreateProcessMetrics(
+ ProcessHandle process) {
+ return WrapUnique(new ProcessMetrics(process));
}
// On linux, we return vsize.
@@ -190,7 +192,7 @@ size_t ProcessMetrics::GetPeakWorkingSetSize() const {
}
bool ProcessMetrics::GetMemoryBytes(size_t* private_bytes,
- size_t* shared_bytes) {
+ size_t* shared_bytes) const {
WorkingSetKBytes ws_usage;
if (!GetWorkingSetKBytes(&ws_usage))
return false;
@@ -354,8 +356,7 @@ bool ProcessMetrics::GetWorkingSetKBytesTotmaps(WorkingSetKBytes *ws_usage)
}
std::vector<std::string> totmaps_fields = SplitString(
- totmaps_data, base::kWhitespaceASCII, base::KEEP_WHITESPACE,
- base::SPLIT_WANT_NONEMPTY);
+ totmaps_data, kWhitespaceASCII, KEEP_WHITESPACE, SPLIT_WANT_NONEMPTY);
DCHECK_EQ("Pss:", totmaps_fields[kPssIndex-1]);
DCHECK_EQ("Private_Clean:", totmaps_fields[kPrivate_CleanIndex - 1]);
@@ -406,8 +407,8 @@ bool ProcessMetrics::GetWorkingSetKBytesStatm(WorkingSetKBytes* ws_usage)
return false;
}
- std::vector<StringPiece> statm_vec = SplitStringPiece(
- statm, " ", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL);
+ std::vector<StringPiece> statm_vec =
+ SplitStringPiece(statm, " ", TRIM_WHITESPACE, SPLIT_WANT_ALL);
if (statm_vec.size() != 7)
return false; // Not the format we expect.
@@ -686,12 +687,16 @@ bool ParseProcVmstat(const std::string& vmstat_data,
if (tokens.size() != 2)
continue;
+ uint64_t val;
+ if (!StringToUint64(tokens[1], &val))
+ continue;
+
if (tokens[0] == "pswpin") {
- StringToInt(tokens[1], &meminfo->pswpin);
+ meminfo->pswpin = val;
} else if (tokens[0] == "pswpout") {
- StringToInt(tokens[1], &meminfo->pswpout);
+ meminfo->pswpout = val;
} else if (tokens[0] == "pgmajfault") {
- StringToInt(tokens[1], &meminfo->pgmajfault);
+ meminfo->pgmajfault = val;
}
}
@@ -907,6 +912,10 @@ bool GetSystemDiskInfo(SystemDiskInfo* diskinfo) {
return true;
}
+TimeDelta GetUserCpuTimeSinceBoot() {
+ return internal::GetUserCpuTimeSinceBoot();
+}
+
#if defined(OS_CHROMEOS)
std::unique_ptr<Value> SwapInfo::ToValue() const {
std::unique_ptr<DictionaryValue> res(new DictionaryValue());
diff --git a/base/process/process_metrics_mac.cc b/base/process/process_metrics_mac.cc
index 8b5d5644ff..51f5fd4e16 100644
--- a/base/process/process_metrics_mac.cc
+++ b/base/process/process_metrics_mac.cc
@@ -15,6 +15,7 @@
#include "base/logging.h"
#include "base/mac/mach_logging.h"
#include "base/mac/scoped_mach_port.h"
+#include "base/memory/ptr_util.h"
#include "base/sys_info.h"
#if !defined(TASK_POWER_INFO)
@@ -79,10 +80,7 @@ bool IsAddressInSharedRegion(mach_vm_address_t addr, cpu_type_t type) {
} // namespace
-SystemMemoryInfoKB::SystemMemoryInfoKB() {
- total = 0;
- free = 0;
-}
+SystemMemoryInfoKB::SystemMemoryInfoKB() : total(0), free(0) {}
SystemMemoryInfoKB::SystemMemoryInfoKB(const SystemMemoryInfoKB& other) =
default;
@@ -94,10 +92,10 @@ SystemMemoryInfoKB::SystemMemoryInfoKB(const SystemMemoryInfoKB& other) =
// otherwise return 0.
// static
-ProcessMetrics* ProcessMetrics::CreateProcessMetrics(
+std::unique_ptr<ProcessMetrics> ProcessMetrics::CreateProcessMetrics(
ProcessHandle process,
PortProvider* port_provider) {
- return new ProcessMetrics(process, port_provider);
+ return WrapUnique(new ProcessMetrics(process, port_provider));
}
size_t ProcessMetrics::GetPagefileUsage() const {
@@ -112,10 +110,12 @@ size_t ProcessMetrics::GetPeakPagefileUsage() const {
}
size_t ProcessMetrics::GetWorkingSetSize() const {
- task_basic_info_64 task_info_data;
- if (!GetTaskInfo(TaskForPid(process_), &task_info_data))
+ size_t private_bytes = 0;
+ size_t shared_bytes = 0;
+ size_t resident_bytes = 0;
+ if (!GetMemoryBytes(&private_bytes, &shared_bytes, &resident_bytes))
return 0;
- return task_info_data.resident_size;
+ return resident_bytes;
}
size_t ProcessMetrics::GetPeakWorkingSetSize() const {
@@ -126,7 +126,7 @@ size_t ProcessMetrics::GetPeakWorkingSetSize() const {
// private_bytes is the size of private resident memory.
// shared_bytes is the size of shared resident memory.
bool ProcessMetrics::GetMemoryBytes(size_t* private_bytes,
- size_t* shared_bytes) {
+ size_t* shared_bytes) const {
size_t private_pages_count = 0;
size_t shared_pages_count = 0;
@@ -145,7 +145,7 @@ bool ProcessMetrics::GetMemoryBytes(size_t* private_bytes,
// The same region can be referenced multiple times. To avoid double counting
// we need to keep track of which regions we've already counted.
- base::hash_set<int> seen_objects;
+ hash_set<int> seen_objects;
// We iterate through each VM region in the task's address map. For shared
// memory we add up all the pages that are marked as shared. Like libtop we
@@ -191,6 +191,7 @@ bool ProcessMetrics::GetMemoryBytes(size_t* private_bytes,
info.share_mode = SM_PRIVATE;
switch (info.share_mode) {
+ case SM_LARGE_PAGE:
case SM_PRIVATE:
private_pages_count += info.private_pages_resident;
private_pages_count += info.shared_pages_resident;
@@ -199,6 +200,9 @@ bool ProcessMetrics::GetMemoryBytes(size_t* private_bytes,
private_pages_count += info.private_pages_resident;
// Fall through
case SM_SHARED:
+ case SM_PRIVATE_ALIASED:
+ case SM_TRUESHARED:
+ case SM_SHARED_ALIASED:
if (seen_objects.count(info.obj_id) == 0) {
// Only count the first reference to this region.
seen_objects.insert(info.obj_id);
@@ -248,6 +252,15 @@ bool ProcessMetrics::GetCommittedAndWorkingSetKBytes(
return true;
}
+bool ProcessMetrics::GetMemoryBytes(size_t* private_bytes,
+ size_t* shared_bytes,
+ size_t* resident_bytes) const {
+ if (!GetMemoryBytes(private_bytes, shared_bytes))
+ return false;
+ *resident_bytes = *private_bytes + *shared_bytes;
+ return true;
+}
+
#define TIME_VALUE_TO_TIMEVAL(a, r) do { \
(r)->tv_sec = (a)->seconds; \
(r)->tv_usec = (a)->microseconds; \
@@ -326,6 +339,17 @@ int ProcessMetrics::GetIdleWakeupsPerSecond() {
// where TASK_POWER_INFO isn't supported yet.
return 0;
}
+
+ // The task_power_info struct contains two wakeup counters:
+ // task_interrupt_wakeups and task_platform_idle_wakeups.
+ // task_interrupt_wakeups is the total number of wakeups generated by the
+ // process, and is the number that Activity Monitor reports.
+ // task_platform_idle_wakeups is a subset of task_interrupt_wakeups that
+ // tallies the number of times the processor was taken out of its low-power
+ // idle state to handle a wakeup. task_platform_idle_wakeups therefore result
+ // in a greater power increase than the other interrupts which occur while the
+ // CPU is already working, and reducing them has a greater overall impact on
+ // power usage. See the powermetrics man page for more info.
return CalculateIdleWakeupsPerSecond(
power_info_data.task_platform_idle_wakeups);
}
diff --git a/base/process/process_metrics_posix.cc b/base/process/process_metrics_posix.cc
index fad581eece..13acf2ea34 100644
--- a/base/process/process_metrics_posix.cc
+++ b/base/process/process_metrics_posix.cc
@@ -33,6 +33,8 @@ static const rlim_t kSystemDefaultMaxFds = 256;
static const rlim_t kSystemDefaultMaxFds = 8192;
#elif defined(OS_FREEBSD)
static const rlim_t kSystemDefaultMaxFds = 8192;
+#elif defined(OS_NETBSD)
+static const rlim_t kSystemDefaultMaxFds = 1024;
#elif defined(OS_OPENBSD)
static const rlim_t kSystemDefaultMaxFds = 256;
#elif defined(OS_ANDROID)
diff --git a/base/process/process_metrics_unittest.cc b/base/process/process_metrics_unittest.cc
index 94a2ffe7f8..b0bd7ea80b 100644
--- a/base/process/process_metrics_unittest.cc
+++ b/base/process/process_metrics_unittest.cc
@@ -286,13 +286,13 @@ TEST_F(SystemMetricsTest, ParseVmstat) {
"pgrefill_high 0\n"
"pgrefill_movable 0\n";
EXPECT_TRUE(ParseProcVmstat(valid_input1, &meminfo));
- EXPECT_EQ(meminfo.pswpin, 179);
- EXPECT_EQ(meminfo.pswpout, 406);
- EXPECT_EQ(meminfo.pgmajfault, 487192);
+ EXPECT_EQ(179LU, meminfo.pswpin);
+ EXPECT_EQ(406LU, meminfo.pswpout);
+ EXPECT_EQ(487192LU, meminfo.pgmajfault);
EXPECT_TRUE(ParseProcVmstat(valid_input2, &meminfo));
- EXPECT_EQ(meminfo.pswpin, 12);
- EXPECT_EQ(meminfo.pswpout, 901);
- EXPECT_EQ(meminfo.pgmajfault, 2023);
+ EXPECT_EQ(12LU, meminfo.pswpin);
+ EXPECT_EQ(901LU, meminfo.pswpout);
+ EXPECT_EQ(2023LU, meminfo.pgmajfault);
}
#endif // defined(OS_LINUX) || defined(OS_ANDROID)
@@ -485,10 +485,13 @@ MULTIPROCESS_TEST_MAIN(ChildMain) {
} // namespace
+// Arc++ note: don't compile as SpawnMultiProcessTestChild brings in a lot of
+// extra dependency.
+#if !defined(OS_ANDROID) && !defined(__ANDROID__) && !defined(__ANDROID_HOST__)
TEST(ProcessMetricsTest, GetOpenFdCount) {
ScopedTempDir temp_dir;
ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
- const FilePath temp_path = temp_dir.path();
+ const FilePath temp_path = temp_dir.GetPath();
CommandLine child_command_line(GetMultiProcessTestChildBaseCommandLine());
child_command_line.AppendSwitchPath(kTempDirFlag, temp_path);
Process child = SpawnMultiProcessTestChild(
@@ -513,6 +516,8 @@ TEST(ProcessMetricsTest, GetOpenFdCount) {
EXPECT_EQ(0, open_fds);
ASSERT_TRUE(child.Terminate(0, true));
}
+#endif // !defined(__ANDROID__)
+
#endif // defined(OS_LINUX)
} // namespace debug
diff --git a/base/process/process_posix.cc b/base/process/process_posix.cc
index ba9b5447c0..a1d84e9128 100644
--- a/base/process/process_posix.cc
+++ b/base/process/process_posix.cc
@@ -9,6 +9,7 @@
#include <sys/resource.h>
#include <sys/wait.h>
+#include "base/debug/activity_tracker.h"
#include "base/files/scoped_file.h"
#include "base/logging.h"
#include "base/posix/eintr_wrapper.h"
@@ -266,12 +267,17 @@ Process Process::DeprecatedGetProcessFromHandle(ProcessHandle handle) {
return Process(handle);
}
-#if !defined(OS_LINUX)
+#if !defined(OS_LINUX) && !defined(OS_MACOSX)
// static
bool Process::CanBackgroundProcesses() {
return false;
}
-#endif // !defined(OS_LINUX)
+#endif // !defined(OS_LINUX) && !defined(OS_MACOSX)
+
+// static
+void Process::TerminateCurrentProcessImmediately(int exit_code) {
+ _exit(exit_code);
+}
bool Process::IsValid() const {
return process_ != kNullProcessHandle;
@@ -314,6 +320,12 @@ bool Process::Terminate(int /*exit_code*/, bool wait) const {
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
@@ -353,15 +365,18 @@ bool Process::Terminate(int /*exit_code*/, bool wait) const {
}
#endif // !defined(OS_NACL_NONSFI)
-bool Process::WaitForExit(int* exit_code) {
+bool Process::WaitForExit(int* exit_code) const {
return WaitForExitWithTimeout(TimeDelta::Max(), exit_code);
}
-bool Process::WaitForExitWithTimeout(TimeDelta timeout, int* exit_code) {
+bool Process::WaitForExitWithTimeout(TimeDelta timeout, int* exit_code) const {
+ // Record the event that this thread is blocking upon (for hang diagnosis).
+ base::debug::ScopedProcessWaitActivity process_activity(this);
+
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());
@@ -369,13 +384,13 @@ bool Process::IsProcessBackgrounded() const {
}
bool Process::SetProcessBackgrounded(bool /*value*/) {
- // Not implemented for POSIX systems other than Linux. With POSIX, if we were
- // to lower the process priority we wouldn't be able to raise it back to its
- // initial priority.
+ // Not implemented for POSIX systems other than Linux and Mac. 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());