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 |