| OLD | NEW |
| 1 // Copyright (c) 2008 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2008 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 | 5 |
| 6 #include "base/process_util.h" | 6 #include "base/process_util.h" |
| 7 | 7 |
| 8 #import <Cocoa/Cocoa.h> | 8 #import <Cocoa/Cocoa.h> |
| 9 #include <crt_externs.h> | 9 #include <crt_externs.h> |
| 10 #include <mach/mach.h> | 10 #include <mach/mach.h> |
| (...skipping 30 matching lines...) Expand all Loading... |
| 41 EXC_MASK_BREAKPOINT; | 41 EXC_MASK_BREAKPOINT; |
| 42 | 42 |
| 43 // Setting the exception port to MACH_PORT_NULL may not be entirely | 43 // Setting the exception port to MACH_PORT_NULL may not be entirely |
| 44 // kosher to restore the default exception handler, but in practice, | 44 // kosher to restore the default exception handler, but in practice, |
| 45 // it results in the exception port being set to Apple Crash Reporter, | 45 // it results in the exception port being set to Apple Crash Reporter, |
| 46 // the desired behavior. | 46 // the desired behavior. |
| 47 task_set_exception_ports(mach_task_self(), exception_mask, MACH_PORT_NULL, | 47 task_set_exception_ports(mach_task_self(), exception_mask, MACH_PORT_NULL, |
| 48 EXCEPTION_DEFAULT, THREAD_STATE_NONE); | 48 EXCEPTION_DEFAULT, THREAD_STATE_NONE); |
| 49 } | 49 } |
| 50 | 50 |
| 51 NamedProcessIterator::NamedProcessIterator(const std::wstring& executable_name, | 51 ProcessIterator::ProcessIterator(const ProcessFilter* filter) |
| 52 const ProcessFilter* filter) | 52 : index_of_kinfo_proc_(0), |
| 53 : executable_name_(executable_name), | |
| 54 index_of_kinfo_proc_(0), | |
| 55 filter_(filter) { | 53 filter_(filter) { |
| 56 // Get a snapshot of all of my processes (yes, as we loop it can go stale, but | 54 // Get a snapshot of all of my processes (yes, as we loop it can go stale, but |
| 57 // but trying to find where we were in a constantly changing list is basically | 55 // but trying to find where we were in a constantly changing list is basically |
| 58 // impossible. | 56 // impossible. |
| 59 | 57 |
| 60 int mib[] = { CTL_KERN, KERN_PROC, KERN_PROC_UID, geteuid() }; | 58 int mib[] = { CTL_KERN, KERN_PROC, KERN_PROC_UID, geteuid() }; |
| 61 | 59 |
| 62 // Since more processes could start between when we get the size and when | 60 // Since more processes could start between when we get the size and when |
| 63 // we get the list, we do a loop to keep trying until we get it. | 61 // we get the list, we do a loop to keep trying until we get it. |
| 64 bool done = false; | 62 bool done = false; |
| 65 int try_num = 1; | 63 int try_num = 1; |
| 66 const int max_tries = 10; | 64 const int max_tries = 10; |
| 67 do { | 65 do { |
| 68 // Get the size of the buffer | 66 // Get the size of the buffer |
| 69 size_t len = 0; | 67 size_t len = 0; |
| 70 if (sysctl(mib, arraysize(mib), NULL, &len, NULL, 0) < 0) { | 68 if (sysctl(mib, arraysize(mib), NULL, &len, NULL, 0) < 0) { |
| 71 LOG(ERROR) << "failed to get the size needed for the process list"; | 69 LOG(ERROR) << "failed to get the size needed for the process list"; |
| 72 kinfo_procs_.resize(0); | 70 kinfo_procs_.resize(0); |
| 73 done = true; | 71 done = true; |
| 74 } else { | 72 } else { |
| 75 size_t num_of_kinfo_proc = len / sizeof(struct kinfo_proc); | 73 size_t num_of_kinfo_proc = len / sizeof(struct kinfo_proc); |
| 76 // Leave some spare room for process table growth (more could show up | 74 // Leave some spare room for process table growth (more could show up |
| 77 // between when we check and now) | 75 // between when we check and now) |
| 78 num_of_kinfo_proc += 4; | 76 num_of_kinfo_proc += 16; |
| 79 kinfo_procs_.resize(num_of_kinfo_proc); | 77 kinfo_procs_.resize(num_of_kinfo_proc); |
| 80 len = num_of_kinfo_proc * sizeof(struct kinfo_proc); | 78 len = num_of_kinfo_proc * sizeof(struct kinfo_proc); |
| 81 // Load the list of processes | 79 // Load the list of processes |
| 82 if (sysctl(mib, arraysize(mib), &kinfo_procs_[0], &len, NULL, 0) < 0) { | 80 if (sysctl(mib, arraysize(mib), &kinfo_procs_[0], &len, NULL, 0) < 0) { |
| 83 // If we get a mem error, it just means we need a bigger buffer, so | 81 // If we get a mem error, it just means we need a bigger buffer, so |
| 84 // loop around again. Anything else is a real error and give up. | 82 // loop around again. Anything else is a real error and give up. |
| 85 if (errno != ENOMEM) { | 83 if (errno != ENOMEM) { |
| 86 LOG(ERROR) << "failed to get the process list"; | 84 LOG(ERROR) << "failed to get the process list"; |
| 87 kinfo_procs_.resize(0); | 85 kinfo_procs_.resize(0); |
| 88 done = true; | 86 done = true; |
| 89 } | 87 } |
| 90 } else { | 88 } else { |
| 91 // Got the list, just make sure we're sized exactly right | 89 // Got the list, just make sure we're sized exactly right |
| 92 size_t num_of_kinfo_proc = len / sizeof(struct kinfo_proc); | 90 size_t num_of_kinfo_proc = len / sizeof(struct kinfo_proc); |
| 93 kinfo_procs_.resize(num_of_kinfo_proc); | 91 kinfo_procs_.resize(num_of_kinfo_proc); |
| 94 done = true; | 92 done = true; |
| 95 } | 93 } |
| 96 } | 94 } |
| 97 } while (!done && (try_num++ < max_tries)); | 95 } while (!done && (try_num++ < max_tries)); |
| 98 | 96 |
| 99 if (!done) { | 97 if (!done) { |
| 100 LOG(ERROR) << "failed to collect the process list in a few tries"; | 98 LOG(ERROR) << "failed to collect the process list in a few tries"; |
| 101 kinfo_procs_.resize(0); | 99 kinfo_procs_.resize(0); |
| 102 } | 100 } |
| 103 } | 101 } |
| 104 | 102 |
| 105 NamedProcessIterator::~NamedProcessIterator() { | 103 ProcessIterator::~ProcessIterator() { |
| 106 } | 104 } |
| 107 | 105 |
| 108 const ProcessEntry* NamedProcessIterator::NextProcessEntry() { | 106 bool ProcessIterator::CheckForNextProcess() { |
| 109 bool result = false; | |
| 110 do { | |
| 111 result = CheckForNextProcess(); | |
| 112 } while (result && !IncludeEntry()); | |
| 113 | |
| 114 if (result) { | |
| 115 return &entry_; | |
| 116 } | |
| 117 | |
| 118 return NULL; | |
| 119 } | |
| 120 | |
| 121 bool NamedProcessIterator::CheckForNextProcess() { | |
| 122 std::string executable_name_utf8(base::SysWideToUTF8(executable_name_)); | |
| 123 | |
| 124 std::string data; | 107 std::string data; |
| 125 std::string exec_name; | |
| 126 | |
| 127 for (; index_of_kinfo_proc_ < kinfo_procs_.size(); ++index_of_kinfo_proc_) { | 108 for (; index_of_kinfo_proc_ < kinfo_procs_.size(); ++index_of_kinfo_proc_) { |
| 128 kinfo_proc* kinfo = &kinfo_procs_[index_of_kinfo_proc_]; | 109 kinfo_proc& kinfo = kinfo_procs_[index_of_kinfo_proc_]; |
| 129 | 110 |
| 130 // Skip processes just awaiting collection | 111 // Skip processes just awaiting collection |
| 131 if ((kinfo->kp_proc.p_pid > 0) && (kinfo->kp_proc.p_stat == SZOMB)) | 112 if ((kinfo.kp_proc.p_pid > 0) && (kinfo.kp_proc.p_stat == SZOMB)) |
| 132 continue; | 113 continue; |
| 133 | 114 |
| 134 int mib[] = { CTL_KERN, KERN_PROCARGS, kinfo->kp_proc.p_pid }; | 115 int mib[] = { CTL_KERN, KERN_PROCARGS, kinfo.kp_proc.p_pid }; |
| 135 | 116 |
| 136 // Found out what size buffer we need | 117 // Find out what size buffer we need. |
| 137 size_t data_len = 0; | 118 size_t data_len = 0; |
| 138 if (sysctl(mib, arraysize(mib), NULL, &data_len, NULL, 0) < 0) { | 119 if (sysctl(mib, arraysize(mib), NULL, &data_len, NULL, 0) < 0) { |
| 139 LOG(ERROR) << "failed to figure out the buffer size for a commandline"; | 120 LOG(ERROR) << "failed to figure out the buffer size for a commandline"; |
| 140 continue; | 121 continue; |
| 141 } | 122 } |
| 142 | 123 |
| 143 data.resize(data_len); | 124 data.resize(data_len); |
| 144 if (sysctl(mib, arraysize(mib), &data[0], &data_len, NULL, 0) < 0) { | 125 if (sysctl(mib, arraysize(mib), &data[0], &data_len, NULL, 0) < 0) { |
| 145 LOG(ERROR) << "failed to fetch a commandline"; | 126 LOG(ERROR) << "failed to fetch a commandline"; |
| 146 continue; | 127 continue; |
| 147 } | 128 } |
| 148 | 129 |
| 149 // Data starts w/ the full path null termed, so we have to extract just the | 130 // Data starts w/ the full path null termed, so we have to extract just the |
| 150 // executable name from the path. | 131 // executable name from the path. |
| 151 | 132 |
| 152 size_t exec_name_end = data.find('\0'); | 133 size_t exec_name_end = data.find('\0'); |
| 153 if (exec_name_end == std::string::npos) { | 134 if (exec_name_end == std::string::npos) { |
| 154 LOG(ERROR) << "command line data didn't match expected format"; | 135 LOG(ERROR) << "command line data didn't match expected format"; |
| 155 continue; | 136 continue; |
| 156 } | 137 } |
| 138 entry_.pid_ = kinfo.kp_proc.p_pid; |
| 139 entry_.ppid_ = kinfo.kp_eproc.e_ppid; |
| 140 entry_.gid_ = kinfo.kp_eproc.e_pgid; |
| 157 size_t last_slash = data.rfind('/', exec_name_end); | 141 size_t last_slash = data.rfind('/', exec_name_end); |
| 158 if (last_slash == std::string::npos) | 142 if (last_slash == std::string::npos) |
| 159 exec_name = data.substr(0, exec_name_end); | 143 entry_.exe_file_.assign(data, 0, exec_name_end); |
| 160 else | 144 else |
| 161 exec_name = data.substr(last_slash + 1, exec_name_end - last_slash - 1); | 145 entry_.exe_file_.assign(data, last_slash + 1, |
| 162 | 146 exec_name_end - last_slash - 1); |
| 163 // Check the name | 147 // Start w/ the next entry next time through |
| 164 if (executable_name_utf8 == exec_name) { | 148 ++index_of_kinfo_proc_; |
| 165 entry_.pid = kinfo->kp_proc.p_pid; | 149 // Done |
| 166 entry_.ppid = kinfo->kp_eproc.e_ppid; | 150 return true; |
| 167 base::strlcpy(entry_.szExeFile, exec_name.c_str(), | |
| 168 sizeof(entry_.szExeFile)); | |
| 169 // Start w/ the next entry next time through | |
| 170 ++index_of_kinfo_proc_; | |
| 171 // Done | |
| 172 return true; | |
| 173 } | |
| 174 } | 151 } |
| 175 return false; | 152 return false; |
| 176 } | 153 } |
| 177 | 154 |
| 178 bool NamedProcessIterator::IncludeEntry() { | 155 bool NamedProcessIterator::IncludeEntry() { |
| 179 // Don't need to check the name, we did that w/in CheckForNextProcess. | 156 return (base::SysWideToUTF8(executable_name_) == entry().exe_file() && |
| 180 if (!filter_) | 157 ProcessIterator::IncludeEntry()); |
| 181 return true; | |
| 182 return filter_->Includes(entry_.pid, entry_.ppid); | |
| 183 } | 158 } |
| 184 | 159 |
| 185 | 160 |
| 186 // ------------------------------------------------------------------------ | 161 // ------------------------------------------------------------------------ |
| 187 // NOTE: about ProcessMetrics | 162 // NOTE: about ProcessMetrics |
| 188 // | 163 // |
| 189 // Getting a mach task from a pid for another process requires permissions in | 164 // Getting a mach task from a pid for another process requires permissions in |
| 190 // general, so there doesn't really seem to be a way to do these (and spinning | 165 // general, so there doesn't really seem to be a way to do these (and spinning |
| 191 // up ps to fetch each stats seems dangerous to put in a base api for anyone to | 166 // up ps to fetch each stats seems dangerous to put in a base api for anyone to |
| 192 // call). Child processes ipc their port, so return something if available, | 167 // call). Child processes ipc their port, so return something if available, |
| 193 // otherwise return 0. | 168 // otherwise return 0. |
| 194 // | 169 // |
| 170 |
| 171 ProcessMetrics::ProcessMetrics(ProcessHandle process, |
| 172 ProcessMetrics::PortProvider* port_provider) |
| 173 : process_(process), |
| 174 last_time_(0), |
| 175 last_system_time_(0), |
| 176 port_provider_(port_provider) { |
| 177 processor_count_ = base::SysInfo::NumberOfProcessors(); |
| 178 } |
| 179 |
| 180 // static |
| 181 ProcessMetrics* ProcessMetrics::CreateProcessMetrics( |
| 182 ProcessHandle process, |
| 183 ProcessMetrics::PortProvider* port_provider) { |
| 184 return new ProcessMetrics(process, port_provider); |
| 185 } |
| 186 |
| 195 bool ProcessMetrics::GetIOCounters(IoCounters* io_counters) const { | 187 bool ProcessMetrics::GetIOCounters(IoCounters* io_counters) const { |
| 196 return false; | 188 return false; |
| 197 } | 189 } |
| 198 | 190 |
| 199 static bool GetTaskInfo(mach_port_t task, task_basic_info_64* task_info_data) { | 191 static bool GetTaskInfo(mach_port_t task, task_basic_info_64* task_info_data) { |
| 200 if (task == MACH_PORT_NULL) | 192 if (task == MACH_PORT_NULL) |
| 201 return false; | 193 return false; |
| 202 mach_msg_type_number_t count = TASK_BASIC_INFO_64_COUNT; | 194 mach_msg_type_number_t count = TASK_BASIC_INFO_64_COUNT; |
| 203 kern_return_t kr = task_info(task, | 195 kern_return_t kr = task_info(task, |
| 204 TASK_BASIC_INFO_64, | 196 TASK_BASIC_INFO_64, |
| (...skipping 372 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 577 @selector(allocWithZone:)); | 569 @selector(allocWithZone:)); |
| 578 g_old_allocWithZone = reinterpret_cast<allocWithZone_t>( | 570 g_old_allocWithZone = reinterpret_cast<allocWithZone_t>( |
| 579 method_getImplementation(orig_method)); | 571 method_getImplementation(orig_method)); |
| 580 CHECK(g_old_allocWithZone) | 572 CHECK(g_old_allocWithZone) |
| 581 << "Failed to get allocWithZone allocation function."; | 573 << "Failed to get allocWithZone allocation function."; |
| 582 method_setImplementation(orig_method, | 574 method_setImplementation(orig_method, |
| 583 reinterpret_cast<IMP>(oom_killer_allocWithZone)); | 575 reinterpret_cast<IMP>(oom_killer_allocWithZone)); |
| 584 } | 576 } |
| 585 | 577 |
| 586 } // namespace base | 578 } // namespace base |
| OLD | NEW |