| 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 #include "base/process_util.h" | 5 #include "base/process_util.h" |
| 6 | 6 |
| 7 #include <ctype.h> | 7 #include <ctype.h> |
| 8 #include <dirent.h> | 8 #include <dirent.h> |
| 9 #include <fcntl.h> | 9 #include <fcntl.h> |
| 10 #include <unistd.h> | 10 #include <unistd.h> |
| (...skipping 82 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 93 } | 93 } |
| 94 | 94 |
| 95 bool DidProcessCrash(ProcessHandle handle) { | 95 bool DidProcessCrash(ProcessHandle handle) { |
| 96 int status; | 96 int status; |
| 97 if (waitpid(handle, &status, WNOHANG)) { | 97 if (waitpid(handle, &status, WNOHANG)) { |
| 98 // I feel like dancing! | 98 // I feel like dancing! |
| 99 return false; | 99 return false; |
| 100 } | 100 } |
| 101 | 101 |
| 102 if (WIFSIGNALED(status)) { | 102 if (WIFSIGNALED(status)) { |
| 103 int signum = WTERMSIG(status); | 103 switch(WTERMSIG(status)) { |
| 104 return (signum == SIGSEGV || signum == SIGILL || signum == SIGABRT || | 104 case SIGSEGV: |
| 105 signum == SIGFPE); | 105 case SIGILL: |
| 106 case SIGABRT: |
| 107 case SIGFPE: |
| 108 return true; |
| 109 default: |
| 110 return false; |
| 111 } |
| 106 } | 112 } |
| 107 | 113 |
| 108 if (WIFEXITED(status)) { | 114 if (WIFEXITED(status)) |
| 109 int exitcode = WEXITSTATUS(status); | 115 return WEXITSTATUS(status) != 0; |
| 110 return (exitcode != 0); | |
| 111 } | |
| 112 | 116 |
| 113 return false; | 117 return false; |
| 114 } | 118 } |
| 115 | 119 |
| 116 NamedProcessIterator::NamedProcessIterator(const std::wstring& executable_name, | 120 NamedProcessIterator::NamedProcessIterator(const std::wstring& executable_name, |
| 117 const ProcessFilter* filter) | 121 const ProcessFilter* filter) |
| 118 : | 122 : executable_name_(executable_name), filter_(filter) { |
| 119 executable_name_(executable_name), | 123 procfs_dir_ = opendir("/proc"); |
| 120 filter_(filter) { | 124 } |
| 121 procfs_dir_ = opendir("/proc"); | |
| 122 } | |
| 123 | 125 |
| 124 NamedProcessIterator::~NamedProcessIterator() { | 126 NamedProcessIterator::~NamedProcessIterator() { |
| 125 if (procfs_dir_) { | 127 if (procfs_dir_) { |
| 126 closedir(procfs_dir_); | 128 closedir(procfs_dir_); |
| 127 procfs_dir_ = 0; | 129 procfs_dir_ = NULL; |
| 128 } | 130 } |
| 129 } | 131 } |
| 130 | 132 |
| 131 const ProcessEntry* NamedProcessIterator::NextProcessEntry() { | 133 const ProcessEntry* NamedProcessIterator::NextProcessEntry() { |
| 132 bool result = false; | 134 bool result = false; |
| 133 do { | 135 do { |
| 134 result = CheckForNextProcess(); | 136 result = CheckForNextProcess(); |
| 135 } while (result && !IncludeEntry()); | 137 } while (result && !IncludeEntry()); |
| 136 | 138 |
| 137 if (result) | 139 if (result) |
| 138 return &entry_; | 140 return &entry_; |
| 139 | 141 |
| 140 return NULL; | 142 return NULL; |
| 141 } | 143 } |
| 142 | 144 |
| 143 bool NamedProcessIterator::CheckForNextProcess() { | 145 bool NamedProcessIterator::CheckForNextProcess() { |
| 144 // TODO(port): skip processes owned by different UID | 146 // TODO(port): skip processes owned by different UID |
| 145 | 147 |
| 146 dirent* slot = 0; | 148 dirent* slot = 0; |
| 147 const char* openparen; | 149 const char* openparen; |
| 148 const char* closeparen; | 150 const char* closeparen; |
| 149 | 151 |
| 150 // Arbitrarily guess that there will never be more than 200 non-process files
in /proc. | 152 // Arbitrarily guess that there will never be more than 200 non-process |
| 151 // (Hardy has 53.) | 153 // files in /proc. Hardy has 53. |
| 152 int skipped = 0; | 154 int skipped = 0; |
| 153 const int kSkipLimit = 200; | 155 const int kSkipLimit = 200; |
| 154 while (skipped < kSkipLimit) { | 156 while (skipped < kSkipLimit) { |
| 155 slot = readdir(procfs_dir_); | 157 slot = readdir(procfs_dir_); |
| 156 // all done looking through /proc? | 158 // all done looking through /proc? |
| 157 if (!slot) | 159 if (!slot) |
| 158 return false; | 160 return false; |
| 159 | 161 |
| 160 // If not a process, keep looking for one. | 162 // If not a process, keep looking for one. |
| 161 bool notprocess = false; | 163 bool notprocess = false; |
| 162 int i; | 164 int i; |
| 163 for (i=0; i < NAME_MAX && slot->d_name[i]; ++i) { | 165 for (i = 0; i < NAME_MAX && slot->d_name[i]; ++i) { |
| 164 if (!isdigit(slot->d_name[i])) { | 166 if (!isdigit(slot->d_name[i])) { |
| 165 notprocess = true; | 167 notprocess = true; |
| 166 break; | 168 break; |
| 167 } | 169 } |
| 168 } | 170 } |
| 169 if (i == NAME_MAX || notprocess) { | 171 if (i == NAME_MAX || notprocess) { |
| 170 skipped++; | 172 skipped++; |
| 171 continue; | 173 continue; |
| 172 } | 174 } |
| 173 | 175 |
| 174 // Read the process's status. | 176 // Read the process's status. |
| 175 char buf[NAME_MAX + 12]; | 177 char buf[NAME_MAX + 12]; |
| 176 sprintf(buf, "/proc/%s/stat", slot->d_name); | 178 sprintf(buf, "/proc/%s/stat", slot->d_name); |
| 177 FILE *fp = fopen(buf, "r"); | 179 FILE *fp = fopen(buf, "r"); |
| 178 if (!fp) | 180 if (!fp) |
| 179 return false; | 181 return false; |
| 180 const char* result = fgets(buf, sizeof(buf), fp); | 182 const char* result = fgets(buf, sizeof(buf), fp); |
| 181 fclose(fp); | 183 fclose(fp); |
| 182 if (!result) | 184 if (!result) |
| 183 return false; | 185 return false; |
| 184 | 186 |
| 185 // Parse the status. It is formatted like this: | 187 // Parse the status. It is formatted like this: |
| 186 // %d (%s) %c %d ... | 188 // %d (%s) %c %d ... |
| 187 // pid (name) runstate ppid | 189 // pid (name) runstate ppid |
| 188 // To avoid being fooled by names containing a closing paren, scan backwards
. | 190 // To avoid being fooled by names containing a closing paren, scan |
| 191 // backwards. |
| 189 openparen = strchr(buf, '('); | 192 openparen = strchr(buf, '('); |
| 190 closeparen = strrchr(buf, ')'); | 193 closeparen = strrchr(buf, ')'); |
| 191 if (!openparen || !closeparen) | 194 if (!openparen || !closeparen) |
| 192 return false; | 195 return false; |
| 193 char runstate = closeparen[2]; | 196 char runstate = closeparen[2]; |
| 194 | 197 |
| 195 // Is the process in 'Zombie' state, i.e. dead but waiting to be reaped? | 198 // Is the process in 'Zombie' state, i.e. dead but waiting to be reaped? |
| 196 // Allowed values: D R S T Z | 199 // Allowed values: D R S T Z |
| 197 if (runstate != 'Z') | 200 if (runstate != 'Z') |
| 198 break; | 201 break; |
| 199 | 202 |
| 200 // Nope, it's a zombie; somebody isn't cleaning up after their children. | 203 // Nope, it's a zombie; somebody isn't cleaning up after their children. |
| 201 // (e.g. WaitForProcessesToExit doesn't clean up after dead children yet.) | 204 // (e.g. WaitForProcessesToExit doesn't clean up after dead children yet.) |
| 202 // There could be a lot of zombies, can't really decrement i here. | 205 // There could be a lot of zombies, can't really decrement i here. |
| 203 } | 206 } |
| 204 if (skipped >= kSkipLimit) { | 207 if (skipped >= kSkipLimit) { |
| 205 NOTREACHED(); | 208 NOTREACHED(); |
| 206 return false; | 209 return false; |
| 207 } | 210 } |
| 208 | 211 |
| 209 entry_.pid = atoi(slot->d_name); | 212 entry_.pid = atoi(slot->d_name); |
| 210 entry_.ppid = atoi(closeparen+3); | 213 entry_.ppid = atoi(closeparen + 3); |
| 211 | 214 |
| 212 // TODO(port): read pid's commandline's $0, like killall does. | 215 // TODO(port): read pid's commandline's $0, like killall does. Using the |
| 213 // Using the short name between openparen and closeparen won't work for long n
ames! | 216 // short name between openparen and closeparen won't work for long names! |
| 214 int len = closeparen - openparen - 1; | 217 int len = closeparen - openparen - 1; |
| 215 if (len > NAME_MAX) | 218 if (len > NAME_MAX) |
| 216 len = NAME_MAX; | 219 len = NAME_MAX; |
| 217 memcpy(entry_.szExeFile, openparen + 1, len); | 220 memcpy(entry_.szExeFile, openparen + 1, len); |
| 218 entry_.szExeFile[len] = 0; | 221 entry_.szExeFile[len] = 0; |
| 219 | 222 |
| 220 return true; | 223 return true; |
| 221 } | 224 } |
| 222 | 225 |
| 223 bool NamedProcessIterator::IncludeEntry() { | 226 bool NamedProcessIterator::IncludeEntry() { |
| 224 // TODO(port): make this also work for non-ASCII filenames | 227 // TODO(port): make this also work for non-ASCII filenames |
| 225 bool result = strcmp(WideToASCII(executable_name_).c_str(), entry_.szExeFile)
== 0 && | 228 if (WideToASCII(executable_name_) != entry_.szExeFile) |
| 226 (!filter_ || filter_->Includes(entry_.pid, entry_.ppid)); | 229 return false; |
| 227 return result; | 230 if (!filter_) |
| 231 return true; |
| 232 return filter_->Includes(entry_.pid, entry_.ppid); |
| 228 } | 233 } |
| 229 | 234 |
| 230 int GetProcessCount(const std::wstring& executable_name, | 235 int GetProcessCount(const std::wstring& executable_name, |
| 231 const ProcessFilter* filter) { | 236 const ProcessFilter* filter) { |
| 232 int count = 0; | 237 int count = 0; |
| 233 | 238 |
| 234 NamedProcessIterator iter(executable_name, filter); | 239 NamedProcessIterator iter(executable_name, filter); |
| 235 while (iter.NextProcessEntry()) | 240 while (iter.NextProcessEntry()) |
| 236 ++count; | 241 ++count; |
| 237 return count; | 242 return count; |
| (...skipping 12 matching lines...) Expand all Loading... |
| 250 } | 255 } |
| 251 | 256 |
| 252 bool WaitForProcessesToExit(const std::wstring& executable_name, | 257 bool WaitForProcessesToExit(const std::wstring& executable_name, |
| 253 int wait_milliseconds, | 258 int wait_milliseconds, |
| 254 const ProcessFilter* filter) { | 259 const ProcessFilter* filter) { |
| 255 bool result = false; | 260 bool result = false; |
| 256 | 261 |
| 257 // TODO(port): This is inefficient, but works if there are multiple procs. | 262 // TODO(port): This is inefficient, but works if there are multiple procs. |
| 258 // TODO(port): use waitpid to avoid leaving zombies around | 263 // TODO(port): use waitpid to avoid leaving zombies around |
| 259 | 264 |
| 260 base::Time end_time = base::Time::Now() + base::TimeDelta::FromMilliseconds(wa
it_milliseconds); | 265 base::Time end_time = base::Time::Now() + |
| 266 base::TimeDelta::FromMilliseconds(wait_milliseconds); |
| 261 do { | 267 do { |
| 262 NamedProcessIterator iter(executable_name, filter); | 268 NamedProcessIterator iter(executable_name, filter); |
| 263 if (!iter.NextProcessEntry()) { | 269 if (!iter.NextProcessEntry()) { |
| 264 result = true; | 270 result = true; |
| 265 break; | 271 break; |
| 266 } | 272 } |
| 267 PlatformThread::Sleep(100); | 273 PlatformThread::Sleep(100); |
| 268 } while ((base::Time::Now() - end_time) > base::TimeDelta()); | 274 } while ((base::Time::Now() - end_time) > base::TimeDelta()); |
| 269 | 275 |
| 270 return result; | 276 return result; |
| 271 } | 277 } |
| 272 | 278 |
| 273 bool CleanupProcesses(const std::wstring& executable_name, | 279 bool CleanupProcesses(const std::wstring& executable_name, |
| 274 int wait_milliseconds, | 280 int wait_milliseconds, |
| 275 int exit_code, | 281 int exit_code, |
| 276 const ProcessFilter* filter) { | 282 const ProcessFilter* filter) { |
| 277 bool exited_cleanly = | 283 bool exited_cleanly = |
| 278 WaitForProcessesToExit(executable_name, wait_milliseconds, | 284 WaitForProcessesToExit(executable_name, wait_milliseconds, |
| 279 filter); | 285 filter); |
| 280 if (!exited_cleanly) | 286 if (!exited_cleanly) |
| 281 KillProcesses(executable_name, exit_code, filter); | 287 KillProcesses(executable_name, exit_code, filter); |
| 282 return exited_cleanly; | 288 return exited_cleanly; |
| 283 } | 289 } |
| 284 | 290 |
| 285 /////////////////////////////////////////////////////////////////////////////// | |
| 286 //// ProcessMetrics | |
| 287 | |
| 288 // To have /proc/self/io file you must enable CONFIG_TASK_IO_ACCOUNTING | 291 // To have /proc/self/io file you must enable CONFIG_TASK_IO_ACCOUNTING |
| 289 // in your kernel configuration. | 292 // in your kernel configuration. |
| 290 bool ProcessMetrics::GetIOCounters(IoCounters* io_counters) { | 293 bool ProcessMetrics::GetIOCounters(IoCounters* io_counters) { |
| 291 std::string proc_io_contents; | 294 std::string proc_io_contents; |
| 292 if (!file_util::ReadFileToString(L"/proc/self/io", &proc_io_contents)) | 295 if (!file_util::ReadFileToString(L"/proc/self/io", &proc_io_contents)) |
| 293 return false; | 296 return false; |
| 294 | 297 |
| 295 (*io_counters).OtherOperationCount = 0; | 298 (*io_counters).OtherOperationCount = 0; |
| 296 (*io_counters).OtherTransferCount = 0; | 299 (*io_counters).OtherTransferCount = 0; |
| 297 | 300 |
| (...skipping 18 matching lines...) Expand all Loading... |
| 316 (*io_counters).WriteTransferCount = StringToInt64(tokenizer.token()); | 319 (*io_counters).WriteTransferCount = StringToInt64(tokenizer.token()); |
| 317 } | 320 } |
| 318 state = KEY_NAME; | 321 state = KEY_NAME; |
| 319 break; | 322 break; |
| 320 } | 323 } |
| 321 } | 324 } |
| 322 return true; | 325 return true; |
| 323 } | 326 } |
| 324 | 327 |
| 325 } // namespace base | 328 } // namespace base |
| OLD | NEW |