diff options
author | Jocelyn Turcotte <jocelyn.turcotte@digia.com> | 2014-08-08 14:30:41 +0200 |
---|---|---|
committer | Jocelyn Turcotte <jocelyn.turcotte@digia.com> | 2014-08-12 13:49:54 +0200 |
commit | ab0a50979b9eb4dfa3320eff7e187e41efedf7a9 (patch) | |
tree | 498dfb8a97ff3361a9f7486863a52bb4e26bb898 /chromium/base/process | |
parent | 4ce69f7403811819800e7c5ae1318b2647e778d1 (diff) |
Update Chromium to beta version 37.0.2062.68
Change-Id: I188e3b5aff1bec75566014291b654eb19f5bc8ca
Reviewed-by: Andras Becsi <andras.becsi@digia.com>
Diffstat (limited to 'chromium/base/process')
37 files changed, 697 insertions, 315 deletions
diff --git a/chromium/base/process/internal_linux.cc b/chromium/base/process/internal_linux.cc index b1d1b6f174e..57a3ab4271a 100644 --- a/chromium/base/process/internal_linux.cc +++ b/chromium/base/process/internal_linux.cc @@ -115,13 +115,13 @@ void ParseProcStat(const std::string& contents, ProcStatMap* output) { } } -int GetProcStatsFieldAsInt(const std::vector<std::string>& proc_stats, - ProcStatsFields field_num) { +int64 GetProcStatsFieldAsInt64(const std::vector<std::string>& proc_stats, + ProcStatsFields field_num) { DCHECK_GE(field_num, VM_PPID); CHECK_LT(static_cast<size_t>(field_num), proc_stats.size()); - int value; - return StringToInt(proc_stats[field_num], &value) ? value : 0; + int64 value; + return StringToInt64(proc_stats[field_num], &value) ? value : 0; } size_t GetProcStatsFieldAsSizeT(const std::vector<std::string>& proc_stats, @@ -133,15 +133,14 @@ size_t GetProcStatsFieldAsSizeT(const std::vector<std::string>& proc_stats, return StringToSizeT(proc_stats[field_num], &value) ? value : 0; } -int ReadProcStatsAndGetFieldAsInt(pid_t pid, - ProcStatsFields field_num) { +int64 ReadProcStatsAndGetFieldAsInt64(pid_t pid, ProcStatsFields field_num) { std::string stats_data; if (!ReadProcStats(pid, &stats_data)) return 0; std::vector<std::string> proc_stats; if (!ParseProcStats(stats_data, &proc_stats)) return 0; - return GetProcStatsFieldAsInt(proc_stats, field_num); + return GetProcStatsFieldAsInt64(proc_stats, field_num); } size_t ReadProcStatsAndGetFieldAsSizeT(pid_t pid, diff --git a/chromium/base/process/internal_linux.h b/chromium/base/process/internal_linux.h index 0cacd3f3a84..5fc33566607 100644 --- a/chromium/base/process/internal_linux.h +++ b/chromium/base/process/internal_linux.h @@ -63,19 +63,18 @@ enum ProcStatsFields { // Reads the |field_num|th field from |proc_stats|. Returns 0 on failure. // This version does not handle the first 3 values, since the first value is // simply |pid|, and the next two values are strings. -int GetProcStatsFieldAsInt(const std::vector<std::string>& proc_stats, - ProcStatsFields field_num); +int64 GetProcStatsFieldAsInt64(const std::vector<std::string>& proc_stats, + ProcStatsFields field_num); -// Same as GetProcStatsFieldAsInt(), but for size_t values. +// Same as GetProcStatsFieldAsInt64(), but for size_t values. size_t GetProcStatsFieldAsSizeT(const std::vector<std::string>& proc_stats, ProcStatsFields field_num); -// Convenience wrapper around GetProcStatsFieldAsInt(), ParseProcStats() and -// ReadProcStats(). See GetProcStatsFieldAsInt() for details. -int ReadProcStatsAndGetFieldAsInt(pid_t pid, - ProcStatsFields field_num); +// Convenience wrapper around GetProcStatsFieldAsInt64(), ParseProcStats() and +// ReadProcStats(). See GetProcStatsFieldAsInt64() for details. +int64 ReadProcStatsAndGetFieldAsInt64(pid_t pid, ProcStatsFields field_num); -// Same as ReadProcStatsAndGetFieldAsInt() but for size_t values. +// Same as ReadProcStatsAndGetFieldAsInt64() but for size_t values. size_t ReadProcStatsAndGetFieldAsSizeT(pid_t pid, ProcStatsFields field_num); diff --git a/chromium/base/process/kill_mac.cc b/chromium/base/process/kill_mac.cc index 5ebca5d0076..1589a641a04 100644 --- a/chromium/base/process/kill_mac.cc +++ b/chromium/base/process/kill_mac.cc @@ -10,6 +10,7 @@ #include <sys/wait.h> #include "base/file_util.h" +#include "base/files/scoped_file.h" #include "base/logging.h" #include "base/posix/eintr_wrapper.h" @@ -76,15 +77,13 @@ void WaitForChildToDie(pid_t child, int timeout) { int result; - int kq = HANDLE_EINTR(kqueue()); - if (kq == -1) { + ScopedFD kq(HANDLE_EINTR(kqueue())); + if (!kq.is_valid()) { DPLOG(ERROR) << "kqueue()"; } else { - file_util::ScopedFD auto_close_kq(&kq); - struct kevent change = {0}; EV_SET(&change, child, EVFILT_PROC, EV_ADD, NOTE_EXIT, 0, NULL); - result = HANDLE_EINTR(kevent(kq, &change, 1, NULL, 0, NULL)); + result = HANDLE_EINTR(kevent(kq.get(), &change, 1, NULL, 0, NULL)); if (result == -1) { if (errno != ESRCH) { @@ -120,7 +119,7 @@ void WaitForChildToDie(pid_t child, int timeout) { struct kevent event = {0}; while (remaining_delta.InMilliseconds() > 0) { const struct timespec remaining_timespec = remaining_delta.ToTimeSpec(); - result = kevent(kq, NULL, 0, &event, 1, &remaining_timespec); + result = kevent(kq.get(), NULL, 0, &event, 1, &remaining_timespec); if (result == -1 && errno == EINTR) { remaining_delta = deadline - TimeTicks::Now(); result = 0; diff --git a/chromium/base/process/kill_posix.cc b/chromium/base/process/kill_posix.cc index 99d70d9ab55..18c14fd3a77 100644 --- a/chromium/base/process/kill_posix.cc +++ b/chromium/base/process/kill_posix.cc @@ -10,6 +10,7 @@ #include <unistd.h> #include "base/file_util.h" +#include "base/files/scoped_file.h" #include "base/logging.h" #include "base/posix/eintr_wrapper.h" #include "base/process/process_iterator.h" @@ -21,11 +22,11 @@ namespace base { namespace { -int WaitpidWithTimeout(ProcessHandle handle, - int64 wait_milliseconds, - bool* success) { +bool WaitpidWithTimeout(ProcessHandle handle, + int* status, + base::TimeDelta wait) { // This POSIX version of this function only guarantees that we wait no less - // than |wait_milliseconds| for the process to exit. The child process may + // than |wait| for the process to exit. The child process may // exit sometime before the timeout has ended but we may still block for up // to 256 milliseconds after the fact. // @@ -36,7 +37,7 @@ int WaitpidWithTimeout(ProcessHandle handle, // affect other parts of the application and would be difficult to debug. // // Our strategy is to call waitpid() once up front to check if the process - // has already exited, otherwise to loop for wait_milliseconds, sleeping for + // has already exited, otherwise to loop for |wait|, sleeping for // at most 256 milliseconds each time using usleep() and then calling // waitpid(). The amount of time we sleep starts out at 1 milliseconds, and // we double it every 4 sleep cycles. @@ -47,15 +48,18 @@ int WaitpidWithTimeout(ProcessHandle handle, // // This function is used primarily for unit tests, if we want to use it in // the application itself it would probably be best to examine other routes. - int status = -1; - pid_t ret_pid = HANDLE_EINTR(waitpid(handle, &status, WNOHANG)); + + if (wait.InMilliseconds() == base::kNoTimeout) { + return HANDLE_EINTR(waitpid(handle, status, 0)) > 0; + } + + pid_t ret_pid = HANDLE_EINTR(waitpid(handle, status, WNOHANG)); static const int64 kMaxSleepInMicroseconds = 1 << 18; // ~256 milliseconds. int64 max_sleep_time_usecs = 1 << 10; // ~1 milliseconds. int64 double_sleep_time = 0; // If the process hasn't exited yet, then sleep and try again. - TimeTicks wakeup_time = TimeTicks::Now() + - TimeDelta::FromMilliseconds(wait_milliseconds); + TimeTicks wakeup_time = TimeTicks::Now() + wait; while (ret_pid == 0) { TimeTicks now = TimeTicks::Now(); if (now > wakeup_time) @@ -69,7 +73,7 @@ int WaitpidWithTimeout(ProcessHandle handle, // usleep() will return 0 and set errno to EINTR on receipt of a signal // such as SIGCHLD. usleep(sleep_time_usecs); - ret_pid = HANDLE_EINTR(waitpid(handle, &status, WNOHANG)); + ret_pid = HANDLE_EINTR(waitpid(handle, status, WNOHANG)); if ((max_sleep_time_usecs < kMaxSleepInMicroseconds) && (double_sleep_time++ % 4 == 0)) { @@ -77,10 +81,7 @@ int WaitpidWithTimeout(ProcessHandle handle, } } - if (success) - *success = (ret_pid != -1); - - return status; + return ret_pid > 0; } TerminationStatus GetTerminationStatusImpl(ProcessHandle handle, @@ -225,12 +226,8 @@ bool WaitForExitCode(ProcessHandle handle, int* exit_code) { bool WaitForExitCodeWithTimeout(ProcessHandle handle, int* exit_code, base::TimeDelta timeout) { - bool waitpid_success = false; - int status = WaitpidWithTimeout(handle, timeout.InMilliseconds(), - &waitpid_success); - if (status == -1) - return false; - if (!waitpid_success) + int status; + if (!WaitpidWithTimeout(handle, &status, timeout)) return false; if (WIFSIGNALED(status)) { *exit_code = -1; @@ -273,16 +270,15 @@ static bool WaitForSingleNonChildProcess(ProcessHandle handle, DCHECK_GT(handle, 0); DCHECK(wait.InMilliseconds() == base::kNoTimeout || wait > base::TimeDelta()); - int kq = kqueue(); - if (kq == -1) { + ScopedFD kq(kqueue()); + if (!kq.is_valid()) { DPLOG(ERROR) << "kqueue"; return false; } - file_util::ScopedFD kq_closer(&kq); struct kevent change = {0}; EV_SET(&change, handle, EVFILT_PROC, EV_ADD, NOTE_EXIT, 0, NULL); - int result = HANDLE_EINTR(kevent(kq, &change, 1, NULL, 0, NULL)); + int result = HANDLE_EINTR(kevent(kq.get(), &change, 1, NULL, 0, NULL)); if (result == -1) { if (errno == ESRCH) { // If the process wasn't found, it must be dead. @@ -316,7 +312,7 @@ static bool WaitForSingleNonChildProcess(ProcessHandle handle, remaining_timespec_ptr = &remaining_timespec; } - result = kevent(kq, NULL, 0, &event, 1, remaining_timespec_ptr); + result = kevent(kq.get(), NULL, 0, &event, 1, remaining_timespec_ptr); if (result == -1 && errno == EINTR) { if (!wait_forever) { @@ -369,21 +365,10 @@ bool WaitForSingleProcess(ProcessHandle handle, base::TimeDelta wait) { #endif // OS_MACOSX } - bool waitpid_success; - int status = -1; - if (wait.InMilliseconds() == base::kNoTimeout) { - waitpid_success = (HANDLE_EINTR(waitpid(handle, &status, 0)) != -1); - } else { - status = WaitpidWithTimeout( - handle, wait.InMilliseconds(), &waitpid_success); - } - - if (status != -1) { - DCHECK(waitpid_success); - return WIFEXITED(status); - } else { + int status; + if (!WaitpidWithTimeout(handle, &status, wait)) return false; - } + return WIFEXITED(status); } bool CleanupProcesses(const FilePath::StringType& executable_name, diff --git a/chromium/base/process/kill_win.cc b/chromium/base/process/kill_win.cc index 99a7c661851..aaf3f30a936 100644 --- a/chromium/base/process/kill_win.cc +++ b/chromium/base/process/kill_win.cc @@ -96,9 +96,9 @@ bool KillProcess(ProcessHandle process, int exit_code, bool wait) { if (result && wait) { // The process may not end immediately due to pending I/O if (WAIT_OBJECT_0 != WaitForSingleObject(process, 60 * 1000)) - DLOG_GETLASTERROR(ERROR) << "Error waiting for process exit"; + DPLOG(ERROR) << "Error waiting for process exit"; } else if (!result) { - DLOG_GETLASTERROR(ERROR) << "Unable to terminate process"; + DPLOG(ERROR) << "Unable to terminate process"; } return result; } @@ -111,7 +111,7 @@ bool KillProcessById(ProcessId process_id, int exit_code, bool wait) { FALSE, // Don't inherit handle process_id); if (!process) { - DLOG_GETLASTERROR(ERROR) << "Unable to open process " << process_id; + DPLOG(ERROR) << "Unable to open process " << process_id; return false; } bool ret = KillProcess(process, exit_code, wait); @@ -123,7 +123,7 @@ TerminationStatus GetTerminationStatus(ProcessHandle handle, int* exit_code) { DWORD tmp_exit_code = 0; if (!::GetExitCodeProcess(handle, &tmp_exit_code)) { - DLOG_GETLASTERROR(FATAL) << "GetExitCodeProcess() failed"; + DPLOG(FATAL) << "GetExitCodeProcess() failed"; if (exit_code) { // This really is a random number. We haven't received any // information about the exit code, presumably because this @@ -149,7 +149,7 @@ TerminationStatus GetTerminationStatus(ProcessHandle handle, int* exit_code) { } if (wait_result == WAIT_FAILED) { - DLOG_GETLASTERROR(ERROR) << "WaitForSingleObject() failed"; + DPLOG(ERROR) << "WaitForSingleObject() failed"; } else { DCHECK_EQ(WAIT_OBJECT_0, wait_result); diff --git a/chromium/base/process/launch.cc b/chromium/base/process/launch.cc index 0c9f3a88fef..a1c4d2159cb 100644 --- a/chromium/base/process/launch.cc +++ b/chromium/base/process/launch.cc @@ -20,11 +20,13 @@ LaunchOptions::LaunchOptions() 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) #endif // OS_LINUX #if defined(OS_CHROMEOS) , ctrl_terminal_fd(-1) @@ -36,4 +38,15 @@ LaunchOptions::LaunchOptions() LaunchOptions::~LaunchOptions() { } +LaunchOptions LaunchOptionsForTest() { + LaunchOptions options; +#if defined(OS_LINUX) + // To prevent accidental privilege sharing to an untrusted child, processes + // are started with PR_SET_NO_NEW_PRIVS. Do not set that here, since this + // new child will be used for testing only. + options.allow_new_privs = true; +#endif + return options; +} + } // namespace base diff --git a/chromium/base/process/launch.h b/chromium/base/process/launch.h index 336bfba16e9..261019b138f 100644 --- a/chromium/base/process/launch.h +++ b/chromium/base/process/launch.h @@ -7,7 +7,6 @@ #ifndef BASE_PROCESS_LAUNCH_H_ #define BASE_PROCESS_LAUNCH_H_ -#include <set> #include <string> #include <utility> #include <vector> @@ -25,10 +24,10 @@ #include "base/win/scoped_handle.h" #endif -class CommandLine; - namespace base { +class CommandLine; + #if defined(OS_WIN) typedef std::vector<HANDLE> HandlesToInheritVector; #endif @@ -89,10 +88,15 @@ struct BASE_EXPORT LaunchOptions { // job if any. bool force_breakaway_from_job_; #else - // Set/unset environment variables. Empty (the default) means to inherit - // the same environment. See AlterEnvironment(). + // 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(). EnvironmentMap environ; + // Clear the environment for the new process before processing changes from + // |environ|. + bool clear_environ; + // If non-null, remap file descriptors according to the mapping of // src fd->dest fd to propagate FDs into the child process. // This pointer is owned by the caller and must live through the @@ -102,7 +106,7 @@ struct BASE_EXPORT LaunchOptions { // Each element is an RLIMIT_* constant that should be raised to its // rlim_max. This pointer is owned by the caller and must live through // the call to LaunchProcess(). - const std::set<int>* maximize_rlimits; + const std::vector<int>* maximize_rlimits; // If true, start the process in a new process group, instead of // inheriting the parent's process group. The pgid of the child process @@ -112,6 +116,10 @@ struct BASE_EXPORT LaunchOptions { #if defined(OS_LINUX) // If non-zero, start the process using clone(), using flags as provided. int clone_flags; + + // By default, child processes will have the PR_SET_NO_NEW_PRIVS bit set. If + // true, then this bit will not be set in the new child process. + bool allow_new_privs; #endif // defined(OS_LINUX) #if defined(OS_CHROMEOS) @@ -120,6 +128,15 @@ struct BASE_EXPORT LaunchOptions { int ctrl_terminal_fd; #endif // defined(OS_CHROMEOS) +#if defined(OS_MACOSX) + // If this name is non-empty, the new child, after fork() but before exec(), + // will look up this server name in the bootstrap namespace. The resulting + // service port will be replaced as the bootstrap port in the child. Because + // the process's IPC space is cleared on exec(), any rights to the old + // bootstrap port will not be transferred to the new process. + std::string replacement_bootstrap_name; +#endif + #endif // !defined(OS_WIN) }; @@ -160,6 +177,16 @@ BASE_EXPORT bool LaunchProcess(const string16& cmdline, const LaunchOptions& options, win::ScopedHandle* process_handle); +// Launches a process with elevated privileges. This does not behave exactly +// like LaunchProcess as it uses ShellExecuteEx instead of CreateProcess to +// create the process. This means the process will have elevated privileges +// and thus some common operations like OpenProcess will fail. The process will +// be available through the |process_handle| argument. Currently the only +// supported LaunchOptions are |start_hidden| and |wait|. +BASE_EXPORT bool LaunchElevatedProcess(const CommandLine& cmdline, + const LaunchOptions& options, + ProcessHandle* process_handle); + #elif defined(OS_POSIX) // A POSIX-specific version of LaunchProcess that takes an argv array // instead of a CommandLine. Useful for situations where you need to @@ -232,8 +259,17 @@ BASE_EXPORT void RaiseProcessToHighPriority(); // in the child after forking will restore the standard exception handler. // See http://crbug.com/20371/ for more details. void RestoreDefaultExceptionHandler(); + +// Look up the bootstrap server named |replacement_bootstrap_name| via the +// current |bootstrap_port|. Then replace the task's bootstrap port with the +// received right. +void ReplaceBootstrapPort(const std::string& replacement_bootstrap_name); #endif // defined(OS_MACOSX) +// Creates a LaunchOptions object suitable for launching processes in a test +// binary. This should not be called in production/released code. +BASE_EXPORT LaunchOptions LaunchOptionsForTest(); + } // namespace base #endif // BASE_PROCESS_LAUNCH_H_ diff --git a/chromium/base/process/launch_mac.cc b/chromium/base/process/launch_mac.cc index 176edca72ea..ce02475541e 100644 --- a/chromium/base/process/launch_mac.cc +++ b/chromium/base/process/launch_mac.cc @@ -5,6 +5,9 @@ #include "base/process/launch.h" #include <mach/mach.h> +#include <servers/bootstrap.h> + +#include "base/logging.h" namespace base { @@ -25,4 +28,21 @@ void RestoreDefaultExceptionHandler() { EXCEPTION_DEFAULT, THREAD_STATE_NONE); } +void ReplaceBootstrapPort(const std::string& new_bootstrap_name) { + // This function is called between fork() and exec(), so it should take care + // to run properly in that situation. + + mach_port_t port = MACH_PORT_NULL; + kern_return_t kr = bootstrap_look_up(bootstrap_port, + new_bootstrap_name.c_str(), &port); + if (kr != KERN_SUCCESS) { + RAW_LOG(FATAL, "Failed to look up replacement bootstrap port."); + } + + kr = task_set_bootstrap_port(mach_task_self(), port); + if (kr != KERN_SUCCESS) { + RAW_LOG(FATAL, "Failed to replace bootstrap port."); + } +} + } // namespace base diff --git a/chromium/base/process/launch_posix.cc b/chromium/base/process/launch_posix.cc index 8dc8f9e215d..78a3be47eb0 100644 --- a/chromium/base/process/launch_posix.cc +++ b/chromium/base/process/launch_posix.cc @@ -26,6 +26,7 @@ #include "base/debug/stack_trace.h" #include "base/file_util.h" #include "base/files/dir_reader_posix.h" +#include "base/files/scoped_file.h" #include "base/logging.h" #include "base/memory/scoped_ptr.h" #include "base/posix/eintr_wrapper.h" @@ -37,6 +38,10 @@ #include "base/threading/platform_thread.h" #include "base/threading/thread_restrictions.h" +#if defined(OS_LINUX) +#include <sys/prctl.h> +#endif + #if defined(OS_CHROMEOS) #include <sys/ioctl.h> #endif @@ -181,16 +186,16 @@ void ResetChildSignalHandlersToDefaults(void) { } // anonymous namespace -// A class to handle auto-closing of DIR*'s. -class ScopedDIRClose { - public: +// Functor for |ScopedDIR| (below). +struct ScopedDIRClose { inline void operator()(DIR* x) const { - if (x) { + if (x) closedir(x); - } } }; -typedef scoped_ptr_malloc<DIR, ScopedDIRClose> ScopedDIR; + +// Automatically closes |DIR*|s. +typedef scoped_ptr<DIR, ScopedDIRClose> ScopedDIR; #if defined(OS_LINUX) static const char kFDDir[] = "/proc/self/fd"; @@ -207,7 +212,7 @@ static const char kFDDir[] = "/proc/self/fd"; #endif void CloseSuperfluousFds(const base::InjectiveMultimap& saved_mapping) { - // DANGER: no calls to malloc are allowed from now on: + // DANGER: no calls to malloc or locks are allowed from now on: // http://crbug.com/36678 // Get the maximum number of FDs possible. @@ -220,12 +225,13 @@ void CloseSuperfluousFds(const base::InjectiveMultimap& saved_mapping) { const int fd = static_cast<int>(i); if (fd == STDIN_FILENO || fd == STDOUT_FILENO || fd == STDERR_FILENO) continue; - InjectiveMultimap::const_iterator j; - for (j = saved_mapping.begin(); j != saved_mapping.end(); j++) { - if (fd == j->dest) + // Cannot use STL iterators here, since debug iterators use locks. + size_t j; + for (j = 0; j < saved_mapping.size(); j++) { + if (fd == saved_mapping[j].dest) break; } - if (j != saved_mapping.end()) + if (j < saved_mapping.size()) continue; // Since we're just trying to close anything we can find, @@ -249,12 +255,13 @@ void CloseSuperfluousFds(const base::InjectiveMultimap& saved_mapping) { continue; if (fd == STDIN_FILENO || fd == STDOUT_FILENO || fd == STDERR_FILENO) continue; - InjectiveMultimap::const_iterator i; - for (i = saved_mapping.begin(); i != saved_mapping.end(); i++) { - if (fd == i->dest) + // Cannot use STL iterators here, since debug iterators use locks. + size_t i; + for (i = 0; i < saved_mapping.size(); i++) { + if (fd == saved_mapping[i].dest) break; } - if (i != saved_mapping.end()) + if (i < saved_mapping.size()) continue; if (fd == dir_fd) continue; @@ -285,8 +292,12 @@ bool LaunchProcess(const std::vector<std::string>& argv, scoped_ptr<char*[]> argv_cstr(new char*[argv.size() + 1]); scoped_ptr<char*[]> new_environ; + char* const empty_environ = NULL; + char* const* old_environ = GetEnvironment(); + if (options.clear_environ) + old_environ = &empty_environ; if (!options.environ.empty()) - new_environ = AlterEnvironment(GetEnvironment(), options.environ); + new_environ = AlterEnvironment(old_environ, options.environ); sigset_t full_sigset; sigfillset(&full_sigset); @@ -318,6 +329,9 @@ bool LaunchProcess(const std::vector<std::string>& argv, } else if (pid == 0) { // Child process + // DANGER: no calls to malloc or locks are allowed from now on: + // http://crbug.com/36678 + // DANGER: fork() rule: in the child, if you don't end up doing exec*(), // you call _exit() instead of exit(). This is because _exit() does not // call any previously-registered (in the parent) exit handlers, which @@ -327,14 +341,13 @@ bool LaunchProcess(const std::vector<std::string>& argv, // If a child process uses the readline library, the process block forever. // In BSD like OSes including OS X it is safe to assign /dev/null as stdin. // See http://crbug.com/56596. - int null_fd = HANDLE_EINTR(open("/dev/null", O_RDONLY)); - if (null_fd < 0) { + base::ScopedFD null_fd(HANDLE_EINTR(open("/dev/null", O_RDONLY))); + if (!null_fd.is_valid()) { RAW_LOG(ERROR, "Failed to open /dev/null"); _exit(127); } - file_util::ScopedFD null_fd_closer(&null_fd); - int new_fd = HANDLE_EINTR(dup2(null_fd, STDIN_FILENO)); + int new_fd = HANDLE_EINTR(dup2(null_fd.get(), STDIN_FILENO)); if (new_fd != STDIN_FILENO) { RAW_LOG(ERROR, "Failed to dup /dev/null for stdin"); _exit(127); @@ -356,16 +369,14 @@ bool LaunchProcess(const std::vector<std::string>& argv, if (options.maximize_rlimits) { // Some resource limits need to be maximal in this child. - std::set<int>::const_iterator resource; - for (resource = options.maximize_rlimits->begin(); - resource != options.maximize_rlimits->end(); - ++resource) { + for (size_t i = 0; i < options.maximize_rlimits->size(); ++i) { + const int resource = (*options.maximize_rlimits)[i]; struct rlimit limit; - if (getrlimit(*resource, &limit) < 0) { + if (getrlimit(resource, &limit) < 0) { RAW_LOG(WARNING, "getrlimit failed"); } else if (limit.rlim_cur < limit.rlim_max) { limit.rlim_cur = limit.rlim_max; - if (setrlimit(*resource, &limit) < 0) { + if (setrlimit(resource, &limit) < 0) { RAW_LOG(WARNING, "setrlimit failed"); } } @@ -374,6 +385,8 @@ bool LaunchProcess(const std::vector<std::string>& argv, #if defined(OS_MACOSX) RestoreDefaultExceptionHandler(); + if (!options.replacement_bootstrap_name.empty()) + ReplaceBootstrapPort(options.replacement_bootstrap_name); #endif // defined(OS_MACOSX) ResetChildSignalHandlersToDefaults(); @@ -388,9 +401,6 @@ bool LaunchProcess(const std::vector<std::string>& argv, memset(reinterpret_cast<void*>(malloc), 0xff, 8); #endif // 0 - // DANGER: no calls to malloc are allowed from now on: - // http://crbug.com/36678 - #if defined(OS_CHROMEOS) if (options.ctrl_terminal_fd >= 0) { // Set process' controlling terminal. @@ -406,15 +416,16 @@ bool LaunchProcess(const std::vector<std::string>& argv, #endif // defined(OS_CHROMEOS) if (options.fds_to_remap) { - for (FileHandleMappingVector::const_iterator - it = options.fds_to_remap->begin(); - it != options.fds_to_remap->end(); ++it) { - fd_shuffle1.push_back(InjectionArc(it->first, it->second, false)); - fd_shuffle2.push_back(InjectionArc(it->first, it->second, false)); + // Cannot use STL iterators here, since debug iterators use locks. + for (size_t i = 0; i < options.fds_to_remap->size(); ++i) { + const FileHandleMappingVector::value_type& value = + (*options.fds_to_remap)[i]; + fd_shuffle1.push_back(InjectionArc(value.first, value.second, false)); + fd_shuffle2.push_back(InjectionArc(value.first, value.second, false)); } } - if (!options.environ.empty()) + if (!options.environ.empty() || options.clear_environ) SetEnvironment(new_environ.get()); // fd_shuffle1 is mutated by this call because it cannot malloc. @@ -423,6 +434,20 @@ bool LaunchProcess(const std::vector<std::string>& argv, CloseSuperfluousFds(fd_shuffle2); + // Set NO_NEW_PRIVS by default. Since NO_NEW_PRIVS only exists in kernel + // 3.5+, do not check the return value of prctl here. +#if defined(OS_LINUX) +#ifndef PR_SET_NO_NEW_PRIVS +#define PR_SET_NO_NEW_PRIVS 38 +#endif + if (!options.allow_new_privs) { + if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) && errno != EINVAL) { + // Only log if the error is not EINVAL (i.e. not supported). + RAW_LOG(FATAL, "prctl(PR_SET_NO_NEW_PRIVS) failed"); + } + } +#endif + for (size_t i = 0; i < argv.size(); i++) argv_cstr[i] = const_cast<char*>(argv[i].c_str()); argv_cstr[argv.size()] = NULL; @@ -518,11 +543,12 @@ static GetAppOutputInternalResult GetAppOutputInternal( return EXECUTE_FAILURE; case 0: // child { + // DANGER: no calls to malloc or locks are allowed from now on: + // http://crbug.com/36678 + #if defined(OS_MACOSX) RestoreDefaultExceptionHandler(); #endif - // DANGER: no calls to malloc are allowed from now on: - // http://crbug.com/36678 // Obscure fork() rule: in the child, if you don't end up doing exec*(), // you call _exit() instead of exit(). This is because _exit() does not @@ -544,8 +570,8 @@ static GetAppOutputInternalResult GetAppOutputInternal( // Adding another element here? Remeber to increase the argument to // reserve(), above. - std::copy(fd_shuffle1.begin(), fd_shuffle1.end(), - std::back_inserter(fd_shuffle2)); + for (size_t i = 0; i < fd_shuffle1.size(); ++i) + fd_shuffle2.push_back(fd_shuffle1[i]); if (!ShuffleFileDescriptors(&fd_shuffle1)) _exit(127); diff --git a/chromium/base/process/launch_win.cc b/chromium/base/process/launch_win.cc index 0c831cf7b4a..9e4db384ebb 100644 --- a/chromium/base/process/launch_win.cc +++ b/chromium/base/process/launch_win.cc @@ -6,6 +6,7 @@ #include <fcntl.h> #include <io.h> +#include <shellapi.h> #include <windows.h> #include <userenv.h> #include <psapi.h> @@ -22,6 +23,7 @@ #include "base/message_loop/message_loop.h" #include "base/metrics/histogram.h" #include "base/process/kill.h" +#include "base/strings/utf_string_conversions.h" #include "base/sys_info.h" #include "base/win/object_watcher.h" #include "base/win/scoped_handle.h" @@ -192,7 +194,8 @@ bool LaunchProcess(const string16& cmdline, &temp_process_info); DestroyEnvironmentBlock(enviroment_block); if (!launched) { - DPLOG(ERROR); + DPLOG(ERROR) << "Command line:" << std::endl << UTF16ToUTF8(cmdline) + << std::endl;; return false; } } else { @@ -200,7 +203,8 @@ bool LaunchProcess(const string16& cmdline, const_cast<wchar_t*>(cmdline.c_str()), NULL, NULL, inherit_handles, flags, NULL, NULL, startup_info, &temp_process_info)) { - DPLOG(ERROR); + DPLOG(ERROR) << "Command line:" << std::endl << UTF16ToUTF8(cmdline) + << std::endl;; return false; } } @@ -239,6 +243,41 @@ bool LaunchProcess(const CommandLine& cmdline, return rv; } +bool LaunchElevatedProcess(const CommandLine& cmdline, + const LaunchOptions& options, + ProcessHandle* process_handle) { + const string16 file = cmdline.GetProgram().value(); + const string16 arguments = cmdline.GetArgumentsString(); + + SHELLEXECUTEINFO shex_info = {0}; + shex_info.cbSize = sizeof(shex_info); + shex_info.fMask = SEE_MASK_NOCLOSEPROCESS; + shex_info.hwnd = GetActiveWindow(); + shex_info.lpVerb = L"runas"; + shex_info.lpFile = file.c_str(); + shex_info.lpParameters = arguments.c_str(); + shex_info.lpDirectory = NULL; + shex_info.nShow = options.start_hidden ? SW_HIDE : SW_SHOW; + shex_info.hInstApp = NULL; + + if (!ShellExecuteEx(&shex_info)) { + DPLOG(ERROR); + return false; + } + + if (options.wait) + WaitForSingleObject(shex_info.hProcess, INFINITE); + + // If the caller wants the process handle give it to them, otherwise just + // close it. Closing it does not terminate the process. + if (process_handle) + *process_handle = shex_info.hProcess; + else + CloseHandle(shex_info.hProcess); + + return true; +} + bool SetJobObjectLimitFlags(HANDLE job_object, DWORD limit_flags) { JOBOBJECT_EXTENDED_LIMIT_INFORMATION limit_info = {0}; limit_info.BasicLimitInformation.LimitFlags = limit_flags; diff --git a/chromium/base/process/memory.cc b/chromium/base/process/memory.cc new file mode 100644 index 00000000000..1dbc3630703 --- /dev/null +++ b/chromium/base/process/memory.cc @@ -0,0 +1,30 @@ +// 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/process/memory.h" + +namespace base { + +// 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 + +} diff --git a/chromium/base/process/memory.h b/chromium/base/process/memory.h index e6696cb8a70..100d9c72f43 100644 --- a/chromium/base/process/memory.h +++ b/chromium/base/process/memory.h @@ -14,6 +14,14 @@ #include <windows.h> #endif +#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 low fragmentation heap (LFH) for every heaps of this process. This @@ -53,17 +61,21 @@ const int kMaxOomScore = 1000; BASE_EXPORT bool AdjustOOMScore(ProcessId process, int score); #endif -#if defined(OS_MACOSX) -// Very large images or svg canvases can cause huge mallocs. Skia -// does tricks on tcmalloc-based systems to allow malloc to fail with -// a NULL rather than hit the oom crasher. This replicates that for -// OSX. -// -// IF YOU USE THIS WITHOUT CONSULTING YOUR FRIENDLY OSX DEVELOPER, -// YOUR CODE IS LIKELY TO BE REVERTED. THANK YOU. -BASE_EXPORT void* UncheckedMalloc(size_t size); -BASE_EXPORT void* UncheckedCalloc(size_t num_items, size_t size); -#endif // defined(OS_MACOSX) +// 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 diff --git a/chromium/base/process/memory_linux.cc b/chromium/base/process/memory_linux.cc index 6bed68bd832..befd832d667 100644 --- a/chromium/base/process/memory_linux.cc +++ b/chromium/base/process/memory_linux.cc @@ -12,6 +12,22 @@ #include "base/process/internal_linux.h" #include "base/strings/string_number_conversions.h" +#if defined(USE_TCMALLOC) +// Used by UncheckedMalloc. If tcmalloc is linked to the executable +// this will be replaced by a strong symbol that actually implement +// the semantics and don't call new handler in case the allocation fails. +extern "C" { + +__attribute__((weak, visibility("default"))) +void* tc_malloc_skip_new_handler_weak(size_t size); + +void* tc_malloc_skip_new_handler_weak(size_t size) { + return malloc(size); +} + +} +#endif + namespace base { size_t g_oom_size = 0U; @@ -44,7 +60,9 @@ 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: @@ -99,7 +117,9 @@ void* __libc_memalign(size_t alignment, size_t size); 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*) @@ -157,9 +177,7 @@ bool AdjustOOMScore(ProcessId process, int score) { DVLOG(1) << "Adjusting oom_score_adj of " << process << " to " << score_str; int score_len = static_cast<int>(score_str.length()); - return (score_len == file_util::WriteFile(oom_file, - score_str.c_str(), - score_len)); + 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 @@ -174,12 +192,22 @@ bool AdjustOOMScore(ProcessId process, int score) { std::string score_str = IntToString(converted_score); DVLOG(1) << "Adjusting oom_adj of " << process << " to " << score_str; int score_len = static_cast<int>(score_str.length()); - return (score_len == file_util::WriteFile(oom_file, - score_str.c_str(), - score_len)); + return (score_len == WriteFile(oom_file, score_str.c_str(), score_len)); } return false; } +bool UncheckedMalloc(size_t size, void** result) { +#if 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_weak(size); +#endif + return *result != NULL; +} + } // namespace base diff --git a/chromium/base/process/memory_mac.mm b/chromium/base/process/memory_mac.mm index 3e281cd8e3c..575e886a175 100644 --- a/chromium/base/process/memory_mac.mm +++ b/chromium/base/process/memory_mac.mm @@ -16,6 +16,7 @@ #include "base/lazy_instance.h" #include "base/logging.h" #include "base/mac/mac_util.h" +#include "base/mac/mach_logging.h" #include "base/scoped_clear_errno.h" #include "third_party/apple_apsl/CFBase.h" #include "third_party/apple_apsl/malloc.h" @@ -222,10 +223,12 @@ void DeprotectMallocZone(ChromeMallocZone* default_zone, reinterpret_cast<vm_region_info_t>(&info), &count, &unused); - CHECK(result == KERN_SUCCESS); + MACH_CHECK(result == KERN_SUCCESS, result) << "mach_vm_region"; - result = mach_port_deallocate(mach_task_self(), unused); - CHECK(result == KERN_SUCCESS); + // The kernel always returns a null object for VM_REGION_BASIC_INFO_64, but + // balance it with a deallocate in case this ever changes. See 10.9.2 + // xnu-2422.90.20/osfmk/vm/vm_map.c vm_map_region. + mach_port_deallocate(mach_task_self(), unused); // Does the region fully enclose the zone pointers? Possibly unwarranted // simplification used: using the size of a full version 8 malloc zone rather @@ -248,7 +251,7 @@ void DeprotectMallocZone(ChromeMallocZone* default_zone, *reprotection_length, false, info.protection | VM_PROT_WRITE); - CHECK(result == KERN_SUCCESS); + MACH_CHECK(result == KERN_SUCCESS, result) << "mach_vm_protect"; } } @@ -435,7 +438,7 @@ void oom_killer_new() { // === Core Foundation CFAllocators === bool CanGetContextForCFAllocator() { - return !base::mac::IsOSLaterThanMavericks_DontCallThis(); + return !base::mac::IsOSLaterThanYosemite_DontCallThis(); } CFAllocatorContext* ContextForCFAllocator(CFAllocatorRef allocator) { @@ -446,7 +449,8 @@ CFAllocatorContext* ContextForCFAllocator(CFAllocatorRef allocator) { return &our_allocator->_context; } else if (base::mac::IsOSLion() || base::mac::IsOSMountainLion() || - base::mac::IsOSMavericks()) { + base::mac::IsOSMavericks() || + base::mac::IsOSYosemite()) { ChromeCFAllocatorLions* our_allocator = const_cast<ChromeCFAllocatorLions*>( reinterpret_cast<const ChromeCFAllocatorLions*>(allocator)); @@ -502,26 +506,42 @@ id oom_killer_allocWithZone(id self, SEL _cmd, NSZone* zone) } // namespace -void* UncheckedMalloc(size_t size) { +bool UncheckedMalloc(size_t size, void** result) { if (g_old_malloc) { #if ARCH_CPU_32_BITS ScopedClearErrno clear_errno; ThreadLocalBooleanAutoReset flag(g_unchecked_alloc.Pointer(), true); #endif // ARCH_CPU_32_BITS - return g_old_malloc(malloc_default_zone(), size); + *result = g_old_malloc(malloc_default_zone(), size); + } else { + *result = malloc(size); } - return malloc(size); + + return *result != NULL; } -void* UncheckedCalloc(size_t num_items, size_t size) { +bool UncheckedCalloc(size_t num_items, size_t size, void** result) { if (g_old_calloc) { #if ARCH_CPU_32_BITS ScopedClearErrno clear_errno; ThreadLocalBooleanAutoReset flag(g_unchecked_alloc.Pointer(), true); #endif // ARCH_CPU_32_BITS - return g_old_calloc(malloc_default_zone(), num_items, size); + *result = g_old_calloc(malloc_default_zone(), num_items, size); + } else { + *result = calloc(num_items, size); } - return calloc(num_items, size); + + return *result != NULL; +} + +void* UncheckedMalloc(size_t size) { + void* address; + return UncheckedMalloc(size, &address) ? address : NULL; +} + +void* UncheckedCalloc(size_t num_items, size_t size) { + void* address; + return UncheckedCalloc(num_items, size, &address) ? address : NULL; } void EnableTerminationOnOutOfMemory() { @@ -630,7 +650,7 @@ void EnableTerminationOnOutOfMemory() { default_reprotection_length, false, default_reprotection_value); - CHECK(result == KERN_SUCCESS); + MACH_CHECK(result == KERN_SUCCESS, result) << "mach_vm_protect"; } if (purgeable_reprotection_start) { @@ -639,7 +659,7 @@ void EnableTerminationOnOutOfMemory() { purgeable_reprotection_length, false, purgeable_reprotection_value); - CHECK(result == KERN_SUCCESS); + MACH_CHECK(result == KERN_SUCCESS, result) << "mach_vm_protect"; } #endif @@ -703,8 +723,9 @@ void EnableTerminationOnOutOfMemory() { << "Failed to get kCFAllocatorMallocZone allocation function."; context->allocate = oom_killer_cfallocator_malloc_zone; } else { - NSLog(@"Internals of CFAllocator not known; out-of-memory failures via " - "CFAllocator will not result in termination. http://crbug.com/45650"); + DLOG(WARNING) << "Internals of CFAllocator not known; out-of-memory " + "failures via CFAllocator will not result in termination. " + "http://crbug.com/45650"; } #endif diff --git a/chromium/base/process/memory_unittest.cc b/chromium/base/process/memory_unittest.cc index e5c759d5711..048c09d38c1 100644 --- a/chromium/base/process/memory_unittest.cc +++ b/chromium/base/process/memory_unittest.cc @@ -121,9 +121,7 @@ TEST(ProcessMemoryTest, MacMallocFailureDoesNotTerminate) { buf = malloc(std::numeric_limits<size_t>::max() - (2 * PAGE_SIZE) - 1); }, testing::KilledBySignal(SIGTRAP), - "\\*\\*\\* error: can't allocate region.*" - "(Terminating process due to a potential for future heap " - "corruption){0}"); + "\\*\\*\\* error: can't allocate region.*\\n?.*"); base::debug::Alias(buf); } @@ -143,8 +141,8 @@ TEST(ProcessMemoryTest, MacTerminateOnHeapCorruption) { ASSERT_DEATH(free(buf), "attempting free on address which " "was not malloc\\(\\)-ed"); #else - ASSERT_DEATH(free(buf), "being freed.*" - "\\*\\*\\* set a breakpoint in malloc_error_break to debug.*" + ASSERT_DEATH(free(buf), "being freed.*\\n?\\.*" + "\\*\\*\\* set a breakpoint in malloc_error_break to debug.*\\n?.*" "Terminating process due to a potential for future heap corruption"); #endif // ARCH_CPU_64_BITS || defined(ADDRESS_SANITIZER) } @@ -154,13 +152,9 @@ TEST(ProcessMemoryTest, MacTerminateOnHeapCorruption) { // Android doesn't implement set_new_handler, so we can't use the // OutOfMemoryTest cases. // OpenBSD does not support these tests either. -// AddressSanitizer and ThreadSanitizer define the malloc()/free()/etc. -// functions so that they don't crash if the program is out of memory, so the -// OOM tests aren't supposed to work. // TODO(vandebo) make this work on Windows too. #if !defined(OS_ANDROID) && !defined(OS_OPENBSD) && \ - !defined(OS_WIN) && \ - !defined(ADDRESS_SANITIZER) && !defined(THREAD_SANITIZER) + !defined(OS_WIN) #if defined(USE_TCMALLOC) extern "C" { @@ -168,14 +162,14 @@ int tc_set_new_mode(int mode); } #endif // defined(USE_TCMALLOC) -class OutOfMemoryDeathTest : public testing::Test { +class OutOfMemoryTest : public testing::Test { public: - OutOfMemoryDeathTest() - : value_(NULL), - // Make test size as large as possible minus a few pages so - // that alignment or other rounding doesn't make it wrap. - test_size_(std::numeric_limits<std::size_t>::max() - 12 * 1024), - signed_test_size_(std::numeric_limits<ssize_t>::max()) { + OutOfMemoryTest() + : value_(NULL), + // Make test size as large as possible minus a few pages so + // that alignment or other rounding doesn't make it wrap. + test_size_(std::numeric_limits<std::size_t>::max() - 12 * 1024), + signed_test_size_(std::numeric_limits<ssize_t>::max()) { } #if defined(USE_TCMALLOC) @@ -188,6 +182,14 @@ class OutOfMemoryDeathTest : public testing::Test { } #endif // defined(USE_TCMALLOC) + protected: + void* value_; + size_t test_size_; + ssize_t signed_test_size_; +}; + +class OutOfMemoryDeathTest : public OutOfMemoryTest { + public: void SetUpInDeathAssert() { // Must call EnableTerminationOnOutOfMemory() because that is called from // chrome's main function and therefore hasn't been called yet. @@ -196,10 +198,6 @@ class OutOfMemoryDeathTest : public testing::Test { // should be done inside of the ASSERT_DEATH. base::EnableTerminationOnOutOfMemory(); } - - void* value_; - size_t test_size_; - ssize_t signed_test_size_; }; TEST_F(OutOfMemoryDeathTest, New) { @@ -245,12 +243,15 @@ TEST_F(OutOfMemoryDeathTest, Valloc) { } #if defined(OS_LINUX) + +#if PVALLOC_AVAILABLE == 1 TEST_F(OutOfMemoryDeathTest, Pvalloc) { ASSERT_DEATH({ SetUpInDeathAssert(); value_ = pvalloc(test_size_); }, ""); } +#endif // PVALLOC_AVAILABLE == 1 TEST_F(OutOfMemoryDeathTest, Memalign) { ASSERT_DEATH({ @@ -374,5 +375,54 @@ TEST_F(OutOfMemoryDeathTest, PsychoticallyBigObjCObject) { #endif // !ARCH_CPU_64_BITS #endif // OS_MACOSX -#endif // !defined(OS_ANDROID) && !defined(OS_OPENBSD) && - // !defined(OS_WIN) && !defined(ADDRESS_SANITIZER) +class OutOfMemoryHandledTest : public OutOfMemoryTest { + public: + static const size_t kSafeMallocSize = 512; + static const size_t kSafeCallocSize = 128; + static const size_t kSafeCallocItems = 4; + + virtual void SetUp() { + OutOfMemoryTest::SetUp(); + + // We enable termination on OOM - just as Chrome does at early + // initialization - and test that UncheckedMalloc and UncheckedCalloc + // properly by-pass this in order to allow the caller to handle OOM. + base::EnableTerminationOnOutOfMemory(); + } +}; + +// TODO(b.kelemen): make UncheckedMalloc and UncheckedCalloc work +// on Windows as well. +// UncheckedMalloc() and UncheckedCalloc() work as regular malloc()/calloc() +// under sanitizer tools. +#if !defined(MEMORY_TOOL_REPLACES_ALLOCATOR) +TEST_F(OutOfMemoryHandledTest, UncheckedMalloc) { + EXPECT_TRUE(base::UncheckedMalloc(kSafeMallocSize, &value_)); + EXPECT_TRUE(value_ != NULL); + free(value_); + + EXPECT_FALSE(base::UncheckedMalloc(test_size_, &value_)); + EXPECT_TRUE(value_ == NULL); +} + +TEST_F(OutOfMemoryHandledTest, UncheckedCalloc) { + EXPECT_TRUE(base::UncheckedCalloc(1, kSafeMallocSize, &value_)); + EXPECT_TRUE(value_ != NULL); + const char* bytes = static_cast<const char*>(value_); + for (size_t i = 0; i < kSafeMallocSize; ++i) + EXPECT_EQ(0, bytes[i]); + free(value_); + + EXPECT_TRUE( + base::UncheckedCalloc(kSafeCallocItems, kSafeCallocSize, &value_)); + EXPECT_TRUE(value_ != NULL); + bytes = static_cast<const char*>(value_); + for (size_t i = 0; i < (kSafeCallocItems * kSafeCallocSize); ++i) + EXPECT_EQ(0, bytes[i]); + free(value_); + + EXPECT_FALSE(base::UncheckedCalloc(1, test_size_, &value_)); + EXPECT_TRUE(value_ == NULL); +} +#endif // !defined(MEMORY_TOOL_REPLACES_ALLOCATOR) +#endif // !defined(OS_ANDROID) && !defined(OS_OPENBSD) && !defined(OS_WIN) diff --git a/chromium/base/process/memory_win.cc b/chromium/base/process/memory_win.cc index c53a1be5395..668214ceaf0 100644 --- a/chromium/base/process/memory_win.cc +++ b/chromium/base/process/memory_win.cc @@ -82,4 +82,15 @@ HMODULE GetModuleFromAddress(void* address) { return instance; } +// TODO(b.kelemen): implement it with the required semantics. On Linux this is +// implemented with a weak symbol that is overridden by tcmalloc. This is +// neccessary because base cannot have a direct dependency on tcmalloc. Since +// weak symbols are not supported on Windows this will involve some build time +// magic, much like what is done for libcrt in order to override the allocation +// functions. +bool UncheckedMalloc(size_t size, void** result) { + *result = malloc(size); + return *result != NULL; +} + } // namespace base diff --git a/chromium/base/process/process_handle.h b/chromium/base/process/process_handle.h index a37784275ea..6f8094ee80b 100644 --- a/chromium/base/process/process_handle.h +++ b/chromium/base/process/process_handle.h @@ -81,12 +81,10 @@ BASE_EXPORT bool GetProcessIntegrityLevel(ProcessHandle process, IntegrityLevel* level); #endif -#if defined(OS_LINUX) || defined(OS_ANDROID) || defined(OS_BSD) +#if defined(OS_POSIX) // Returns the path to the executable of the given process. BASE_EXPORT FilePath GetProcessExecutablePath(ProcessHandle process); -#endif -#if defined(OS_POSIX) // Returns the ID for the parent of the given process. BASE_EXPORT ProcessId GetParentProcessId(ProcessHandle process); #endif diff --git a/chromium/base/process/process_handle_freebsd.cc b/chromium/base/process/process_handle_freebsd.cc index 8a0e9de8f2d..e465a85b7e0 100644 --- a/chromium/base/process/process_handle_freebsd.cc +++ b/chromium/base/process/process_handle_freebsd.cc @@ -6,6 +6,7 @@ #include <sys/sysctl.h> #include <sys/types.h> +#include <sys/user.h> #include <unistd.h> namespace base { diff --git a/chromium/base/process/process_handle_linux.cc b/chromium/base/process/process_handle_linux.cc index 0f7ccd348a8..6c5cd2fa182 100644 --- a/chromium/base/process/process_handle_linux.cc +++ b/chromium/base/process/process_handle_linux.cc @@ -11,7 +11,7 @@ namespace base { ProcessId GetParentProcessId(ProcessHandle process) { ProcessId pid = - internal::ReadProcStatsAndGetFieldAsInt(process, internal::VM_PPID); + internal::ReadProcStatsAndGetFieldAsInt64(process, internal::VM_PPID); if (pid) return pid; return -1; diff --git a/chromium/base/process/process_handle_mac.cc b/chromium/base/process/process_handle_mac.cc index 6cb8d686e4d..cbf0bc5c439 100644 --- a/chromium/base/process/process_handle_mac.cc +++ b/chromium/base/process/process_handle_mac.cc @@ -4,6 +4,7 @@ #include "base/process/process_handle.h" +#include <libproc.h> #include <sys/sysctl.h> #include <sys/types.h> @@ -24,4 +25,12 @@ ProcessId GetParentProcessId(ProcessHandle process) { return info.kp_eproc.e_ppid; } +FilePath GetProcessExecutablePath(ProcessHandle process) { + char pathbuf[PROC_PIDPATHINFO_MAXSIZE]; + if (!proc_pidpath(process, pathbuf, sizeof(pathbuf))) + return FilePath(); + + return FilePath(pathbuf); +} + } // namespace base diff --git a/chromium/base/process/process_info_linux.cc b/chromium/base/process/process_info_linux.cc index da34c180e8a..9ec23135bfc 100644 --- a/chromium/base/process/process_info_linux.cc +++ b/chromium/base/process/process_info_linux.cc @@ -15,8 +15,8 @@ namespace base { //static const Time CurrentProcessInfo::CreationTime() { ProcessHandle pid = GetCurrentProcessHandle(); - int start_ticks = internal::ReadProcStatsAndGetFieldAsInt( - pid, internal::VM_STARTTIME); + int64 start_ticks = + internal::ReadProcStatsAndGetFieldAsInt64(pid, internal::VM_STARTTIME); DCHECK(start_ticks); TimeDelta start_offset = internal::ClockTicksToTimeDelta(start_ticks); Time boot_time = internal::GetBootTime(); diff --git a/chromium/base/process/process_info_mac.cc b/chromium/base/process/process_info_mac.cc index ab8394b891d..b7cfdceda05 100644 --- a/chromium/base/process/process_info_mac.cc +++ b/chromium/base/process/process_info_mac.cc @@ -21,7 +21,7 @@ const Time CurrentProcessInfo::CreationTime() { if (sysctl(mib, arraysize(mib), NULL, &len, NULL, 0) < 0) return Time(); - scoped_ptr_malloc<struct kinfo_proc> + scoped_ptr<struct kinfo_proc, base::FreeDeleter> proc(static_cast<struct kinfo_proc*>(malloc(len))); if (sysctl(mib, arraysize(mib), proc.get(), &len, NULL, 0) < 0) return Time(); diff --git a/chromium/base/process/process_iterator.h b/chromium/base/process/process_iterator.h index aa8ba74aba0..aa2fc4148ad 100644 --- a/chromium/base/process/process_iterator.h +++ b/chromium/base/process/process_iterator.h @@ -20,8 +20,10 @@ #if defined(OS_WIN) #include <windows.h> #include <tlhelp32.h> -#elif defined(OS_MACOSX) || defined(OS_BSD) +#elif defined(OS_MACOSX) || defined(OS_OPENBSD) #include <sys/sysctl.h> +#elif defined(OS_FREEBSD) +#include <sys/user.h> #elif defined(OS_POSIX) #include <dirent.h> #endif diff --git a/chromium/base/process/process_iterator_freebsd.cc b/chromium/base/process/process_iterator_freebsd.cc index e8225b0054e..5b1e2ab09db 100644 --- a/chromium/base/process/process_iterator_freebsd.cc +++ b/chromium/base/process/process_iterator_freebsd.cc @@ -4,7 +4,9 @@ #include "base/process/process_iterator.h" +#include <sys/types.h> #include <sys/sysctl.h> +#include <unistd.h> #include "base/logging.h" #include "base/strings/string_util.h" diff --git a/chromium/base/process/process_iterator_linux.cc b/chromium/base/process/process_iterator_linux.cc index 0587d7b88c5..8f746aa3391 100644 --- a/chromium/base/process/process_iterator_linux.cc +++ b/chromium/base/process/process_iterator_linux.cc @@ -121,8 +121,8 @@ bool ProcessIterator::CheckForNextProcess() { } entry_.pid_ = pid; - entry_.ppid_ = GetProcStatsFieldAsInt(proc_stats, internal::VM_PPID); - entry_.gid_ = GetProcStatsFieldAsInt(proc_stats, internal::VM_PGRP); + entry_.ppid_ = GetProcStatsFieldAsInt64(proc_stats, internal::VM_PPID); + entry_.gid_ = GetProcStatsFieldAsInt64(proc_stats, internal::VM_PGRP); entry_.cmd_line_args_.assign(cmd_line_args.begin(), cmd_line_args.end()); entry_.exe_file_ = GetProcessExecutablePath(pid).BaseName().value(); return true; diff --git a/chromium/base/process/process_iterator_mac.cc b/chromium/base/process/process_iterator_mac.cc index 29daa2d489f..e35c2ae19ba 100644 --- a/chromium/base/process/process_iterator_mac.cc +++ b/chromium/base/process/process_iterator_mac.cc @@ -7,6 +7,7 @@ #include <errno.h> #include <sys/sysctl.h> #include <sys/types.h> +#include <unistd.h> #include "base/logging.h" #include "base/strings/string_util.h" diff --git a/chromium/base/process/process_linux.cc b/chromium/base/process/process_linux.cc index b12e994848d..d92d7c30eee 100644 --- a/chromium/base/process/process_linux.cc +++ b/chromium/base/process/process_linux.cc @@ -14,6 +14,8 @@ #include "base/strings/stringprintf.h" #include "base/synchronization/lock.h" +namespace base { + namespace { const int kForegroundPriority = 0; @@ -46,13 +48,13 @@ struct CGroups { base::FilePath(base::StringPrintf(kControlPath, kForeground)); background_file = base::FilePath(base::StringPrintf(kControlPath, kBackground)); - file_util::FileSystemType foreground_type; - file_util::FileSystemType background_type; + base::FileSystemType foreground_type; + base::FileSystemType background_type; enabled = - file_util::GetFileSystemType(foreground_file, &foreground_type) && - file_util::GetFileSystemType(background_file, &background_type) && - foreground_type == file_util::FILE_SYSTEM_CGROUP && - background_type == file_util::FILE_SYSTEM_CGROUP; + base::GetFileSystemType(foreground_file, &foreground_type) && + base::GetFileSystemType(background_file, &background_type) && + foreground_type == FILE_SYSTEM_CGROUP && + background_type == FILE_SYSTEM_CGROUP; } }; @@ -62,8 +64,6 @@ const int kBackgroundPriority = 5; #endif } -namespace base { - bool Process::IsProcessBackgrounded() const { DCHECK(process_); @@ -95,7 +95,7 @@ bool Process::SetProcessBackgrounded(bool background) { const base::FilePath file = background ? cgroups.Get().background_file : cgroups.Get().foreground_file; - return file_util::WriteFile(file, pid.c_str(), pid.size()) > 0; + return base::WriteFile(file, pid.c_str(), pid.size()) > 0; } #endif // OS_CHROMEOS diff --git a/chromium/base/process/process_metrics.cc b/chromium/base/process/process_metrics.cc index 83289b8c78b..95fb87ddf62 100644 --- a/chromium/base/process/process_metrics.cc +++ b/chromium/base/process/process_metrics.cc @@ -4,6 +4,7 @@ #include "base/process/process_metrics.h" +#include "base/logging.h" #include "base/values.h" namespace base { @@ -50,4 +51,11 @@ double ProcessMetrics::GetPlatformIndependentCPUUsage() { #endif } +#if !defined(OS_MACOSX) +int ProcessMetrics::GetIdleWakeupsPerSecond() { + NOTIMPLEMENTED(); // http://crbug.com/20488 + return 0; +} +#endif // !defined(OS_MACOSX) + } // namespace base diff --git a/chromium/base/process/process_metrics.h b/chromium/base/process/process_metrics.h index 560490a9b8c..d7a49ab5948 100644 --- a/chromium/base/process/process_metrics.h +++ b/chromium/base/process/process_metrics.h @@ -168,6 +168,10 @@ class BASE_EXPORT ProcessMetrics { // CPU, this method returns 50. double GetCPUUsage(); + // Returns the number of average idle cpu wakeups per second since the last + // call. + int GetIdleWakeupsPerSecond(); + // Same as GetCPUUsage(), but will return consistent values on all platforms // (cancelling the Windows exception mentioned above) by returning a value in // the range of 0 to (100 * numCPUCores) everywhere. @@ -201,9 +205,13 @@ class BASE_EXPORT ProcessMetrics { // Used to store the previous times and CPU usage counts so we can // compute the CPU usage between calls. - int64 last_time_; + TimeTicks last_cpu_time_; int64 last_system_time_; + // Same thing for idle wakeups. + TimeTicks last_idle_wakeups_time_; + int64 last_absolute_idle_wakeups_; + #if !defined(OS_IOS) #if defined(OS_MACOSX) // Queries the port provider if it's set. @@ -211,7 +219,7 @@ class BASE_EXPORT ProcessMetrics { PortProvider* port_provider_; #elif defined(OS_POSIX) - // Jiffie count at the last_time_ we updated. + // Jiffie count at the last_cpu_time_ we updated. int last_cpu_; #endif // defined(OS_POSIX) #endif // !defined(OS_IOS) @@ -227,6 +235,10 @@ BASE_EXPORT size_t GetSystemCommitCharge(); // Returns the maximum number of file descriptors that can be open by a process // at once. If the number is unavailable, a conservative best guess is returned. size_t GetMaxFds(); + +// Sets the file descriptor soft limit to |max_descriptors| or the OS hard +// limit, whichever is lower. +BASE_EXPORT void SetFdLimit(unsigned int max_descriptors); #endif // defined(OS_POSIX) #if defined(OS_LINUX) || defined(OS_ANDROID) diff --git a/chromium/base/process/process_metrics_freebsd.cc b/chromium/base/process/process_metrics_freebsd.cc index 019454cd81a..9d4149de2ad 100644 --- a/chromium/base/process/process_metrics_freebsd.cc +++ b/chromium/base/process/process_metrics_freebsd.cc @@ -4,11 +4,16 @@ #include "base/process/process_metrics.h" +#include <sys/sysctl.h> +#include <sys/user.h> +#include <unistd.h> + +#include "base/sys_info.h" + namespace base { ProcessMetrics::ProcessMetrics(ProcessHandle process) : process_(process), - last_time_(0), last_system_time_(0), last_cpu_(0) { processor_count_ = base::SysInfo::NumberOfProcessors(); @@ -81,11 +86,6 @@ double ProcessMetrics::GetCPUUsage() { int mib[] = { CTL_KERN, KERN_PROC, KERN_PROC_PID, process_ }; size_t length = sizeof(info); - struct timeval now; - int retval = gettimeofday(&now, NULL); - if (retval) - return 0; - if (sysctl(mib, arraysize(mib), &info, &length, NULL, 0) < 0) return 0; diff --git a/chromium/base/process/process_metrics_ios.cc b/chromium/base/process/process_metrics_ios.cc index 94c671901b6..405c373c9fd 100644 --- a/chromium/base/process/process_metrics_ios.cc +++ b/chromium/base/process/process_metrics_ios.cc @@ -61,4 +61,8 @@ size_t GetMaxFds() { return static_cast<size_t>(max_fds); } +void SetFdLimit(unsigned int max_descriptors) { + // Unimplemented. +} + } // namespace base diff --git a/chromium/base/process/process_metrics_linux.cc b/chromium/base/process/process_metrics_linux.cc index afa88486a0b..0e12595f92e 100644 --- a/chromium/base/process/process_metrics_linux.cc +++ b/chromium/base/process/process_metrics_linux.cc @@ -36,7 +36,7 @@ static uint64 ReadFileToUint64(const base::FilePath file) { std::string file_as_string; if (!ReadFileToString(file, &file_as_string)) return 0; - TrimWhitespaceASCII(file_as_string, TRIM_ALL, &file_as_string); + base::TrimWhitespaceASCII(file_as_string, base::TRIM_ALL, &file_as_string); uint64 file_as_uint64 = 0; if (!base::StringToUint64(file_as_string, &file_as_uint64)) return 0; @@ -71,7 +71,8 @@ size_t ReadProcStatusAndGetFieldAsSizeT(pid_t pid, const std::string& field) { std::string value_str; tokenizer.token_piece().CopyToString(&value_str); std::string value_str_trimmed; - TrimWhitespaceASCII(value_str, TRIM_ALL, &value_str_trimmed); + base::TrimWhitespaceASCII(value_str, base::TRIM_ALL, + &value_str_trimmed); std::vector<std::string> split_value_str; SplitString(value_str_trimmed, ' ', &split_value_str); if (split_value_str.size() != 2 || split_value_str[1] != "kB") { @@ -181,20 +182,16 @@ bool ProcessMetrics::GetWorkingSetKBytes(WorkingSetKBytes* ws_usage) const { } double ProcessMetrics::GetCPUUsage() { - struct timeval now; - int retval = gettimeofday(&now, NULL); - if (retval) - return 0; - int64 time = TimeValToMicroseconds(now); + TimeTicks time = TimeTicks::Now(); - if (last_time_ == 0) { + if (last_cpu_ == 0) { // First call, just set the last values. - last_time_ = time; + last_cpu_time_ = time; last_cpu_ = GetProcessCPU(process_); return 0; } - int64 time_delta = time - last_time_; + int64 time_delta = (time - last_cpu_time_).InMicroseconds(); DCHECK_NE(time_delta, 0); if (time_delta == 0) return 0; @@ -209,7 +206,7 @@ double ProcessMetrics::GetCPUUsage() { int percentage = 100 * (cpu_time - last_cpu_time).InSecondsF() / TimeDelta::FromMicroseconds(time_delta).InSecondsF(); - last_time_ = time; + last_cpu_time_ = time; last_cpu_ = cpu; return percentage; @@ -262,7 +259,6 @@ bool ProcessMetrics::GetIOCounters(IoCounters* io_counters) const { ProcessMetrics::ProcessMetrics(ProcessHandle process) : process_(process), - last_time_(0), last_system_time_(0), last_cpu_(0) { processor_count_ = base::SysInfo::NumberOfProcessors(); @@ -391,16 +387,16 @@ int ParseProcStatCPU(const std::string& input) { if (proc_stats.size() <= internal::VM_STIME) return -1; - int utime = GetProcStatsFieldAsInt(proc_stats, internal::VM_UTIME); - int stime = GetProcStatsFieldAsInt(proc_stats, internal::VM_STIME); + int utime = GetProcStatsFieldAsInt64(proc_stats, internal::VM_UTIME); + int stime = GetProcStatsFieldAsInt64(proc_stats, internal::VM_STIME); return utime + stime; } const char kProcSelfExe[] = "/proc/self/exe"; int GetNumberOfThreads(ProcessHandle process) { - return internal::ReadProcStatsAndGetFieldAsInt(process, - internal::VM_NUMTHREADS); + return internal::ReadProcStatsAndGetFieldAsInt64(process, + internal::VM_NUMTHREADS); } namespace { diff --git a/chromium/base/process/process_metrics_mac.cc b/chromium/base/process/process_metrics_mac.cc index 048735ed36b..ada04e1427f 100644 --- a/chromium/base/process/process_metrics_mac.cc +++ b/chromium/base/process/process_metrics_mac.cc @@ -11,9 +11,27 @@ #include "base/containers/hash_tables.h" #include "base/logging.h" +#include "base/mac/mach_logging.h" #include "base/mac/scoped_mach_port.h" #include "base/sys_info.h" +#if !defined(TASK_POWER_INFO) +// Doesn't exist in the 10.6 or 10.7 SDKs. +#define TASK_POWER_INFO 21 +struct task_power_info { + uint64_t total_user; + uint64_t total_system; + uint64_t task_interrupt_wakeups; + uint64_t task_platform_idle_wakeups; + uint64_t task_timer_wakeups_bin_1; + uint64_t task_timer_wakeups_bin_2; +}; +typedef struct task_power_info task_power_info_data_t; +typedef struct task_power_info *task_power_info_t; +#define TASK_POWER_INFO_COUNT ((mach_msg_type_number_t) \ + (sizeof (task_power_info_data_t) / sizeof (natural_t))) +#endif + namespace base { namespace { @@ -99,7 +117,6 @@ size_t ProcessMetrics::GetPeakWorkingSetSize() const { // shared_bytes is the size of shared resident memory. bool ProcessMetrics::GetMemoryBytes(size_t* private_bytes, size_t* shared_bytes) { - kern_return_t kr; size_t private_pages_count = 0; size_t shared_pages_count = 0; @@ -136,22 +153,26 @@ bool ProcessMetrics::GetMemoryBytes(size_t* private_bytes, vm_region_top_info_data_t info; mach_msg_type_number_t info_count = VM_REGION_TOP_INFO_COUNT; mach_port_t object_name; - kr = mach_vm_region(task, - &address, - &size, - VM_REGION_TOP_INFO, - (vm_region_info_t)&info, - &info_count, - &object_name); + kern_return_t kr = mach_vm_region(task, + &address, + &size, + VM_REGION_TOP_INFO, + reinterpret_cast<vm_region_info_t>(&info), + &info_count, + &object_name); if (kr == KERN_INVALID_ADDRESS) { // We're at the end of the address space. break; } else if (kr != KERN_SUCCESS) { - DLOG(ERROR) << "Calling mach_vm_region failed with error: " - << mach_error_string(kr); + MACH_DLOG(ERROR, kr) << "mach_vm_region"; return false; } + // The kernel always returns a null object for VM_REGION_TOP_INFO, but + // balance it with a deallocate in case this ever changes. See 10.9.2 + // xnu-2422.90.20/osfmk/vm/vm_map.c vm_map_region. + mach_port_deallocate(mach_task_self(), object_name); + if (IsAddressInSharedRegion(address, cpu_type) && info.share_mode != SM_PRIVATE) continue; @@ -179,18 +200,10 @@ bool ProcessMetrics::GetMemoryBytes(size_t* private_bytes, } } - vm_size_t page_size; - kr = host_page_size(task, &page_size); - if (kr != KERN_SUCCESS) { - DLOG(ERROR) << "Failed to fetch host page size, error: " - << mach_error_string(kr); - return false; - } - if (private_bytes) - *private_bytes = private_pages_count * page_size; + *private_bytes = private_pages_count * PAGE_SIZE; if (shared_bytes) - *shared_bytes = shared_pages_count * page_size; + *shared_bytes = shared_pages_count * PAGE_SIZE; return true; } @@ -218,16 +231,14 @@ double ProcessMetrics::GetCPUUsage() { if (task == MACH_PORT_NULL) return 0; - kern_return_t kr; - // Libtop explicitly loops over the threads (libtop_pinfo_update_cpu_usage() // in libtop.c), but this is more concise and gives the same results: task_thread_times_info thread_info_data; mach_msg_type_number_t thread_info_count = TASK_THREAD_TIMES_INFO_COUNT; - kr = task_info(task, - TASK_THREAD_TIMES_INFO, - reinterpret_cast<task_info_t>(&thread_info_data), - &thread_info_count); + kern_return_t kr = task_info(task, + TASK_THREAD_TIMES_INFO, + reinterpret_cast<task_info_t>(&thread_info_data), + &thread_info_count); if (kr != KERN_SUCCESS) { // Most likely cause: |task| is a zombie. return 0; @@ -250,33 +261,69 @@ double ProcessMetrics::GetCPUUsage() { timeradd(&user_timeval, &task_timeval, &task_timeval); timeradd(&system_timeval, &task_timeval, &task_timeval); - struct timeval now; - int retval = gettimeofday(&now, NULL); - if (retval) - return 0; - - int64 time = TimeValToMicroseconds(now); + TimeTicks time = TimeTicks::Now(); int64 task_time = TimeValToMicroseconds(task_timeval); - if ((last_system_time_ == 0) || (last_time_ == 0)) { + if (last_system_time_ == 0) { // First call, just set the last values. + last_cpu_time_ = time; last_system_time_ = task_time; - last_time_ = time; return 0; } int64 system_time_delta = task_time - last_system_time_; - int64 time_delta = time - last_time_; + int64 time_delta = (time - last_cpu_time_).InMicroseconds(); DCHECK_NE(0U, time_delta); if (time_delta == 0) return 0; + last_cpu_time_ = time; last_system_time_ = task_time; - last_time_ = time; return static_cast<double>(system_time_delta * 100.0) / time_delta; } +int ProcessMetrics::GetIdleWakeupsPerSecond() { + mach_port_t task = TaskForPid(process_); + if (task == MACH_PORT_NULL) + return 0; + + task_power_info power_info_data; + mach_msg_type_number_t power_info_count = TASK_POWER_INFO_COUNT; + kern_return_t kr = task_info(task, + TASK_POWER_INFO, + reinterpret_cast<task_info_t>(&power_info_data), + &power_info_count); + if (kr != KERN_SUCCESS) { + // Most likely cause: |task| is a zombie, or this is on a pre-10.8.4 system + // where TASK_POWER_INFO isn't supported yet. + return 0; + } + uint64_t absolute_idle_wakeups = power_info_data.task_platform_idle_wakeups; + + TimeTicks time = TimeTicks::Now(); + + if (last_absolute_idle_wakeups_ == 0) { + // First call, just set the last values. + last_idle_wakeups_time_ = time; + last_absolute_idle_wakeups_ = absolute_idle_wakeups; + return 0; + } + + int64 wakeups_delta = absolute_idle_wakeups - last_absolute_idle_wakeups_; + int64 time_delta = (time - last_idle_wakeups_time_).InMicroseconds(); + DCHECK_NE(0U, time_delta); + if (time_delta == 0) + return 0; + + last_idle_wakeups_time_ = time; + last_absolute_idle_wakeups_ = absolute_idle_wakeups; + + // Round to average wakeups per second. + const int kMicrosecondsPerSecond = 1000 * 1000; + return (wakeups_delta * kMicrosecondsPerSecond + time_delta/2) / time_delta; +} + bool ProcessMetrics::GetIOCounters(IoCounters* io_counters) const { return false; } @@ -284,8 +331,8 @@ bool ProcessMetrics::GetIOCounters(IoCounters* io_counters) const { ProcessMetrics::ProcessMetrics(ProcessHandle process, ProcessMetrics::PortProvider* port_provider) : process_(process), - last_time_(0), last_system_time_(0), + last_absolute_idle_wakeups_(0), port_provider_(port_provider) { processor_count_ = SysInfo::NumberOfProcessors(); } @@ -301,25 +348,18 @@ mach_port_t ProcessMetrics::TaskForPid(ProcessHandle process) const { // Bytes committed by the system. size_t GetSystemCommitCharge() { - base::mac::ScopedMachPort host(mach_host_self()); + base::mac::ScopedMachSendRight host(mach_host_self()); mach_msg_type_number_t count = HOST_VM_INFO_COUNT; vm_statistics_data_t data; kern_return_t kr = host_statistics(host, HOST_VM_INFO, reinterpret_cast<host_info_t>(&data), &count); - if (kr) { - DLOG(WARNING) << "Failed to fetch host statistics."; - return 0; - } - - vm_size_t page_size; - kr = host_page_size(host, &page_size); - if (kr) { - DLOG(ERROR) << "Failed to fetch host page size."; + if (kr != KERN_SUCCESS) { + MACH_DLOG(WARNING, kr) << "host_statistics"; return 0; } - return (data.active_count * page_size) / 1024; + return (data.active_count * PAGE_SIZE) / 1024; } } // namespace base diff --git a/chromium/base/process/process_metrics_openbsd.cc b/chromium/base/process/process_metrics_openbsd.cc index 36f607c0a9c..72927a1b578 100644 --- a/chromium/base/process/process_metrics_openbsd.cc +++ b/chromium/base/process/process_metrics_openbsd.cc @@ -106,22 +106,16 @@ static int GetProcessCPU(pid_t pid) { } double ProcessMetrics::GetCPUUsage() { - struct timeval now; + TimeTicks time = TimeTicks::Now(); - int retval = gettimeofday(&now, NULL); - if (retval) - return 0; - - int64 time = TimeValToMicroseconds(now); - - if (last_time_ == 0) { + if (last_cpu_ == 0) { // First call, just set the last values. - last_time_ = time; + last_cpu_time_ = time; last_cpu_ = GetProcessCPU(process_); return 0; } - int64 time_delta = time - last_time_; + int64 time_delta = (time - last_cpu_time_).InMicroseconds(); DCHECK_NE(time_delta, 0); if (time_delta == 0) @@ -129,7 +123,7 @@ double ProcessMetrics::GetCPUUsage() { int cpu = GetProcessCPU(process_); - last_time_ = time; + last_cpu_time_ = time; last_cpu_ = cpu; double percentage = static_cast<double>((cpu * 100.0) / FSCALE); @@ -139,7 +133,6 @@ double ProcessMetrics::GetCPUUsage() { ProcessMetrics::ProcessMetrics(ProcessHandle process) : process_(process), - last_time_(0), last_system_time_(0), last_cpu_(0) { diff --git a/chromium/base/process/process_metrics_posix.cc b/chromium/base/process/process_metrics_posix.cc index 531f6a40d70..ea79d7348ff 100644 --- a/chromium/base/process/process_metrics_posix.cc +++ b/chromium/base/process/process_metrics_posix.cc @@ -52,4 +52,21 @@ size_t GetMaxFds() { return static_cast<size_t>(max_fds); } + +void SetFdLimit(unsigned int max_descriptors) { + struct rlimit limits; + if (getrlimit(RLIMIT_NOFILE, &limits) == 0) { + unsigned int new_limit = max_descriptors; + if (limits.rlim_max > 0 && limits.rlim_max < max_descriptors) { + new_limit = limits.rlim_max; + } + limits.rlim_cur = new_limit; + if (setrlimit(RLIMIT_NOFILE, &limits) != 0) { + PLOG(INFO) << "Failed to set file descriptor limit"; + } + } else { + PLOG(INFO) << "Failed to get file descriptor limit"; + } +} + } // namespace base diff --git a/chromium/base/process/process_metrics_win.cc b/chromium/base/process/process_metrics_win.cc index f42ea86feb7..b1810b4c921 100644 --- a/chromium/base/process/process_metrics_win.cc +++ b/chromium/base/process/process_metrics_win.cc @@ -195,14 +195,11 @@ static uint64 FileTimeToUTC(const FILETIME& ftime) { } double ProcessMetrics::GetCPUUsage() { - FILETIME now; FILETIME creation_time; FILETIME exit_time; FILETIME kernel_time; FILETIME user_time; - GetSystemTimeAsFileTime(&now); - if (!GetProcessTimes(process_, &creation_time, &exit_time, &kernel_time, &user_time)) { // We don't assert here because in some cases (such as in the Task Manager) @@ -212,17 +209,18 @@ double ProcessMetrics::GetCPUUsage() { } int64 system_time = (FileTimeToUTC(kernel_time) + FileTimeToUTC(user_time)) / processor_count_; - int64 time = FileTimeToUTC(now); + TimeTicks time = TimeTicks::Now(); - if ((last_system_time_ == 0) || (last_time_ == 0)) { + if (last_system_time_ == 0) { // First call, just set the last values. last_system_time_ = system_time; - last_time_ = time; + last_cpu_time_ = time; return 0; } int64 system_time_delta = system_time - last_system_time_; - int64 time_delta = time - last_time_; + // FILETIME is in 100-nanosecond units, so this needs microseconds times 10. + int64 time_delta = (time - last_cpu_time_).InMicroseconds() * 10; DCHECK_NE(0U, time_delta); if (time_delta == 0) return 0; @@ -232,7 +230,7 @@ double ProcessMetrics::GetCPUUsage() { time_delta); last_system_time_ = system_time; - last_time_ = time; + last_cpu_time_ = time; return cpu; } @@ -269,7 +267,6 @@ bool ProcessMetrics::GetIOCounters(IoCounters* io_counters) const { ProcessMetrics::ProcessMetrics(ProcessHandle process) : process_(process), processor_count_(base::SysInfo::NumberOfProcessors()), - last_time_(0), last_system_time_(0) { } diff --git a/chromium/base/process/process_util_unittest.cc b/chromium/base/process/process_util_unittest.cc index 6bfc1d07388..20623a60e18 100644 --- a/chromium/base/process/process_util_unittest.cc +++ b/chromium/base/process/process_util_unittest.cc @@ -144,8 +144,9 @@ MULTIPROCESS_TEST_MAIN(SimpleChildProcess) { return 0; } +// TODO(viettrungluu): This should be in a "MultiProcessTestTest". TEST_F(ProcessUtilTest, SpawnChild) { - base::ProcessHandle handle = this->SpawnChild("SimpleChildProcess", false); + base::ProcessHandle handle = SpawnChild("SimpleChildProcess"); ASSERT_NE(base::kNullProcessHandle, handle); EXPECT_TRUE(base::WaitForSingleProcess( handle, TestTimeouts::action_max_timeout())); @@ -161,7 +162,7 @@ TEST_F(ProcessUtilTest, KillSlowChild) { const std::string signal_file = ProcessUtilTest::GetSignalFilePath(kSignalFileSlow); remove(signal_file.c_str()); - base::ProcessHandle handle = this->SpawnChild("SlowChildProcess", false); + base::ProcessHandle handle = SpawnChild("SlowChildProcess"); ASSERT_NE(base::kNullProcessHandle, handle); SignalChildren(signal_file.c_str()); EXPECT_TRUE(base::WaitForSingleProcess( @@ -175,7 +176,7 @@ TEST_F(ProcessUtilTest, DISABLED_GetTerminationStatusExit) { const std::string signal_file = ProcessUtilTest::GetSignalFilePath(kSignalFileSlow); remove(signal_file.c_str()); - base::ProcessHandle handle = this->SpawnChild("SlowChildProcess", false); + base::ProcessHandle handle = SpawnChild("SlowChildProcess"); ASSERT_NE(base::kNullProcessHandle, handle); int exit_code = 42; @@ -198,7 +199,7 @@ TEST_F(ProcessUtilTest, DISABLED_GetTerminationStatusExit) { TEST_F(ProcessUtilTest, GetProcId) { base::ProcessId id1 = base::GetProcId(GetCurrentProcess()); EXPECT_NE(0ul, id1); - base::ProcessHandle handle = this->SpawnChild("SimpleChildProcess", false); + base::ProcessHandle handle = SpawnChild("SimpleChildProcess"); ASSERT_NE(base::kNullProcessHandle, handle); base::ProcessId id2 = base::GetProcId(handle); EXPECT_NE(0ul, id2); @@ -233,8 +234,7 @@ MULTIPROCESS_TEST_MAIN(CrashingChildProcess) { // This test intentionally crashes, so we don't need to run it under // AddressSanitizer. -// TODO(jschuh): crbug.com/175753 Fix this in Win64 bots. -#if defined(ADDRESS_SANITIZER) || (defined(OS_WIN) && defined(ARCH_CPU_X86_64)) +#if defined(ADDRESS_SANITIZER) || defined(SYZYASAN) #define MAYBE_GetTerminationStatusCrash DISABLED_GetTerminationStatusCrash #else #define MAYBE_GetTerminationStatusCrash GetTerminationStatusCrash @@ -243,8 +243,7 @@ TEST_F(ProcessUtilTest, MAYBE_GetTerminationStatusCrash) { const std::string signal_file = ProcessUtilTest::GetSignalFilePath(kSignalFileCrash); remove(signal_file.c_str()); - base::ProcessHandle handle = this->SpawnChild("CrashingChildProcess", - false); + base::ProcessHandle handle = SpawnChild("CrashingChildProcess"); ASSERT_NE(base::kNullProcessHandle, handle); int exit_code = 42; @@ -291,8 +290,7 @@ TEST_F(ProcessUtilTest, GetTerminationStatusKill) { const std::string signal_file = ProcessUtilTest::GetSignalFilePath(kSignalFileKill); remove(signal_file.c_str()); - base::ProcessHandle handle = this->SpawnChild("KilledChildProcess", - false); + base::ProcessHandle handle = SpawnChild("KilledChildProcess"); ASSERT_NE(base::kNullProcessHandle, handle); int exit_code = 42; @@ -322,7 +320,7 @@ TEST_F(ProcessUtilTest, GetTerminationStatusKill) { // Note: a platform may not be willing or able to lower the priority of // a process. The calls to SetProcessBackground should be noops then. TEST_F(ProcessUtilTest, SetProcessBackgrounded) { - base::ProcessHandle handle = this->SpawnChild("SimpleChildProcess", false); + base::ProcessHandle handle = SpawnChild("SimpleChildProcess"); base::Process process(handle); int old_priority = process.GetPriority(); #if defined(OS_WIN) @@ -393,8 +391,8 @@ TEST_F(ProcessUtilTest, LaunchAsUser) { ASSERT_TRUE(OpenProcessToken(GetCurrentProcess(), TOKEN_ALL_ACCESS, &token)); base::LaunchOptions options; options.as_user = token; - EXPECT_TRUE(base::LaunchProcess( - this->MakeCmdLine("SimpleChildProcess", false), options, NULL)); + EXPECT_TRUE(base::LaunchProcess(MakeCmdLine("SimpleChildProcess"), options, + NULL)); } static const char kEventToTriggerHandleSwitch[] = "event-to-trigger-handle"; @@ -430,7 +428,7 @@ TEST_F(ProcessUtilTest, InheritSpecifiedHandles) { base::LaunchOptions options; options.handles_to_inherit = &handles_to_inherit; - CommandLine cmd_line = MakeCmdLine("TriggerEventChildProcess", false); + CommandLine cmd_line = MakeCmdLine("TriggerEventChildProcess"); cmd_line.AppendSwitchASCII(kEventToTriggerHandleSwitch, base::Uint64ToString(reinterpret_cast<uint64>(event.handle()))); @@ -505,8 +503,10 @@ int ProcessUtilTest::CountOpenFDsInChild() { base::FileHandleMappingVector fd_mapping_vec; fd_mapping_vec.push_back(std::pair<int, int>(fds[1], kChildPipe)); - base::ProcessHandle handle = this->SpawnChild( - "ProcessUtilsLeakFDChildProcess", fd_mapping_vec, false); + base::LaunchOptions options; + options.fds_to_remap = &fd_mapping_vec; + base::ProcessHandle handle = + SpawnChildWithOptions("ProcessUtilsLeakFDChildProcess", options); CHECK(handle); int ret = IGNORE_EINTR(close(fds[1])); DPCHECK(ret == 0); @@ -517,7 +517,7 @@ int ProcessUtilTest::CountOpenFDsInChild() { HANDLE_EINTR(read(fds[0], &num_open_files, sizeof(num_open_files))); CHECK_EQ(bytes_read, static_cast<ssize_t>(sizeof(num_open_files))); -#if defined(THREAD_SANITIZER) || defined(USE_HEAPCHECKER) +#if defined(THREAD_SANITIZER) // Compiler-based ThreadSanitizer makes this test slow. CHECK(base::WaitForSingleProcess(handle, base::TimeDelta::FromSeconds(3))); #else @@ -562,15 +562,12 @@ TEST_F(ProcessUtilTest, MAYBE_FDRemapping) { namespace { -std::string TestLaunchProcess(const base::EnvironmentMap& env_changes, +std::string TestLaunchProcess(const std::vector<std::string>& args, + const base::EnvironmentMap& env_changes, + const bool clear_environ, const int clone_flags) { - std::vector<std::string> args; base::FileHandleMappingVector fds_to_remap; - args.push_back(kPosixShell); - args.push_back("-c"); - args.push_back("echo $BASE_TEST"); - int fds[2]; PCHECK(pipe(fds) == 0); @@ -578,6 +575,7 @@ std::string TestLaunchProcess(const base::EnvironmentMap& env_changes, base::LaunchOptions options; options.wait = true; options.environ = env_changes; + options.clear_environ = clear_environ; options.fds_to_remap = &fds_to_remap; #if defined(OS_LINUX) options.clone_flags = clone_flags; @@ -589,7 +587,6 @@ std::string TestLaunchProcess(const base::EnvironmentMap& env_changes, char buf[512]; const ssize_t n = HANDLE_EINTR(read(fds[0], buf, sizeof(buf))); - PCHECK(n > 0); PCHECK(IGNORE_EINTR(close(fds[0])) == 0); @@ -609,37 +606,69 @@ const char kLargeString[] = TEST_F(ProcessUtilTest, LaunchProcess) { base::EnvironmentMap env_changes; + std::vector<std::string> echo_base_test; + echo_base_test.push_back(kPosixShell); + echo_base_test.push_back("-c"); + echo_base_test.push_back("echo $BASE_TEST"); + + std::vector<std::string> print_env; + print_env.push_back("/usr/bin/env"); const int no_clone_flags = 0; + const bool no_clear_environ = false; const char kBaseTest[] = "BASE_TEST"; env_changes[kBaseTest] = "bar"; - EXPECT_EQ("bar\n", TestLaunchProcess(env_changes, no_clone_flags)); + EXPECT_EQ("bar\n", + TestLaunchProcess( + echo_base_test, env_changes, no_clear_environ, no_clone_flags)); env_changes.clear(); EXPECT_EQ(0, setenv(kBaseTest, "testing", 1 /* override */)); - EXPECT_EQ("testing\n", TestLaunchProcess(env_changes, no_clone_flags)); + EXPECT_EQ("testing\n", + TestLaunchProcess( + echo_base_test, env_changes, no_clear_environ, no_clone_flags)); env_changes[kBaseTest] = std::string(); - EXPECT_EQ("\n", TestLaunchProcess(env_changes, no_clone_flags)); + EXPECT_EQ("\n", + TestLaunchProcess( + echo_base_test, env_changes, no_clear_environ, no_clone_flags)); env_changes[kBaseTest] = "foo"; - EXPECT_EQ("foo\n", TestLaunchProcess(env_changes, no_clone_flags)); + EXPECT_EQ("foo\n", + TestLaunchProcess( + echo_base_test, env_changes, no_clear_environ, no_clone_flags)); env_changes.clear(); EXPECT_EQ(0, setenv(kBaseTest, kLargeString, 1 /* override */)); EXPECT_EQ(std::string(kLargeString) + "\n", - TestLaunchProcess(env_changes, no_clone_flags)); + TestLaunchProcess( + echo_base_test, env_changes, no_clear_environ, no_clone_flags)); env_changes[kBaseTest] = "wibble"; - EXPECT_EQ("wibble\n", TestLaunchProcess(env_changes, no_clone_flags)); + EXPECT_EQ("wibble\n", + TestLaunchProcess( + echo_base_test, env_changes, no_clear_environ, no_clone_flags)); #if defined(OS_LINUX) // Test a non-trival value for clone_flags. // Don't test on Valgrind as it has limited support for clone(). if (!RunningOnValgrind()) { - EXPECT_EQ("wibble\n", TestLaunchProcess(env_changes, CLONE_FS | SIGCHLD)); + EXPECT_EQ( + "wibble\n", + TestLaunchProcess( + echo_base_test, env_changes, no_clear_environ, CLONE_FS | SIGCHLD)); } + + EXPECT_EQ( + "BASE_TEST=wibble\n", + TestLaunchProcess( + print_env, env_changes, true /* clear_environ */, no_clone_flags)); + env_changes.clear(); + EXPECT_EQ( + "", + TestLaunchProcess( + print_env, env_changes, true /* clear_environ */, no_clone_flags)); #endif } @@ -677,7 +706,13 @@ TEST_F(ProcessUtilTest, GetAppOutput) { #endif // defined(OS_ANDROID) } -TEST_F(ProcessUtilTest, GetAppOutputRestricted) { +// Flakes on Android, crbug.com/375840 +#if defined(OS_ANDROID) +#define MAYBE_GetAppOutputRestricted DISABLED_GetAppOutputRestricted +#else +#define MAYBE_GetAppOutputRestricted GetAppOutputRestricted +#endif +TEST_F(ProcessUtilTest, MAYBE_GetAppOutputRestricted) { // Unfortunately, since we can't rely on the path, we need to know where // everything is. So let's use /bin/sh, which is on every POSIX system, and // its built-ins. @@ -812,8 +847,7 @@ bool IsProcessDead(base::ProcessHandle child) { } TEST_F(ProcessUtilTest, DelayedTermination) { - base::ProcessHandle child_process = - SpawnChild("process_util_test_never_die", false); + base::ProcessHandle child_process = SpawnChild("process_util_test_never_die"); ASSERT_TRUE(child_process); base::EnsureProcessTerminated(child_process); base::WaitForSingleProcess(child_process, base::TimeDelta::FromSeconds(5)); @@ -832,7 +866,7 @@ MULTIPROCESS_TEST_MAIN(process_util_test_never_die) { TEST_F(ProcessUtilTest, ImmediateTermination) { base::ProcessHandle child_process = - SpawnChild("process_util_test_die_immediately", false); + SpawnChild("process_util_test_die_immediately"); ASSERT_TRUE(child_process); // Give it time to die. sleep(2); |