diff options
author | rsesek@chromium.org <rsesek@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-05-31 15:38:01 +0000 |
---|---|---|
committer | rsesek@chromium.org <rsesek@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-05-31 15:38:01 +0000 |
commit | d3acb67574294e4e0b3b74840558ef81ebb433b5 (patch) | |
tree | 84472dcdaf777dfdecd1644e2256bd985d791f3d /base | |
parent | 52325b2a52aa5ea3adab9edbab67a10380c08618 (diff) | |
download | chromium_src-d3acb67574294e4e0b3b74840558ef81ebb433b5.zip chromium_src-d3acb67574294e4e0b3b74840558ef81ebb433b5.tar.gz chromium_src-d3acb67574294e4e0b3b74840558ef81ebb433b5.tar.bz2 |
Split ProcessIterator and friends out of base/process_util.h into base/process/process_iterator.h.
BUG=242290
R=brettw@chromium.org
Review URL: https://codereview.chromium.org/15806006
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@203412 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'base')
-rw-r--r-- | base/base.gypi | 13 | ||||
-rw-r--r-- | base/process/process_iterator.cc | 65 | ||||
-rw-r--r-- | base/process/process_iterator.h | 183 | ||||
-rw-r--r-- | base/process/process_iterator_freebsd.cc | 124 | ||||
-rw-r--r-- | base/process/process_iterator_linux.cc | 138 | ||||
-rw-r--r-- | base/process/process_iterator_mac.cc | 134 | ||||
-rw-r--r-- | base/process/process_iterator_openbsd.cc | 128 | ||||
-rw-r--r-- | base/process/process_iterator_win.cc | 41 | ||||
-rw-r--r-- | base/process_util.cc | 56 | ||||
-rw-r--r-- | base/process_util.h | 155 | ||||
-rw-r--r-- | base/process_util_freebsd.cc | 110 | ||||
-rw-r--r-- | base/process_util_linux.cc | 123 | ||||
-rw-r--r-- | base/process_util_mac.mm | 117 | ||||
-rw-r--r-- | base/process_util_openbsd.cc | 114 | ||||
-rw-r--r-- | base/process_util_win.cc | 32 |
15 files changed, 825 insertions, 708 deletions
diff --git a/base/base.gypi b/base/base.gypi index 8b1e93d..456bcb3 100644 --- a/base/base.gypi +++ b/base/base.gypi @@ -377,6 +377,15 @@ 'process_util_posix.cc', 'process_util_win.cc', 'process_win.cc', + 'process/internal_linux.cc', + 'process/internal_linux.h', + 'process/process_iterator.cc', + 'process/process_iterator.h', + 'process/process_iterator_freebsd.cc', + 'process/process_iterator_linux.cc', + 'process/process_iterator_mac.cc', + 'process/process_iterator_openbsd.cc', + 'process/process_iterator_win.cc', 'process/process_metrics.h', 'process/process_metrics_freebsd.cc', 'process/process_metrics_ios.cc', @@ -385,8 +394,6 @@ 'process/process_metrics_openbsd.cc', 'process/process_metrics_posix.cc', 'process/process_metrics_win.cc', - 'process/internal_linux.cc', - 'process/internal_linux.h', 'profiler/scoped_profile.cc', 'profiler/scoped_profile.h', 'profiler/alternate_timer.cc', @@ -689,6 +696,8 @@ ['include', '^files/file_path_watcher_linux\\.cc$'], ['include', '^process_util_linux\\.cc$'], ['include', '^process/internal_linux\\.cc$'], + ['include', '^process/process_iterator\\.cc$'], + ['include', '^process/process_iterator_linux\\.cc$'], ['include', '^process/process_metrics_linux\\.cc$'], ['include', '^posix/unix_domain_socket_linux\\.cc$'], ['include', '^strings/sys_string_conversions_posix\\.cc$'], diff --git a/base/process/process_iterator.cc b/base/process/process_iterator.cc new file mode 100644 index 0000000..b9ef047 --- /dev/null +++ b/base/process/process_iterator.cc @@ -0,0 +1,65 @@ +// Copyright (c) 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/process/process_iterator.h" + +namespace base { + +#if defined(OS_POSIX) +ProcessEntry::ProcessEntry() : pid_(0), ppid_(0), gid_(0) {} +ProcessEntry::~ProcessEntry() {} +#endif + +const ProcessEntry* ProcessIterator::NextProcessEntry() { + bool result = false; + do { + result = CheckForNextProcess(); + } while (result && !IncludeEntry()); + if (result) + return &entry_; + return NULL; +} + +ProcessIterator::ProcessEntries ProcessIterator::Snapshot() { + ProcessEntries found; + while (const ProcessEntry* process_entry = NextProcessEntry()) { + found.push_back(*process_entry); + } + return found; +} + +bool ProcessIterator::IncludeEntry() { + return !filter_ || filter_->Includes(entry_); +} + +NamedProcessIterator::NamedProcessIterator( + const FilePath::StringType& executable_name, + const ProcessFilter* filter) : ProcessIterator(filter), + executable_name_(executable_name) { +#if defined(OS_ANDROID) + // On Android, the process name contains only the last 15 characters, which + // is in file /proc/<pid>/stat, the string between open parenthesis and close + // parenthesis. Please See ProcessIterator::CheckForNextProcess for details. + // Now if the length of input process name is greater than 15, only save the + // last 15 characters. + if (executable_name_.size() > 15) { + executable_name_ = FilePath::StringType(executable_name_, + executable_name_.size() - 15, 15); + } +#endif +} + +NamedProcessIterator::~NamedProcessIterator() { +} + +int GetProcessCount(const FilePath::StringType& executable_name, + const ProcessFilter* filter) { + int count = 0; + NamedProcessIterator iter(executable_name, filter); + while (iter.NextProcessEntry()) + ++count; + return count; +} + +} // namespace base diff --git a/base/process/process_iterator.h b/base/process/process_iterator.h new file mode 100644 index 0000000..f8ab2a2 --- /dev/null +++ b/base/process/process_iterator.h @@ -0,0 +1,183 @@ +// Copyright (c) 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// This file contains methods to iterate over processes on the system. + +#ifndef BASE_PROCESS_PROCESS_ITERATOR_H_ +#define BASE_PROCESS_PROCESS_ITERATOR_H_ + +#include <list> +#include <string> +#include <vector> + +#include "build/build_config.h" +#include "base/basictypes.h" +#include "base/base_export.h" +#include "base/files/file_path.h" +#include "base/process.h" + +#if defined(OS_WIN) +#include <windows.h> +#include <tlhelp32.h> +#elif defined(OS_MACOSX) || defined(OS_BSD) +#include <sys/sysctl.h> +#elif defined(OS_POSIX) +#include <dirent.h> +#endif + +namespace base { + +#if defined(OS_WIN) +struct ProcessEntry : public PROCESSENTRY32 { + ProcessId pid() const { return th32ProcessID; } + ProcessId parent_pid() const { return th32ParentProcessID; } + const wchar_t* exe_file() const { return szExeFile; } +}; + +// Process access masks. These constants provide platform-independent +// definitions for the standard Windows access masks. +// See http://msdn.microsoft.com/en-us/library/ms684880(VS.85).aspx for +// the specific semantics of each mask value. +const uint32 kProcessAccessTerminate = PROCESS_TERMINATE; +const uint32 kProcessAccessCreateThread = PROCESS_CREATE_THREAD; +const uint32 kProcessAccessSetSessionId = PROCESS_SET_SESSIONID; +const uint32 kProcessAccessVMOperation = PROCESS_VM_OPERATION; +const uint32 kProcessAccessVMRead = PROCESS_VM_READ; +const uint32 kProcessAccessVMWrite = PROCESS_VM_WRITE; +const uint32 kProcessAccessDuplicateHandle = PROCESS_DUP_HANDLE; +const uint32 kProcessAccessCreateProcess = PROCESS_CREATE_PROCESS; +const uint32 kProcessAccessSetQuota = PROCESS_SET_QUOTA; +const uint32 kProcessAccessSetInformation = PROCESS_SET_INFORMATION; +const uint32 kProcessAccessQueryInformation = PROCESS_QUERY_INFORMATION; +const uint32 kProcessAccessSuspendResume = PROCESS_SUSPEND_RESUME; +const uint32 kProcessAccessQueryLimitedInfomation = + PROCESS_QUERY_LIMITED_INFORMATION; +const uint32 kProcessAccessWaitForTermination = SYNCHRONIZE; +#elif defined(OS_POSIX) +struct BASE_EXPORT ProcessEntry { + ProcessEntry(); + ~ProcessEntry(); + + ProcessId pid() const { return pid_; } + ProcessId parent_pid() const { return ppid_; } + ProcessId gid() const { return gid_; } + const char* exe_file() const { return exe_file_.c_str(); } + const std::vector<std::string>& cmd_line_args() const { + return cmd_line_args_; + } + + ProcessId pid_; + ProcessId ppid_; + ProcessId gid_; + std::string exe_file_; + std::vector<std::string> cmd_line_args_; +}; + +// Process access masks. They are not used on Posix because access checking +// does not happen during handle creation. +const uint32 kProcessAccessTerminate = 0; +const uint32 kProcessAccessCreateThread = 0; +const uint32 kProcessAccessSetSessionId = 0; +const uint32 kProcessAccessVMOperation = 0; +const uint32 kProcessAccessVMRead = 0; +const uint32 kProcessAccessVMWrite = 0; +const uint32 kProcessAccessDuplicateHandle = 0; +const uint32 kProcessAccessCreateProcess = 0; +const uint32 kProcessAccessSetQuota = 0; +const uint32 kProcessAccessSetInformation = 0; +const uint32 kProcessAccessQueryInformation = 0; +const uint32 kProcessAccessSuspendResume = 0; +const uint32 kProcessAccessQueryLimitedInfomation = 0; +const uint32 kProcessAccessWaitForTermination = 0; +#endif // defined(OS_POSIX) + +// Used to filter processes by process ID. +class ProcessFilter { + public: + // Returns true to indicate set-inclusion and false otherwise. This method + // should not have side-effects and should be idempotent. + virtual bool Includes(const ProcessEntry& entry) const = 0; + + protected: + virtual ~ProcessFilter() {} +}; + +// This class provides a way to iterate through a list of processes on the +// current machine with a specified filter. +// To use, create an instance and then call NextProcessEntry() until it returns +// false. +class BASE_EXPORT ProcessIterator { + public: + typedef std::list<ProcessEntry> ProcessEntries; + + explicit ProcessIterator(const ProcessFilter* filter); + virtual ~ProcessIterator(); + + // If there's another process that matches the given executable name, + // returns a const pointer to the corresponding PROCESSENTRY32. + // If there are no more matching processes, returns NULL. + // The returned pointer will remain valid until NextProcessEntry() + // is called again or this NamedProcessIterator goes out of scope. + const ProcessEntry* NextProcessEntry(); + + // Takes a snapshot of all the ProcessEntry found. + ProcessEntries Snapshot(); + + protected: + virtual bool IncludeEntry(); + const ProcessEntry& entry() { return entry_; } + + private: + // Determines whether there's another process (regardless of executable) + // left in the list of all processes. Returns true and sets entry_ to + // that process's info if there is one, false otherwise. + bool CheckForNextProcess(); + + // Initializes a PROCESSENTRY32 data structure so that it's ready for + // use with Process32First/Process32Next. + void InitProcessEntry(ProcessEntry* entry); + +#if defined(OS_WIN) + HANDLE snapshot_; + bool started_iteration_; +#elif defined(OS_MACOSX) || defined(OS_BSD) + std::vector<kinfo_proc> kinfo_procs_; + size_t index_of_kinfo_proc_; +#elif defined(OS_POSIX) + DIR* procfs_dir_; +#endif + ProcessEntry entry_; + const ProcessFilter* filter_; + + DISALLOW_COPY_AND_ASSIGN(ProcessIterator); +}; + +// This class provides a way to iterate through the list of processes +// on the current machine that were started from the given executable +// name. To use, create an instance and then call NextProcessEntry() +// until it returns false. +class BASE_EXPORT NamedProcessIterator : public ProcessIterator { + public: + NamedProcessIterator(const FilePath::StringType& executable_name, + const ProcessFilter* filter); + virtual ~NamedProcessIterator(); + + protected: + virtual bool IncludeEntry() OVERRIDE; + + private: + FilePath::StringType executable_name_; + + DISALLOW_COPY_AND_ASSIGN(NamedProcessIterator); +}; + +// Returns the number of processes on the machine that are running from the +// given executable name. If filter is non-null, then only processes selected +// by the filter will be counted. +BASE_EXPORT int GetProcessCount(const FilePath::StringType& executable_name, + const ProcessFilter* filter); + +} // namespace base + +#endif // BASE_PROCESS_PROCESS_ITERATOR_H_ diff --git a/base/process/process_iterator_freebsd.cc b/base/process/process_iterator_freebsd.cc new file mode 100644 index 0000000..408ce8d --- /dev/null +++ b/base/process/process_iterator_freebsd.cc @@ -0,0 +1,124 @@ +// Copyright (c) 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/process/process_iterator.h" + +#include <sys/sysctl.h> + +#include "base/logging.h" +#include "base/string_util.h" + +namespace base { + +ProcessIterator::ProcessIterator(const ProcessFilter* filter) + : index_of_kinfo_proc_(), + filter_(filter) { + + int mib[] = { CTL_KERN, KERN_PROC, KERN_PROC_UID, getuid() }; + + bool done = false; + int try_num = 1; + const int max_tries = 10; + + do { + size_t len = 0; + if (sysctl(mib, arraysize(mib), NULL, &len, NULL, 0) < 0) { + LOG(ERROR) << "failed to get the size needed for the process list"; + kinfo_procs_.resize(0); + done = true; + } else { + size_t num_of_kinfo_proc = len / sizeof(struct kinfo_proc); + // Leave some spare room for process table growth (more could show up + // between when we check and now) + num_of_kinfo_proc += 16; + kinfo_procs_.resize(num_of_kinfo_proc); + len = num_of_kinfo_proc * sizeof(struct kinfo_proc); + if (sysctl(mib, arraysize(mib), &kinfo_procs_[0], &len, NULL, 0) <0) { + // If we get a mem error, it just means we need a bigger buffer, so + // loop around again. Anything else is a real error and give up. + if (errno != ENOMEM) { + LOG(ERROR) << "failed to get the process list"; + kinfo_procs_.resize(0); + done = true; + } + } else { + // Got the list, just make sure we're sized exactly right + size_t num_of_kinfo_proc = len / sizeof(struct kinfo_proc); + kinfo_procs_.resize(num_of_kinfo_proc); + done = true; + } + } + } while (!done && (try_num++ < max_tries)); + + if (!done) { + LOG(ERROR) << "failed to collect the process list in a few tries"; + kinfo_procs_.resize(0); + } +} + +ProcessIterator::~ProcessIterator() { +} + +bool ProcessIterator::CheckForNextProcess() { + std::string data; + + for (; index_of_kinfo_proc_ < kinfo_procs_.size(); ++index_of_kinfo_proc_) { + size_t length; + struct kinfo_proc kinfo = kinfo_procs_[index_of_kinfo_proc_]; + int mib[] = { CTL_KERN, KERN_PROC_ARGS, kinfo.ki_pid }; + + if ((kinfo.ki_pid > 0) && (kinfo.ki_stat == SZOMB)) + continue; + + length = 0; + if (sysctl(mib, arraysize(mib), NULL, &length, NULL, 0) < 0) { + LOG(ERROR) << "failed to figure out the buffer size for a command line"; + continue; + } + + data.resize(length); + + if (sysctl(mib, arraysize(mib), &data[0], &length, NULL, 0) < 0) { + LOG(ERROR) << "failed to fetch a commandline"; + continue; + } + + std::string delimiters; + delimiters.push_back('\0'); + Tokenize(data, delimiters, &entry_.cmd_line_args_); + + size_t exec_name_end = data.find('\0'); + if (exec_name_end == std::string::npos) { + LOG(ERROR) << "command line data didn't match expected format"; + continue; + } + + entry_.pid_ = kinfo.ki_pid; + entry_.ppid_ = kinfo.ki_ppid; + entry_.gid_ = kinfo.ki_pgid; + + size_t last_slash = data.rfind('/', exec_name_end); + if (last_slash == std::string::npos) { + entry_.exe_file_.assign(data, 0, exec_name_end); + } else { + entry_.exe_file_.assign(data, last_slash + 1, + exec_name_end - last_slash - 1); + } + + // Start w/ the next entry next time through + ++index_of_kinfo_proc_; + + return true; + } + return false; +} + +bool NamedProcessIterator::IncludeEntry() { + if (executable_name_ != entry().exe_file()) + return false; + + return ProcessIterator::IncludeEntry(); +} + +} // namespace base diff --git a/base/process/process_iterator_linux.cc b/base/process/process_iterator_linux.cc new file mode 100644 index 0000000..94925d4 --- /dev/null +++ b/base/process/process_iterator_linux.cc @@ -0,0 +1,138 @@ +// Copyright (c) 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/process/process_iterator.h" + +#include "base/file_util.h" +#include "base/logging.h" +#include "base/process/internal_linux.h" +#include "base/process_util.h" +#include "base/string_util.h" +#include "base/threading/thread_restrictions.h" + +namespace base { + +namespace { + +// Reads the |field_num|th field from |proc_stats|. +// Returns an empty string on failure. +// This version only handles VM_COMM and VM_STATE, which are the only fields +// that are strings. +std::string GetProcStatsFieldAsString( + const std::vector<std::string>& proc_stats, + internal::ProcStatsFields field_num) { + if (field_num < internal::VM_COMM || field_num > internal::VM_STATE) { + NOTREACHED(); + return std::string(); + } + + if (proc_stats.size() > static_cast<size_t>(field_num)) + return proc_stats[field_num]; + + NOTREACHED(); + return 0; +} + +// Reads /proc/<pid>/cmdline and populates |proc_cmd_line_args| with the command +// line arguments. Returns true if successful. +// Note: /proc/<pid>/cmdline contains command line arguments separated by single +// null characters. We tokenize it into a vector of strings using '\0' as a +// delimiter. +bool GetProcCmdline(pid_t pid, std::vector<std::string>* proc_cmd_line_args) { + // Synchronously reading files in /proc is safe. + ThreadRestrictions::ScopedAllowIO allow_io; + + FilePath cmd_line_file = internal::GetProcPidDir(pid).Append("cmdline"); + std::string cmd_line; + if (!file_util::ReadFileToString(cmd_line_file, &cmd_line)) + return false; + std::string delimiters; + delimiters.push_back('\0'); + Tokenize(cmd_line, delimiters, proc_cmd_line_args); + return true; +} + +} // namespace + +ProcessIterator::ProcessIterator(const ProcessFilter* filter) + : filter_(filter) { + procfs_dir_ = opendir(internal::kProcDir); +} + +ProcessIterator::~ProcessIterator() { + if (procfs_dir_) { + closedir(procfs_dir_); + procfs_dir_ = NULL; + } +} + +bool ProcessIterator::CheckForNextProcess() { + // TODO(port): skip processes owned by different UID + + pid_t pid = kNullProcessId; + std::vector<std::string> cmd_line_args; + std::string stats_data; + std::vector<std::string> proc_stats; + + // Arbitrarily guess that there will never be more than 200 non-process + // files in /proc. Hardy has 53 and Lucid has 61. + int skipped = 0; + const int kSkipLimit = 200; + while (skipped < kSkipLimit) { + dirent* slot = readdir(procfs_dir_); + // all done looking through /proc? + if (!slot) + return false; + + // If not a process, keep looking for one. + pid = internal::ProcDirSlotToPid(slot->d_name); + if (!pid) { + skipped++; + continue; + } + + if (!GetProcCmdline(pid, &cmd_line_args)) + continue; + + if (!internal::ReadProcStats(pid, &stats_data)) + continue; + if (!internal::ParseProcStats(stats_data, &proc_stats)) + continue; + + std::string runstate = + GetProcStatsFieldAsString(proc_stats, internal::VM_STATE); + if (runstate.size() != 1) { + NOTREACHED(); + continue; + } + + // Is the process in 'Zombie' state, i.e. dead but waiting to be reaped? + // Allowed values: D R S T Z + if (runstate[0] != 'Z') + break; + + // Nope, it's a zombie; somebody isn't cleaning up after their children. + // (e.g. WaitForProcessesToExit doesn't clean up after dead children yet.) + // There could be a lot of zombies, can't really decrement i here. + } + if (skipped >= kSkipLimit) { + NOTREACHED(); + return false; + } + + entry_.pid_ = pid; + entry_.ppid_ = GetProcStatsFieldAsInt(proc_stats, internal::VM_PPID); + entry_.gid_ = GetProcStatsFieldAsInt(proc_stats, internal::VM_PGRP); + entry_.cmd_line_args_.assign(cmd_line_args.begin(), cmd_line_args.end()); + entry_.exe_file_ = GetProcessExecutablePath(pid).BaseName().value(); + return true; +} + +bool NamedProcessIterator::IncludeEntry() { + if (executable_name_ != entry().exe_file()) + return false; + return ProcessIterator::IncludeEntry(); +} + +} // namespace base diff --git a/base/process/process_iterator_mac.cc b/base/process/process_iterator_mac.cc new file mode 100644 index 0000000..1cc9b25 --- /dev/null +++ b/base/process/process_iterator_mac.cc @@ -0,0 +1,134 @@ +// Copyright (c) 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/process/process_iterator.h" + +#include <errno.h> +#include <sys/sysctl.h> +#include <sys/types.h> + +#include "base/logging.h" +#include "base/string_util.h" + +namespace base { + +ProcessIterator::ProcessIterator(const ProcessFilter* filter) + : index_of_kinfo_proc_(0), + filter_(filter) { + // Get a snapshot of all of my processes (yes, as we loop it can go stale, but + // but trying to find where we were in a constantly changing list is basically + // impossible. + + int mib[] = { CTL_KERN, KERN_PROC, KERN_PROC_UID, geteuid() }; + + // Since more processes could start between when we get the size and when + // we get the list, we do a loop to keep trying until we get it. + bool done = false; + int try_num = 1; + const int max_tries = 10; + do { + // Get the size of the buffer + size_t len = 0; + if (sysctl(mib, arraysize(mib), NULL, &len, NULL, 0) < 0) { + DLOG(ERROR) << "failed to get the size needed for the process list"; + kinfo_procs_.resize(0); + done = true; + } else { + size_t num_of_kinfo_proc = len / sizeof(struct kinfo_proc); + // Leave some spare room for process table growth (more could show up + // between when we check and now) + num_of_kinfo_proc += 16; + kinfo_procs_.resize(num_of_kinfo_proc); + len = num_of_kinfo_proc * sizeof(struct kinfo_proc); + // Load the list of processes + if (sysctl(mib, arraysize(mib), &kinfo_procs_[0], &len, NULL, 0) < 0) { + // If we get a mem error, it just means we need a bigger buffer, so + // loop around again. Anything else is a real error and give up. + if (errno != ENOMEM) { + DLOG(ERROR) << "failed to get the process list"; + kinfo_procs_.resize(0); + done = true; + } + } else { + // Got the list, just make sure we're sized exactly right + size_t num_of_kinfo_proc = len / sizeof(struct kinfo_proc); + kinfo_procs_.resize(num_of_kinfo_proc); + done = true; + } + } + } while (!done && (try_num++ < max_tries)); + + if (!done) { + DLOG(ERROR) << "failed to collect the process list in a few tries"; + kinfo_procs_.resize(0); + } +} + +ProcessIterator::~ProcessIterator() { +} + +bool ProcessIterator::CheckForNextProcess() { + std::string data; + for (; index_of_kinfo_proc_ < kinfo_procs_.size(); ++index_of_kinfo_proc_) { + kinfo_proc& kinfo = kinfo_procs_[index_of_kinfo_proc_]; + + // Skip processes just awaiting collection + if ((kinfo.kp_proc.p_pid > 0) && (kinfo.kp_proc.p_stat == SZOMB)) + continue; + + int mib[] = { CTL_KERN, KERN_PROCARGS, kinfo.kp_proc.p_pid }; + + // Find out what size buffer we need. + size_t data_len = 0; + if (sysctl(mib, arraysize(mib), NULL, &data_len, NULL, 0) < 0) { + DVPLOG(1) << "failed to figure out the buffer size for a commandline"; + continue; + } + + data.resize(data_len); + if (sysctl(mib, arraysize(mib), &data[0], &data_len, NULL, 0) < 0) { + DVPLOG(1) << "failed to fetch a commandline"; + continue; + } + + // |data| contains all the command line parameters of the process, separated + // by blocks of one or more null characters. We tokenize |data| into a + // vector of strings using '\0' as a delimiter and populate + // |entry_.cmd_line_args_|. + std::string delimiters; + delimiters.push_back('\0'); + Tokenize(data, delimiters, &entry_.cmd_line_args_); + + // |data| starts with the full executable path followed by a null character. + // We search for the first instance of '\0' and extract everything before it + // to populate |entry_.exe_file_|. + size_t exec_name_end = data.find('\0'); + if (exec_name_end == std::string::npos) { + DLOG(ERROR) << "command line data didn't match expected format"; + continue; + } + + entry_.pid_ = kinfo.kp_proc.p_pid; + entry_.ppid_ = kinfo.kp_eproc.e_ppid; + entry_.gid_ = kinfo.kp_eproc.e_pgid; + size_t last_slash = data.rfind('/', exec_name_end); + if (last_slash == std::string::npos) + entry_.exe_file_.assign(data, 0, exec_name_end); + else + entry_.exe_file_.assign(data, last_slash + 1, + exec_name_end - last_slash - 1); + // Start w/ the next entry next time through + ++index_of_kinfo_proc_; + // Done + return true; + } + return false; +} + +bool NamedProcessIterator::IncludeEntry() { + return (executable_name_ == entry().exe_file() && + ProcessIterator::IncludeEntry()); +} + +} // namespace base diff --git a/base/process/process_iterator_openbsd.cc b/base/process/process_iterator_openbsd.cc new file mode 100644 index 0000000..b52ddcb --- /dev/null +++ b/base/process/process_iterator_openbsd.cc @@ -0,0 +1,128 @@ +// Copyright (c) 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/process/process_iterator.h" + +#include <errno.h> +#include <sys/sysctl.h> + +#include "base/logging.h" +#include "base/string_util.h" + +namespace base { + +ProcessIterator::ProcessIterator(const ProcessFilter* filter) + : index_of_kinfo_proc_(), + filter_(filter) { + + int mib[] = { CTL_KERN, KERN_PROC, KERN_PROC_UID, getuid(), + sizeof(struct kinfo_proc), 0 }; + + bool done = false; + int try_num = 1; + const int max_tries = 10; + + do { + size_t len = 0; + if (sysctl(mib, arraysize(mib), NULL, &len, NULL, 0) < 0) { + DLOG(ERROR) << "failed to get the size needed for the process list"; + kinfo_procs_.resize(0); + done = true; + } else { + size_t num_of_kinfo_proc = len / sizeof(struct kinfo_proc); + // Leave some spare room for process table growth (more could show up + // between when we check and now) + num_of_kinfo_proc += 16; + kinfo_procs_.resize(num_of_kinfo_proc); + len = num_of_kinfo_proc * sizeof(struct kinfo_proc); + if (sysctl(mib, arraysize(mib), &kinfo_procs_[0], &len, NULL, 0) < 0) { + // If we get a mem error, it just means we need a bigger buffer, so + // loop around again. Anything else is a real error and give up. + if (errno != ENOMEM) { + DLOG(ERROR) << "failed to get the process list"; + kinfo_procs_.resize(0); + done = true; + } + } else { + // Got the list, just make sure we're sized exactly right + size_t num_of_kinfo_proc = len / sizeof(struct kinfo_proc); + kinfo_procs_.resize(num_of_kinfo_proc); + done = true; + } + } + } while (!done && (try_num++ < max_tries)); + + if (!done) { + DLOG(ERROR) << "failed to collect the process list in a few tries"; + kinfo_procs_.resize(0); + } +} + +ProcessIterator::~ProcessIterator() { +} + +bool ProcessIterator::CheckForNextProcess() { + std::string data; + for (; index_of_kinfo_proc_ < kinfo_procs_.size(); ++index_of_kinfo_proc_) { + kinfo_proc& kinfo = kinfo_procs_[index_of_kinfo_proc_]; + + // Skip processes just awaiting collection + if ((kinfo.p_pid > 0) && (kinfo.p_stat == SZOMB)) + continue; + + int mib[] = { CTL_KERN, KERN_PROC_ARGS, kinfo.p_pid }; + + // Find out what size buffer we need. + size_t data_len = 0; + if (sysctl(mib, arraysize(mib), NULL, &data_len, NULL, 0) < 0) { + DVPLOG(1) << "failed to figure out the buffer size for a commandline"; + continue; + } + + data.resize(data_len); + if (sysctl(mib, arraysize(mib), &data[0], &data_len, NULL, 0) < 0) { + DVPLOG(1) << "failed to fetch a commandline"; + continue; + } + + // |data| contains all the command line parameters of the process, separated + // by blocks of one or more null characters. We tokenize |data| into a + // vector of strings using '\0' as a delimiter and populate + // |entry_.cmd_line_args_|. + std::string delimiters; + delimiters.push_back('\0'); + Tokenize(data, delimiters, &entry_.cmd_line_args_); + + // |data| starts with the full executable path followed by a null character. + // We search for the first instance of '\0' and extract everything before it + // to populate |entry_.exe_file_|. + size_t exec_name_end = data.find('\0'); + if (exec_name_end == std::string::npos) { + DLOG(ERROR) << "command line data didn't match expected format"; + continue; + } + + entry_.pid_ = kinfo.p_pid; + entry_.ppid_ = kinfo.p_ppid; + entry_.gid_ = kinfo.p__pgid; + size_t last_slash = data.rfind('/', exec_name_end); + if (last_slash == std::string::npos) + entry_.exe_file_.assign(data, 0, exec_name_end); + else + entry_.exe_file_.assign(data, last_slash + 1, + exec_name_end - last_slash - 1); + // Start w/ the next entry next time through + ++index_of_kinfo_proc_; + // Done + return true; + } + return false; +} + +bool NamedProcessIterator::IncludeEntry() { + return (executable_name_ == entry().exe_file() && + ProcessIterator::IncludeEntry()); +} + +} // namespace base diff --git a/base/process/process_iterator_win.cc b/base/process/process_iterator_win.cc new file mode 100644 index 0000000..9d5a970 --- /dev/null +++ b/base/process/process_iterator_win.cc @@ -0,0 +1,41 @@ +// Copyright (c) 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/process/process_iterator.h" + +namespace base { + +ProcessIterator::ProcessIterator(const ProcessFilter* filter) + : started_iteration_(false), + filter_(filter) { + snapshot_ = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); +} + +ProcessIterator::~ProcessIterator() { + CloseHandle(snapshot_); +} + +bool ProcessIterator::CheckForNextProcess() { + InitProcessEntry(&entry_); + + if (!started_iteration_) { + started_iteration_ = true; + return !!Process32First(snapshot_, &entry_); + } + + return !!Process32Next(snapshot_, &entry_); +} + +void ProcessIterator::InitProcessEntry(ProcessEntry* entry) { + memset(entry, 0, sizeof(*entry)); + entry->dwSize = sizeof(*entry); +} + +bool NamedProcessIterator::IncludeEntry() { + // Case insensitive. + return _wcsicmp(executable_name_.c_str(), entry().exe_file()) == 0 && + ProcessIterator::IncludeEntry(); +} + +} // namespace base diff --git a/base/process_util.cc b/base/process_util.cc index 9979dfa..9b89c8f 100644 --- a/base/process_util.cc +++ b/base/process_util.cc @@ -6,20 +6,6 @@ namespace base { -#if defined(OS_POSIX) -ProcessEntry::ProcessEntry() : pid_(0), ppid_(0), gid_(0) {} -ProcessEntry::~ProcessEntry() {} -#endif - -int GetProcessCount(const FilePath::StringType& executable_name, - const ProcessFilter* filter) { - int count = 0; - NamedProcessIterator iter(executable_name, filter); - while (iter.NextProcessEntry()) - ++count; - return count; -} - bool KillProcesses(const FilePath::StringType& executable_name, int exit_code, const ProcessFilter* filter) { bool result = true; @@ -34,46 +20,4 @@ bool KillProcesses(const FilePath::StringType& executable_name, int exit_code, return result; } -const ProcessEntry* ProcessIterator::NextProcessEntry() { - bool result = false; - do { - result = CheckForNextProcess(); - } while (result && !IncludeEntry()); - if (result) - return &entry_; - return NULL; -} - -ProcessIterator::ProcessEntries ProcessIterator::Snapshot() { - ProcessEntries found; - while (const ProcessEntry* process_entry = NextProcessEntry()) { - found.push_back(*process_entry); - } - return found; -} - -bool ProcessIterator::IncludeEntry() { - return !filter_ || filter_->Includes(entry_); -} - -NamedProcessIterator::NamedProcessIterator( - const FilePath::StringType& executable_name, - const ProcessFilter* filter) : ProcessIterator(filter), - executable_name_(executable_name) { -#if defined(OS_ANDROID) - // On Android, the process name contains only the last 15 characters, which - // is in file /proc/<pid>/stat, the string between open parenthesis and close - // parenthesis. Please See ProcessIterator::CheckForNextProcess for details. - // Now if the length of input process name is greater than 15, only save the - // last 15 characters. - if (executable_name_.size() > 15) { - executable_name_ = FilePath::StringType(executable_name_, - executable_name_.size() - 15, 15); - } -#endif -} - -NamedProcessIterator::~NamedProcessIterator() { -} - } // namespace base diff --git a/base/process_util.h b/base/process_util.h index 2036ca8..0bec8e0 100644 --- a/base/process_util.h +++ b/base/process_util.h @@ -15,9 +15,6 @@ #include <windows.h> #include <tlhelp32.h> #elif defined(OS_MACOSX) || defined(OS_BSD) -// kinfo_proc is defined in <sys/sysctl.h>, but this forward declaration -// is sufficient for the vector<kinfo_proc> below. -struct kinfo_proc; // malloc_zone_t is defined in <malloc/malloc.h>, but this forward declaration // is sufficient for GetPurgeableZone() below. typedef struct _malloc_zone_t malloc_zone_t; @@ -39,6 +36,7 @@ typedef struct _malloc_zone_t malloc_zone_t; #include "base/base_export.h" #include "base/files/file_path.h" #include "base/process.h" +#include "base/process/process_iterator.h" #include "base/process/process_metrics.h" #if defined(OS_POSIX) @@ -49,71 +47,6 @@ class CommandLine; namespace base { -#if defined(OS_WIN) -struct ProcessEntry : public PROCESSENTRY32 { - ProcessId pid() const { return th32ProcessID; } - ProcessId parent_pid() const { return th32ParentProcessID; } - const wchar_t* exe_file() const { return szExeFile; } -}; - -// Process access masks. These constants provide platform-independent -// definitions for the standard Windows access masks. -// See http://msdn.microsoft.com/en-us/library/ms684880(VS.85).aspx for -// the specific semantics of each mask value. -const uint32 kProcessAccessTerminate = PROCESS_TERMINATE; -const uint32 kProcessAccessCreateThread = PROCESS_CREATE_THREAD; -const uint32 kProcessAccessSetSessionId = PROCESS_SET_SESSIONID; -const uint32 kProcessAccessVMOperation = PROCESS_VM_OPERATION; -const uint32 kProcessAccessVMRead = PROCESS_VM_READ; -const uint32 kProcessAccessVMWrite = PROCESS_VM_WRITE; -const uint32 kProcessAccessDuplicateHandle = PROCESS_DUP_HANDLE; -const uint32 kProcessAccessCreateProcess = PROCESS_CREATE_PROCESS; -const uint32 kProcessAccessSetQuota = PROCESS_SET_QUOTA; -const uint32 kProcessAccessSetInformation = PROCESS_SET_INFORMATION; -const uint32 kProcessAccessQueryInformation = PROCESS_QUERY_INFORMATION; -const uint32 kProcessAccessSuspendResume = PROCESS_SUSPEND_RESUME; -const uint32 kProcessAccessQueryLimitedInfomation = - PROCESS_QUERY_LIMITED_INFORMATION; -const uint32 kProcessAccessWaitForTermination = SYNCHRONIZE; -#elif defined(OS_POSIX) - -struct BASE_EXPORT ProcessEntry { - ProcessEntry(); - ~ProcessEntry(); - - ProcessId pid() const { return pid_; } - ProcessId parent_pid() const { return ppid_; } - ProcessId gid() const { return gid_; } - const char* exe_file() const { return exe_file_.c_str(); } - const std::vector<std::string>& cmd_line_args() const { - return cmd_line_args_; - } - - ProcessId pid_; - ProcessId ppid_; - ProcessId gid_; - std::string exe_file_; - std::vector<std::string> cmd_line_args_; -}; - -// Process access masks. They are not used on Posix because access checking -// does not happen during handle creation. -const uint32 kProcessAccessTerminate = 0; -const uint32 kProcessAccessCreateThread = 0; -const uint32 kProcessAccessSetSessionId = 0; -const uint32 kProcessAccessVMOperation = 0; -const uint32 kProcessAccessVMRead = 0; -const uint32 kProcessAccessVMWrite = 0; -const uint32 kProcessAccessDuplicateHandle = 0; -const uint32 kProcessAccessCreateProcess = 0; -const uint32 kProcessAccessSetQuota = 0; -const uint32 kProcessAccessSetInformation = 0; -const uint32 kProcessAccessQueryInformation = 0; -const uint32 kProcessAccessSuspendResume = 0; -const uint32 kProcessAccessQueryLimitedInfomation = 0; -const uint32 kProcessAccessWaitForTermination = 0; -#endif // defined(OS_POSIX) - // Return status values from GetTerminationStatus. Don't use these as // exit code arguments to KillProcess*(), use platform/application // specific values instead. @@ -433,23 +366,6 @@ BASE_EXPORT bool GetAppOutputWithExitCode(const CommandLine& cl, std::string* output, int* exit_code); #endif // defined(OS_POSIX) -// Used to filter processes by process ID. -class ProcessFilter { - public: - // Returns true to indicate set-inclusion and false otherwise. This method - // should not have side-effects and should be idempotent. - virtual bool Includes(const ProcessEntry& entry) const = 0; - - protected: - virtual ~ProcessFilter() {} -}; - -// Returns the number of processes on the machine that are running from the -// given executable name. If filter is non-null, then only processes selected -// by the filter will be counted. -BASE_EXPORT int GetProcessCount(const FilePath::StringType& executable_name, - const ProcessFilter* filter); - // Attempts to kill all the processes on the current machine that were launched // from the given executable name, ending them with the given exit code. If // filter is non-null, then only processes selected by the filter are killed. @@ -559,75 +475,6 @@ BASE_EXPORT void EnsureProcessTerminated(ProcessHandle process_handle); BASE_EXPORT void EnsureProcessGetsReaped(ProcessHandle process_handle); #endif -// This class provides a way to iterate through a list of processes on the -// current machine with a specified filter. -// To use, create an instance and then call NextProcessEntry() until it returns -// false. -class BASE_EXPORT ProcessIterator { - public: - typedef std::list<ProcessEntry> ProcessEntries; - - explicit ProcessIterator(const ProcessFilter* filter); - virtual ~ProcessIterator(); - - // If there's another process that matches the given executable name, - // returns a const pointer to the corresponding PROCESSENTRY32. - // If there are no more matching processes, returns NULL. - // The returned pointer will remain valid until NextProcessEntry() - // is called again or this NamedProcessIterator goes out of scope. - const ProcessEntry* NextProcessEntry(); - - // Takes a snapshot of all the ProcessEntry found. - ProcessEntries Snapshot(); - - protected: - virtual bool IncludeEntry(); - const ProcessEntry& entry() { return entry_; } - - private: - // Determines whether there's another process (regardless of executable) - // left in the list of all processes. Returns true and sets entry_ to - // that process's info if there is one, false otherwise. - bool CheckForNextProcess(); - - // Initializes a PROCESSENTRY32 data structure so that it's ready for - // use with Process32First/Process32Next. - void InitProcessEntry(ProcessEntry* entry); - -#if defined(OS_WIN) - HANDLE snapshot_; - bool started_iteration_; -#elif defined(OS_MACOSX) || defined(OS_BSD) - std::vector<kinfo_proc> kinfo_procs_; - size_t index_of_kinfo_proc_; -#elif defined(OS_POSIX) - DIR* procfs_dir_; -#endif - ProcessEntry entry_; - const ProcessFilter* filter_; - - DISALLOW_COPY_AND_ASSIGN(ProcessIterator); -}; - -// This class provides a way to iterate through the list of processes -// on the current machine that were started from the given executable -// name. To use, create an instance and then call NextProcessEntry() -// until it returns false. -class BASE_EXPORT NamedProcessIterator : public ProcessIterator { - public: - NamedProcessIterator(const FilePath::StringType& executable_name, - const ProcessFilter* filter); - virtual ~NamedProcessIterator(); - - protected: - virtual bool IncludeEntry() OVERRIDE; - - private: - FilePath::StringType executable_name_; - - DISALLOW_COPY_AND_ASSIGN(NamedProcessIterator); -}; - // Enables low fragmentation heap (LFH) for every heaps of this process. This // won't have any effect on heaps created after this function call. It will not // modify data allocated in the heaps before calling this function. So it is diff --git a/base/process_util_freebsd.cc b/base/process_util_freebsd.cc index bcf629f..4ea934a 100644 --- a/base/process_util_freebsd.cc +++ b/base/process_util_freebsd.cc @@ -52,116 +52,6 @@ FilePath GetProcessExecutablePath(ProcessHandle process) { return FilePath(std::string(pathname)); } -ProcessIterator::ProcessIterator(const ProcessFilter* filter) - : index_of_kinfo_proc_(), - filter_(filter) { - - int mib[] = { CTL_KERN, KERN_PROC, KERN_PROC_UID, getuid() }; - - bool done = false; - int try_num = 1; - const int max_tries = 10; - - do { - size_t len = 0; - if (sysctl(mib, arraysize(mib), NULL, &len, NULL, 0) < 0) { - LOG(ERROR) << "failed to get the size needed for the process list"; - kinfo_procs_.resize(0); - done = true; - } else { - size_t num_of_kinfo_proc = len / sizeof(struct kinfo_proc); - // Leave some spare room for process table growth (more could show up - // between when we check and now) - num_of_kinfo_proc += 16; - kinfo_procs_.resize(num_of_kinfo_proc); - len = num_of_kinfo_proc * sizeof(struct kinfo_proc); - if (sysctl(mib, arraysize(mib), &kinfo_procs_[0], &len, NULL, 0) <0) { - // If we get a mem error, it just means we need a bigger buffer, so - // loop around again. Anything else is a real error and give up. - if (errno != ENOMEM) { - LOG(ERROR) << "failed to get the process list"; - kinfo_procs_.resize(0); - done = true; - } - } else { - // Got the list, just make sure we're sized exactly right - size_t num_of_kinfo_proc = len / sizeof(struct kinfo_proc); - kinfo_procs_.resize(num_of_kinfo_proc); - done = true; - } - } - } while (!done && (try_num++ < max_tries)); - - if (!done) { - LOG(ERROR) << "failed to collect the process list in a few tries"; - kinfo_procs_.resize(0); - } -} - -ProcessIterator::~ProcessIterator() { -} - -bool ProcessIterator::CheckForNextProcess() { - std::string data; - - for (; index_of_kinfo_proc_ < kinfo_procs_.size(); ++index_of_kinfo_proc_) { - size_t length; - struct kinfo_proc kinfo = kinfo_procs_[index_of_kinfo_proc_]; - int mib[] = { CTL_KERN, KERN_PROC_ARGS, kinfo.ki_pid }; - - if ((kinfo.ki_pid > 0) && (kinfo.ki_stat == SZOMB)) - continue; - - length = 0; - if (sysctl(mib, arraysize(mib), NULL, &length, NULL, 0) < 0) { - LOG(ERROR) << "failed to figure out the buffer size for a command line"; - continue; - } - - data.resize(length); - - if (sysctl(mib, arraysize(mib), &data[0], &length, NULL, 0) < 0) { - LOG(ERROR) << "failed to fetch a commandline"; - continue; - } - - std::string delimiters; - delimiters.push_back('\0'); - Tokenize(data, delimiters, &entry_.cmd_line_args_); - - size_t exec_name_end = data.find('\0'); - if (exec_name_end == std::string::npos) { - LOG(ERROR) << "command line data didn't match expected format"; - continue; - } - - entry_.pid_ = kinfo.ki_pid; - entry_.ppid_ = kinfo.ki_ppid; - entry_.gid_ = kinfo.ki_pgid; - - size_t last_slash = data.rfind('/', exec_name_end); - if (last_slash == std::string::npos) { - entry_.exe_file_.assign(data, 0, exec_name_end); - } else { - entry_.exe_file_.assign(data, last_slash + 1, - exec_name_end - last_slash - 1); - } - - // Start w/ the next entry next time through - ++index_of_kinfo_proc_; - - return true; - } - return false; -} - -bool NamedProcessIterator::IncludeEntry() { - if (executable_name_ != entry().exe_file()) - return false; - - return ProcessIterator::IncludeEntry(); -} - void EnableTerminationOnOutOfMemory() { DLOG(WARNING) << "Not feasible."; } diff --git a/base/process_util_linux.cc b/base/process_util_linux.cc index 4f510d7..32f191e 100644 --- a/base/process_util_linux.cc +++ b/base/process_util_linux.cc @@ -21,48 +21,6 @@ namespace base { -namespace { - -// Reads the |field_num|th field from |proc_stats|. -// Returns an empty string on failure. -// This version only handles VM_COMM and VM_STATE, which are the only fields -// that are strings. -std::string GetProcStatsFieldAsString( - const std::vector<std::string>& proc_stats, - internal::ProcStatsFields field_num) { - if (field_num < internal::VM_COMM || field_num > internal::VM_STATE) { - NOTREACHED(); - return std::string(); - } - - if (proc_stats.size() > static_cast<size_t>(field_num)) - return proc_stats[field_num]; - - NOTREACHED(); - return 0; -} - -// Reads /proc/<pid>/cmdline and populates |proc_cmd_line_args| with the command -// line arguments. Returns true if successful. -// Note: /proc/<pid>/cmdline contains command line arguments separated by single -// null characters. We tokenize it into a vector of strings using '\0' as a -// delimiter. -bool GetProcCmdline(pid_t pid, std::vector<std::string>* proc_cmd_line_args) { - // Synchronously reading files in /proc is safe. - ThreadRestrictions::ScopedAllowIO allow_io; - - FilePath cmd_line_file = internal::GetProcPidDir(pid).Append("cmdline"); - std::string cmd_line; - if (!file_util::ReadFileToString(cmd_line_file, &cmd_line)) - return false; - std::string delimiters; - delimiters.push_back('\0'); - Tokenize(cmd_line, delimiters, proc_cmd_line_args); - return true; -} - -} // namespace - #if defined(USE_LINUX_BREAKPAD) size_t g_oom_size = 0U; #endif @@ -87,87 +45,6 @@ FilePath GetProcessExecutablePath(ProcessHandle process) { return exe_name; } -ProcessIterator::ProcessIterator(const ProcessFilter* filter) - : filter_(filter) { - procfs_dir_ = opendir(internal::kProcDir); -} - -ProcessIterator::~ProcessIterator() { - if (procfs_dir_) { - closedir(procfs_dir_); - procfs_dir_ = NULL; - } -} - -bool ProcessIterator::CheckForNextProcess() { - // TODO(port): skip processes owned by different UID - - pid_t pid = kNullProcessId; - std::vector<std::string> cmd_line_args; - std::string stats_data; - std::vector<std::string> proc_stats; - - // Arbitrarily guess that there will never be more than 200 non-process - // files in /proc. Hardy has 53 and Lucid has 61. - int skipped = 0; - const int kSkipLimit = 200; - while (skipped < kSkipLimit) { - dirent* slot = readdir(procfs_dir_); - // all done looking through /proc? - if (!slot) - return false; - - // If not a process, keep looking for one. - pid = internal::ProcDirSlotToPid(slot->d_name); - if (!pid) { - skipped++; - continue; - } - - if (!GetProcCmdline(pid, &cmd_line_args)) - continue; - - if (!internal::ReadProcStats(pid, &stats_data)) - continue; - if (!internal::ParseProcStats(stats_data, &proc_stats)) - continue; - - std::string runstate = - GetProcStatsFieldAsString(proc_stats, internal::VM_STATE); - if (runstate.size() != 1) { - NOTREACHED(); - continue; - } - - // Is the process in 'Zombie' state, i.e. dead but waiting to be reaped? - // Allowed values: D R S T Z - if (runstate[0] != 'Z') - break; - - // Nope, it's a zombie; somebody isn't cleaning up after their children. - // (e.g. WaitForProcessesToExit doesn't clean up after dead children yet.) - // There could be a lot of zombies, can't really decrement i here. - } - if (skipped >= kSkipLimit) { - NOTREACHED(); - return false; - } - - entry_.pid_ = pid; - entry_.ppid_ = GetProcStatsFieldAsInt(proc_stats, internal::VM_PPID); - entry_.gid_ = GetProcStatsFieldAsInt(proc_stats, internal::VM_PGRP); - entry_.cmd_line_args_.assign(cmd_line_args.begin(), cmd_line_args.end()); - entry_.exe_file_ = GetProcessExecutablePath(pid).BaseName().value(); - return true; -} - -bool NamedProcessIterator::IncludeEntry() { - if (executable_name_ != entry().exe_file()) - return false; - return ProcessIterator::IncludeEntry(); -} - - int GetNumberOfThreads(ProcessHandle process) { return internal::ReadProcStatsAndGetFieldAsInt(process, internal::VM_NUMTHREADS); diff --git a/base/process_util_mac.mm b/base/process_util_mac.mm index c9f0ef7..83a24b8 100644 --- a/base/process_util_mac.mm +++ b/base/process_util_mac.mm @@ -65,123 +65,6 @@ void RestoreDefaultExceptionHandler() { EXCEPTION_DEFAULT, THREAD_STATE_NONE); } -ProcessIterator::ProcessIterator(const ProcessFilter* filter) - : index_of_kinfo_proc_(0), - filter_(filter) { - // Get a snapshot of all of my processes (yes, as we loop it can go stale, but - // but trying to find where we were in a constantly changing list is basically - // impossible. - - int mib[] = { CTL_KERN, KERN_PROC, KERN_PROC_UID, geteuid() }; - - // Since more processes could start between when we get the size and when - // we get the list, we do a loop to keep trying until we get it. - bool done = false; - int try_num = 1; - const int max_tries = 10; - do { - // Get the size of the buffer - size_t len = 0; - if (sysctl(mib, arraysize(mib), NULL, &len, NULL, 0) < 0) { - DLOG(ERROR) << "failed to get the size needed for the process list"; - kinfo_procs_.resize(0); - done = true; - } else { - size_t num_of_kinfo_proc = len / sizeof(struct kinfo_proc); - // Leave some spare room for process table growth (more could show up - // between when we check and now) - num_of_kinfo_proc += 16; - kinfo_procs_.resize(num_of_kinfo_proc); - len = num_of_kinfo_proc * sizeof(struct kinfo_proc); - // Load the list of processes - if (sysctl(mib, arraysize(mib), &kinfo_procs_[0], &len, NULL, 0) < 0) { - // If we get a mem error, it just means we need a bigger buffer, so - // loop around again. Anything else is a real error and give up. - if (errno != ENOMEM) { - DLOG(ERROR) << "failed to get the process list"; - kinfo_procs_.resize(0); - done = true; - } - } else { - // Got the list, just make sure we're sized exactly right - size_t num_of_kinfo_proc = len / sizeof(struct kinfo_proc); - kinfo_procs_.resize(num_of_kinfo_proc); - done = true; - } - } - } while (!done && (try_num++ < max_tries)); - - if (!done) { - DLOG(ERROR) << "failed to collect the process list in a few tries"; - kinfo_procs_.resize(0); - } -} - -ProcessIterator::~ProcessIterator() { -} - -bool ProcessIterator::CheckForNextProcess() { - std::string data; - for (; index_of_kinfo_proc_ < kinfo_procs_.size(); ++index_of_kinfo_proc_) { - kinfo_proc& kinfo = kinfo_procs_[index_of_kinfo_proc_]; - - // Skip processes just awaiting collection - if ((kinfo.kp_proc.p_pid > 0) && (kinfo.kp_proc.p_stat == SZOMB)) - continue; - - int mib[] = { CTL_KERN, KERN_PROCARGS, kinfo.kp_proc.p_pid }; - - // Find out what size buffer we need. - size_t data_len = 0; - if (sysctl(mib, arraysize(mib), NULL, &data_len, NULL, 0) < 0) { - DVPLOG(1) << "failed to figure out the buffer size for a commandline"; - continue; - } - - data.resize(data_len); - if (sysctl(mib, arraysize(mib), &data[0], &data_len, NULL, 0) < 0) { - DVPLOG(1) << "failed to fetch a commandline"; - continue; - } - - // |data| contains all the command line parameters of the process, separated - // by blocks of one or more null characters. We tokenize |data| into a - // vector of strings using '\0' as a delimiter and populate - // |entry_.cmd_line_args_|. - std::string delimiters; - delimiters.push_back('\0'); - Tokenize(data, delimiters, &entry_.cmd_line_args_); - - // |data| starts with the full executable path followed by a null character. - // We search for the first instance of '\0' and extract everything before it - // to populate |entry_.exe_file_|. - size_t exec_name_end = data.find('\0'); - if (exec_name_end == std::string::npos) { - DLOG(ERROR) << "command line data didn't match expected format"; - continue; - } - - entry_.pid_ = kinfo.kp_proc.p_pid; - entry_.ppid_ = kinfo.kp_eproc.e_ppid; - entry_.gid_ = kinfo.kp_eproc.e_pgid; - size_t last_slash = data.rfind('/', exec_name_end); - if (last_slash == std::string::npos) - entry_.exe_file_.assign(data, 0, exec_name_end); - else - entry_.exe_file_.assign(data, last_slash + 1, - exec_name_end - last_slash - 1); - // Start w/ the next entry next time through - ++index_of_kinfo_proc_; - // Done - return true; - } - return false; -} - -bool NamedProcessIterator::IncludeEntry() { - return (executable_name_ == entry().exe_file() && - ProcessIterator::IncludeEntry()); -} // These are helpers for EnableTerminationOnHeapCorruption, which is a no-op diff --git a/base/process_util_openbsd.cc b/base/process_util_openbsd.cc index de16909..5179786 100644 --- a/base/process_util_openbsd.cc +++ b/base/process_util_openbsd.cc @@ -64,120 +64,6 @@ FilePath GetProcessExecutablePath(ProcessHandle process) { return FilePath(); } -ProcessIterator::ProcessIterator(const ProcessFilter* filter) - : index_of_kinfo_proc_(), - filter_(filter) { - - int mib[] = { CTL_KERN, KERN_PROC, KERN_PROC_UID, getuid(), - sizeof(struct kinfo_proc), 0 }; - - bool done = false; - int try_num = 1; - const int max_tries = 10; - - do { - size_t len = 0; - if (sysctl(mib, arraysize(mib), NULL, &len, NULL, 0) < 0) { - DLOG(ERROR) << "failed to get the size needed for the process list"; - kinfo_procs_.resize(0); - done = true; - } else { - size_t num_of_kinfo_proc = len / sizeof(struct kinfo_proc); - // Leave some spare room for process table growth (more could show up - // between when we check and now) - num_of_kinfo_proc += 16; - kinfo_procs_.resize(num_of_kinfo_proc); - len = num_of_kinfo_proc * sizeof(struct kinfo_proc); - if (sysctl(mib, arraysize(mib), &kinfo_procs_[0], &len, NULL, 0) < 0) { - // If we get a mem error, it just means we need a bigger buffer, so - // loop around again. Anything else is a real error and give up. - if (errno != ENOMEM) { - DLOG(ERROR) << "failed to get the process list"; - kinfo_procs_.resize(0); - done = true; - } - } else { - // Got the list, just make sure we're sized exactly right - size_t num_of_kinfo_proc = len / sizeof(struct kinfo_proc); - kinfo_procs_.resize(num_of_kinfo_proc); - done = true; - } - } - } while (!done && (try_num++ < max_tries)); - - if (!done) { - DLOG(ERROR) << "failed to collect the process list in a few tries"; - kinfo_procs_.resize(0); - } -} - -ProcessIterator::~ProcessIterator() { -} - -bool ProcessIterator::CheckForNextProcess() { - std::string data; - for (; index_of_kinfo_proc_ < kinfo_procs_.size(); ++index_of_kinfo_proc_) { - kinfo_proc& kinfo = kinfo_procs_[index_of_kinfo_proc_]; - - // Skip processes just awaiting collection - if ((kinfo.p_pid > 0) && (kinfo.p_stat == SZOMB)) - continue; - - int mib[] = { CTL_KERN, KERN_PROC_ARGS, kinfo.p_pid }; - - // Find out what size buffer we need. - size_t data_len = 0; - if (sysctl(mib, arraysize(mib), NULL, &data_len, NULL, 0) < 0) { - DVPLOG(1) << "failed to figure out the buffer size for a commandline"; - continue; - } - - data.resize(data_len); - if (sysctl(mib, arraysize(mib), &data[0], &data_len, NULL, 0) < 0) { - DVPLOG(1) << "failed to fetch a commandline"; - continue; - } - - // |data| contains all the command line parameters of the process, separated - // by blocks of one or more null characters. We tokenize |data| into a - // vector of strings using '\0' as a delimiter and populate - // |entry_.cmd_line_args_|. - std::string delimiters; - delimiters.push_back('\0'); - Tokenize(data, delimiters, &entry_.cmd_line_args_); - - // |data| starts with the full executable path followed by a null character. - // We search for the first instance of '\0' and extract everything before it - // to populate |entry_.exe_file_|. - size_t exec_name_end = data.find('\0'); - if (exec_name_end == std::string::npos) { - DLOG(ERROR) << "command line data didn't match expected format"; - continue; - } - - entry_.pid_ = kinfo.p_pid; - entry_.ppid_ = kinfo.p_ppid; - entry_.gid_ = kinfo.p__pgid; - size_t last_slash = data.rfind('/', exec_name_end); - if (last_slash == std::string::npos) - entry_.exe_file_.assign(data, 0, exec_name_end); - else - entry_.exe_file_.assign(data, last_slash + 1, - exec_name_end - last_slash - 1); - // Start w/ the next entry next time through - ++index_of_kinfo_proc_; - // Done - return true; - } - return false; -} - -bool NamedProcessIterator::IncludeEntry() { - return (executable_name_ == entry().exe_file() && - ProcessIterator::IncludeEntry()); -} - - void EnableTerminationOnOutOfMemory() { } diff --git a/base/process_util_win.cc b/base/process_util_win.cc index 1a30d0e..7119004 100644 --- a/base/process_util_win.cc +++ b/base/process_util_win.cc @@ -589,38 +589,6 @@ bool WaitForExitCodeWithTimeout(ProcessHandle handle, int* exit_code, return true; } -ProcessIterator::ProcessIterator(const ProcessFilter* filter) - : started_iteration_(false), - filter_(filter) { - snapshot_ = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); -} - -ProcessIterator::~ProcessIterator() { - CloseHandle(snapshot_); -} - -bool ProcessIterator::CheckForNextProcess() { - InitProcessEntry(&entry_); - - if (!started_iteration_) { - started_iteration_ = true; - return !!Process32First(snapshot_, &entry_); - } - - return !!Process32Next(snapshot_, &entry_); -} - -void ProcessIterator::InitProcessEntry(ProcessEntry* entry) { - memset(entry, 0, sizeof(*entry)); - entry->dwSize = sizeof(*entry); -} - -bool NamedProcessIterator::IncludeEntry() { - // Case insensitive. - return _wcsicmp(executable_name_.c_str(), entry().exe_file()) == 0 && - ProcessIterator::IncludeEntry(); -} - bool WaitForProcessesToExit(const FilePath::StringType& executable_name, base::TimeDelta wait, const ProcessFilter* filter) { |