diff options
author | mark@chromium.org <mark@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-02-05 21:44:13 +0000 |
---|---|---|
committer | mark@chromium.org <mark@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-02-05 21:44:13 +0000 |
commit | 962dd31f9cff7017d90da505e077ea84db9c5e48 (patch) | |
tree | 821a70988ea4e963be58314c5b9b7becab7312fe | |
parent | 3115504c55a338fb3de3684a85b2383b4c4dbbc0 (diff) | |
download | chromium_src-962dd31f9cff7017d90da505e077ea84db9c5e48.zip chromium_src-962dd31f9cff7017d90da505e077ea84db9c5e48.tar.gz chromium_src-962dd31f9cff7017d90da505e077ea84db9c5e48.tar.bz2 |
Implement NamedProcessIterator in base/process_util_mac.mm. Patch by Naoki
Takano <takano.naoki@gmail.com>
Review URL: http://codereview.chromium.org/18192
Review URL: http://codereview.chromium.org/21097
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@9263 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r-- | AUTHORS | 1 | ||||
-rw-r--r-- | base/process_util.h | 12 | ||||
-rw-r--r-- | base/process_util_linux.cc | 57 | ||||
-rw-r--r-- | base/process_util_mac.mm | 118 | ||||
-rw-r--r-- | base/process_util_posix.cc | 57 | ||||
-rw-r--r-- | base/process_util_unittest.cc | 6 |
6 files changed, 175 insertions, 76 deletions
@@ -20,6 +20,7 @@ Comodo CA Limited Torchmobile Inc. Craig Schlenter <craig.schlenter@gmail.com> Ibrar Ahmed <ibrar.ahmad@gmail.com> +Naoki Takano <takano.naoki@gmail.com> Fabien Tassin <fta@sofaraway.org> Kunal Thakar <kunalt@gmail.com> Mohamed Mansour <m0.interactive@gmail.com> diff --git a/base/process_util.h b/base/process_util.h index ec858bd..8c4c5d4 100644 --- a/base/process_util.h +++ b/base/process_util.h @@ -20,6 +20,7 @@ #endif #include <string> +#include <vector> #include "base/command_line.h" #include "base/process.h" @@ -28,10 +29,11 @@ typedef PROCESSENTRY32 ProcessEntry; typedef IO_COUNTERS IoCounters; #elif defined(OS_POSIX) +// TODO(port): we should not rely on a Win32 structure. struct ProcessEntry { int pid; int ppid; - char szExeFile[NAME_MAX+1]; + char szExeFile[NAME_MAX + 1]; }; struct IoCounters { @@ -44,6 +46,10 @@ struct IoCounters { }; #endif +#if defined(OS_MACOSX) +struct kinfo_proc; +#endif + namespace base { // A minimalistic but hopefully cross-platform set of exit codes. @@ -215,9 +221,9 @@ class NamedProcessIterator { #elif defined(OS_LINUX) DIR *procfs_dir_; #elif defined(OS_MACOSX) - // probably kvm_t *kvmd_; + std::vector<kinfo_proc> kinfo_procs_; + size_t index_of_kinfo_proc_; #endif - ProcessEntry entry_; const ProcessFilter* filter_; diff --git a/base/process_util_linux.cc b/base/process_util_linux.cc index 8f83212..2253ff2 100644 --- a/base/process_util_linux.cc +++ b/base/process_util_linux.cc @@ -14,7 +14,6 @@ #include "base/file_util.h" #include "base/logging.h" -#include "base/platform_thread.h" #include "base/string_tokenizer.h" #include "base/string_util.h" #include "base/time.h" @@ -202,62 +201,6 @@ bool NamedProcessIterator::IncludeEntry() { return filter_->Includes(entry_.pid, entry_.ppid); } -int GetProcessCount(const std::wstring& executable_name, - const ProcessFilter* filter) { - int count = 0; - - NamedProcessIterator iter(executable_name, filter); - while (iter.NextProcessEntry()) - ++count; - return count; -} - -bool KillProcesses(const std::wstring& executable_name, int exit_code, - const ProcessFilter* filter) { - bool result = true; - const ProcessEntry* entry; - - NamedProcessIterator iter(executable_name, filter); - while ((entry = iter.NextProcessEntry()) != NULL) - result = KillProcess((*entry).pid, exit_code, true) && result; - - return result; -} - -bool WaitForProcessesToExit(const std::wstring& executable_name, - int wait_milliseconds, - const ProcessFilter* filter) { - bool result = false; - - // TODO(port): This is inefficient, but works if there are multiple procs. - // TODO(port): use waitpid to avoid leaving zombies around - - base::Time end_time = base::Time::Now() + - base::TimeDelta::FromMilliseconds(wait_milliseconds); - do { - NamedProcessIterator iter(executable_name, filter); - if (!iter.NextProcessEntry()) { - result = true; - break; - } - PlatformThread::Sleep(100); - } while ((base::Time::Now() - end_time) > base::TimeDelta()); - - return result; -} - -bool CleanupProcesses(const std::wstring& executable_name, - int wait_milliseconds, - int exit_code, - const ProcessFilter* filter) { - bool exited_cleanly = - WaitForProcessesToExit(executable_name, wait_milliseconds, - filter); - if (!exited_cleanly) - KillProcesses(executable_name, exit_code, filter); - return exited_cleanly; -} - // To have /proc/self/io file you must enable CONFIG_TASK_IO_ACCOUNTING // in your kernel configuration. bool ProcessMetrics::GetIOCounters(IoCounters* io_counters) { diff --git a/base/process_util_mac.mm b/base/process_util_mac.mm index 6dd865b..501205c 100644 --- a/base/process_util_mac.mm +++ b/base/process_util_mac.mm @@ -3,16 +3,21 @@ // found in the LICENSE file. -#include "base/logging.h" #include "base/process_util.h" #import <Cocoa/Cocoa.h> #include <crt_externs.h> #include <spawn.h> -#include <string> +#include <sys/sysctl.h> #include <sys/types.h> #include <sys/wait.h> +#include <string> + +#include "base/logging.h" +#include "base/string_util.h" +#include "base/time.h" + namespace base { bool LaunchApp(const std::vector<std::string>& argv, @@ -73,7 +78,7 @@ bool LaunchApp(const std::vector<std::string>& argv, if (wait) waitpid(pid, 0, 0); - if(process_handle) + if (process_handle) *process_handle = pid; } @@ -87,21 +92,106 @@ bool LaunchApp(const CommandLine& cl, return LaunchApp(cl.argv(), no_files, wait, process_handle); } -bool ProcessMetrics::GetIOCounters(IoCounters* io_counters) { - // TODO(pinkerton): can we implement this? On linux it relies on /proc. - return false; +NamedProcessIterator::NamedProcessIterator(const std::wstring& executable_name, + const ProcessFilter* filter) + : executable_name_(executable_name), + index_of_kinfo_proc_(0), + filter_(filter) { } -int GetProcessCount(const std::wstring& executable_name, - const ProcessFilter* filter) { - NOTIMPLEMENTED(); - return 0; +NamedProcessIterator::~NamedProcessIterator() { } -bool CleanupProcesses(const std::wstring& executable_name, - int wait_milliseconds, - int exit_code, - const ProcessFilter* filter) { +const ProcessEntry* NamedProcessIterator::NextProcessEntry() { + // Every call, you have to get new kinfo_procs_. + // Because the process status might be changed. + int num_of_kinfo_proc = 0; + index_of_kinfo_proc_ = 0; + int mib[] = { CTL_KERN, KERN_PROC, KERN_PROC_ALL }; + size_t len = 0; + + if (sysctl(mib, arraysize(mib), NULL, &len, NULL, 0) < 0) + return NULL; + + num_of_kinfo_proc = len / sizeof(struct kinfo_proc); + // Leave some spare room for process table growth. + 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) + return NULL; + + num_of_kinfo_proc = len / sizeof(struct kinfo_proc); + kinfo_procs_.resize(num_of_kinfo_proc); + + bool result = false; + do { + result = CheckForNextProcess(); + } while (result && !IncludeEntry()); + + if (result) + return &entry_; + + return NULL; +} + +bool NamedProcessIterator::CheckForNextProcess() { + std::string exec_name; + kinfo_proc* kinfo = NULL; + for (; index_of_kinfo_proc_ < kinfo_procs_.size(); ++index_of_kinfo_proc_) { + if (kinfo_procs_[index_of_kinfo_proc_].kp_proc.p_stat != SZOMB) { + kinfo = &kinfo_procs_[index_of_kinfo_proc_]; + + int mib[] = { KERN_PROCARGS, KERN_PROCARGS, kinfo->kp_proc.p_pid }; + + size_t data_len = 0; + if (sysctl(mib, arraysize(mib), NULL, &data_len, NULL, 0) < 0) + continue; + + std::string data; + data.resize(data_len); + if (sysctl(mib, arraysize(mib), &data[0], &data_len, NULL, 0) < 0) + continue; + + // "data" has absolute process path with '/', + // so we get the last part as execution process name. + + int exec_name_end = data.find('\0'); + int last_slash = data.rfind('/', exec_name_end); + + // If the index is not -1, it means valid exec name is found. + // Get the exec name and store the name into exec_name and break. + // "last_slash" point is '/', so get substr from the next. + if (last_slash != -1) { + exec_name = data.substr(exec_name_end - last_slash - 1); + } else { + exec_name = data.substr(0, exec_name_end); + } + break; + } + } + + if (index_of_kinfo_proc_ >= kinfo_procs_.size()) + return false; + + entry_.pid = kinfo->kp_proc.p_pid; + entry_.ppid = kinfo->kp_proc.p_oppid; + + base::strlcpy(entry_.szExeFile, exec_name.c_str(), sizeof(entry_.szExeFile)); + + return true; +} + +bool NamedProcessIterator::IncludeEntry() { + if (WideToUTF8(executable_name_) != entry_.szExeFile) + return false; + if (!filter_) + return true; + return filter_->Includes(entry_.pid, entry_.ppid); +} + +bool ProcessMetrics::GetIOCounters(IoCounters* io_counters) { + // TODO(pinkerton): can we implement this? On linux it relies on /proc. NOTIMPLEMENTED(); return false; } diff --git a/base/process_util_posix.cc b/base/process_util_posix.cc index d8fefca..680cacc 100644 --- a/base/process_util_posix.cc +++ b/base/process_util_posix.cc @@ -17,6 +17,7 @@ #include "base/basictypes.h" #include "base/logging.h" +#include "base/platform_thread.h" #include "base/process_util.h" #include "base/sys_info.h" #include "base/time.h" @@ -270,4 +271,60 @@ int ProcessMetrics::GetCPUUsage() { return cpu; } +int GetProcessCount(const std::wstring& executable_name, + const ProcessFilter* filter) { + int count = 0; + + NamedProcessIterator iter(executable_name, filter); + while (iter.NextProcessEntry()) + ++count; + return count; +} + +bool KillProcesses(const std::wstring& executable_name, int exit_code, + const ProcessFilter* filter) { + bool result = true; + const ProcessEntry* entry; + + NamedProcessIterator iter(executable_name, filter); + while ((entry = iter.NextProcessEntry()) != NULL) + result = KillProcess((*entry).pid, exit_code, true) && result; + + return result; +} + +bool WaitForProcessesToExit(const std::wstring& executable_name, + int wait_milliseconds, + const ProcessFilter* filter) { + bool result = false; + + // TODO(port): This is inefficient, but works if there are multiple procs. + // TODO(port): use waitpid to avoid leaving zombies around + + base::Time end_time = base::Time::Now() + + base::TimeDelta::FromMilliseconds(wait_milliseconds); + do { + NamedProcessIterator iter(executable_name, filter); + if (!iter.NextProcessEntry()) { + result = true; + break; + } + PlatformThread::Sleep(100); + } while ((base::Time::Now() - end_time) > base::TimeDelta()); + + return result; +} + +bool CleanupProcesses(const std::wstring& executable_name, + int wait_milliseconds, + int exit_code, + const ProcessFilter* filter) { + bool exited_cleanly = + WaitForProcessesToExit(executable_name, wait_milliseconds, + filter); + if (!exited_cleanly) + KillProcesses(executable_name, exit_code, filter); + return exited_cleanly; +} + } // namespace base diff --git a/base/process_util_unittest.cc b/base/process_util_unittest.cc index 0b3bc21..1c99733 100644 --- a/base/process_util_unittest.cc +++ b/base/process_util_unittest.cc @@ -55,9 +55,12 @@ MULTIPROCESS_TEST_MAIN(SlowChildProcess) { #define EXE_SUFFIX L"" #endif +#if defined(OS_MACOSX) // TODO(port): finish port on Mac -#if !defined(OS_MACOSX) +TEST_F(ProcessUtilTest, DISABLED_KillSlowChild) { +#else TEST_F(ProcessUtilTest, KillSlowChild) { +#endif remove("SlowChildProcess.die"); int oldcount = GetProcessCount(L"base_unittests" EXE_SUFFIX, 0); @@ -70,7 +73,6 @@ TEST_F(ProcessUtilTest, KillSlowChild) { EXPECT_TRUE(base::WaitForSingleProcess(handle, 5000)); EXPECT_EQ(oldcount, GetProcessCount(L"base_unittests" EXE_SUFFIX, 0)); } -#endif // TODO(estade): if possible, port these 2 tests. #if defined(OS_WIN) |