diff options
Diffstat (limited to 'base/process')
-rw-r--r-- | base/process/internal_linux.cc | 41 | ||||
-rw-r--r-- | base/process/internal_linux.h | 8 | ||||
-rw-r--r-- | base/process/kill.h | 11 | ||||
-rw-r--r-- | base/process/kill_posix.cc | 2 | ||||
-rw-r--r-- | base/process/launch.cc | 36 | ||||
-rw-r--r-- | base/process/launch.h | 53 | ||||
-rw-r--r-- | base/process/launch_mac.cc | 153 | ||||
-rw-r--r-- | base/process/launch_posix.cc | 21 | ||||
-rw-r--r-- | base/process/memory.cc | 54 | ||||
-rw-r--r-- | base/process/memory.h | 83 | ||||
-rw-r--r-- | base/process/memory_linux.cc | 212 | ||||
-rw-r--r-- | base/process/memory_stubs.cc | 49 | ||||
-rw-r--r-- | base/process/port_provider_mac.cc | 3 | ||||
-rw-r--r-- | base/process/process.h | 42 | ||||
-rw-r--r-- | base/process/process_metrics.cc | 5 | ||||
-rw-r--r-- | base/process/process_metrics.h | 32 | ||||
-rw-r--r-- | base/process/process_metrics_linux.cc | 33 | ||||
-rw-r--r-- | base/process/process_metrics_mac.cc | 46 | ||||
-rw-r--r-- | base/process/process_metrics_posix.cc | 2 | ||||
-rw-r--r-- | base/process/process_metrics_unittest.cc | 19 | ||||
-rw-r--r-- | base/process/process_posix.cc | 33 |
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()); |