OLD | NEW |
1 // Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2006-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 <dirent.h> | 5 #include <dirent.h> |
6 #include <errno.h> | 6 #include <errno.h> |
7 #include <fcntl.h> | 7 #include <fcntl.h> |
8 #include <signal.h> | 8 #include <signal.h> |
9 #include <stdlib.h> | 9 #include <stdlib.h> |
10 #include <sys/resource.h> | 10 #include <sys/resource.h> |
11 #include <sys/time.h> | 11 #include <sys/time.h> |
12 #include <sys/types.h> | 12 #include <sys/types.h> |
13 #include <sys/wait.h> | 13 #include <sys/wait.h> |
14 #include <unistd.h> | 14 #include <unistd.h> |
15 | 15 |
16 #include <limits> | 16 #include <limits> |
17 #include <set> | 17 #include <set> |
18 | 18 |
19 #include "base/basictypes.h" | 19 #include "base/debug_util.h" |
20 #include "base/eintr_wrapper.h" | 20 #include "base/eintr_wrapper.h" |
21 #include "base/logging.h" | 21 #include "base/logging.h" |
22 #include "base/platform_thread.h" | 22 #include "base/platform_thread.h" |
23 #include "base/process_util.h" | 23 #include "base/process_util.h" |
24 #include "base/scoped_ptr.h" | 24 #include "base/scoped_ptr.h" |
25 #include "base/sys_info.h" | 25 #include "base/sys_info.h" |
26 #include "base/time.h" | 26 #include "base/time.h" |
27 #include "base/waitable_event.h" | 27 #include "base/waitable_event.h" |
28 | 28 |
29 const int kMicrosecondsPerSecond = 1000000; | 29 const int kMicrosecondsPerSecond = 1000000; |
30 | 30 |
31 namespace base { | 31 namespace base { |
32 | 32 |
| 33 namespace { |
| 34 |
| 35 int WaitpidWithTimeout(ProcessHandle handle, int64 wait_milliseconds, |
| 36 bool* success) { |
| 37 // This POSIX version of this function only guarantees that we wait no less |
| 38 // than |wait_milliseconds| for the proces to exit. The child process may |
| 39 // exit sometime before the timeout has ended but we may still block for |
| 40 // up to 0.25 seconds after the fact. |
| 41 // |
| 42 // waitpid() has no direct support on POSIX for specifying a timeout, you can |
| 43 // either ask it to block indefinitely or return immediately (WNOHANG). |
| 44 // When a child process terminates a SIGCHLD signal is sent to the parent. |
| 45 // Catching this signal would involve installing a signal handler which may |
| 46 // affect other parts of the application and would be difficult to debug. |
| 47 // |
| 48 // Our strategy is to call waitpid() once up front to check if the process |
| 49 // has already exited, otherwise to loop for wait_milliseconds, sleeping for |
| 50 // at most 0.25 secs each time using usleep() and then calling waitpid(). |
| 51 // |
| 52 // usleep() is speced to exit if a signal is received for which a handler |
| 53 // has been installed. This means that when a SIGCHLD is sent, it will exit |
| 54 // depending on behavior external to this function. |
| 55 // |
| 56 // This function is used primarily for unit tests, if we want to use it in |
| 57 // the application itself it would probably be best to examine other routes. |
| 58 int status = -1; |
| 59 pid_t ret_pid = HANDLE_EINTR(waitpid(handle, &status, WNOHANG)); |
| 60 static const int64 kQuarterSecondInMicroseconds = kMicrosecondsPerSecond / 4; |
| 61 |
| 62 // If the process hasn't exited yet, then sleep and try again. |
| 63 Time wakeup_time = Time::Now() + TimeDelta::FromMilliseconds( |
| 64 wait_milliseconds); |
| 65 while (ret_pid == 0) { |
| 66 Time now = Time::Now(); |
| 67 if (now > wakeup_time) |
| 68 break; |
| 69 // Guaranteed to be non-negative! |
| 70 int64 sleep_time_usecs = (wakeup_time - now).InMicroseconds(); |
| 71 // Don't sleep for more than 0.25 secs at a time. |
| 72 if (sleep_time_usecs > kQuarterSecondInMicroseconds) { |
| 73 sleep_time_usecs = kQuarterSecondInMicroseconds; |
| 74 } |
| 75 |
| 76 // usleep() will return 0 and set errno to EINTR on receipt of a signal |
| 77 // such as SIGCHLD. |
| 78 usleep(sleep_time_usecs); |
| 79 ret_pid = HANDLE_EINTR(waitpid(handle, &status, WNOHANG)); |
| 80 } |
| 81 |
| 82 if (success) |
| 83 *success = (ret_pid != -1); |
| 84 |
| 85 return status; |
| 86 } |
| 87 |
| 88 void StackDumpSignalHandler(int signal) { |
| 89 StackTrace().PrintBacktrace(); |
| 90 _exit(1); |
| 91 } |
| 92 |
| 93 } // namespace |
| 94 |
33 ProcessId GetCurrentProcId() { | 95 ProcessId GetCurrentProcId() { |
34 return getpid(); | 96 return getpid(); |
35 } | 97 } |
36 | 98 |
37 ProcessHandle GetCurrentProcessHandle() { | 99 ProcessHandle GetCurrentProcessHandle() { |
38 return GetCurrentProcId(); | 100 return GetCurrentProcId(); |
39 } | 101 } |
40 | 102 |
41 bool OpenProcessHandle(ProcessId pid, ProcessHandle* handle) { | 103 bool OpenProcessHandle(ProcessId pid, ProcessHandle* handle) { |
42 // On Posix platforms, process handles are the same as PIDs, so we | 104 // On Posix platforms, process handles are the same as PIDs, so we |
(...skipping 273 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
316 ProcessMetrics* ProcessMetrics::CreateProcessMetrics(ProcessHandle process) { | 378 ProcessMetrics* ProcessMetrics::CreateProcessMetrics(ProcessHandle process) { |
317 return new ProcessMetrics(process); | 379 return new ProcessMetrics(process); |
318 } | 380 } |
319 | 381 |
320 ProcessMetrics::~ProcessMetrics() { } | 382 ProcessMetrics::~ProcessMetrics() { } |
321 | 383 |
322 void EnableTerminationOnHeapCorruption() { | 384 void EnableTerminationOnHeapCorruption() { |
323 // On POSIX, there nothing to do AFAIK. | 385 // On POSIX, there nothing to do AFAIK. |
324 } | 386 } |
325 | 387 |
| 388 bool EnableInProcessStackDumping() { |
| 389 // When running in an application, our code typically expects SIGPIPE |
| 390 // to be ignored. Therefore, when testing that same code, it should run |
| 391 // with SIGPIPE ignored as well. |
| 392 struct sigaction action; |
| 393 action.sa_handler = SIG_IGN; |
| 394 action.sa_flags = 0; |
| 395 sigemptyset(&action.sa_mask); |
| 396 bool success = (sigaction(SIGPIPE, &action, NULL) == 0); |
| 397 |
| 398 // TODO(phajdan.jr): Catch other crashy signals, like SIGABRT. |
| 399 success &= (signal(SIGSEGV, &StackDumpSignalHandler) != SIG_ERR); |
| 400 success &= (signal(SIGILL, &StackDumpSignalHandler) != SIG_ERR); |
| 401 success &= (signal(SIGBUS, &StackDumpSignalHandler) != SIG_ERR); |
| 402 success &= (signal(SIGFPE, &StackDumpSignalHandler) != SIG_ERR); |
| 403 return success; |
| 404 } |
| 405 |
| 406 void AttachToConsole() { |
| 407 // On POSIX, there nothing to do AFAIK. Maybe create a new console if none |
| 408 // exist? |
| 409 } |
| 410 |
326 void RaiseProcessToHighPriority() { | 411 void RaiseProcessToHighPriority() { |
327 // On POSIX, we don't actually do anything here. We could try to nice() or | 412 // On POSIX, we don't actually do anything here. We could try to nice() or |
328 // setpriority() or sched_getscheduler, but these all require extra rights. | 413 // setpriority() or sched_getscheduler, but these all require extra rights. |
329 } | 414 } |
330 | 415 |
331 bool DidProcessCrash(bool* child_exited, ProcessHandle handle) { | 416 bool DidProcessCrash(bool* child_exited, ProcessHandle handle) { |
332 int status; | 417 int status; |
333 const pid_t result = HANDLE_EINTR(waitpid(handle, &status, WNOHANG)); | 418 const pid_t result = HANDLE_EINTR(waitpid(handle, &status, WNOHANG)); |
334 if (result == -1) { | 419 if (result == -1) { |
335 PLOG(ERROR) << "waitpid(" << handle << ")"; | 420 PLOG(ERROR) << "waitpid(" << handle << ")"; |
(...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
374 if (WIFEXITED(status)) { | 459 if (WIFEXITED(status)) { |
375 *exit_code = WEXITSTATUS(status); | 460 *exit_code = WEXITSTATUS(status); |
376 return true; | 461 return true; |
377 } | 462 } |
378 | 463 |
379 // If it didn't exit cleanly, it must have been signaled. | 464 // If it didn't exit cleanly, it must have been signaled. |
380 DCHECK(WIFSIGNALED(status)); | 465 DCHECK(WIFSIGNALED(status)); |
381 return false; | 466 return false; |
382 } | 467 } |
383 | 468 |
384 namespace { | |
385 | |
386 int WaitpidWithTimeout(ProcessHandle handle, int64 wait_milliseconds, | |
387 bool* success) { | |
388 // This POSIX version of this function only guarantees that we wait no less | |
389 // than |wait_milliseconds| for the proces to exit. The child process may | |
390 // exit sometime before the timeout has ended but we may still block for | |
391 // up to 0.25 seconds after the fact. | |
392 // | |
393 // waitpid() has no direct support on POSIX for specifying a timeout, you can | |
394 // either ask it to block indefinitely or return immediately (WNOHANG). | |
395 // When a child process terminates a SIGCHLD signal is sent to the parent. | |
396 // Catching this signal would involve installing a signal handler which may | |
397 // affect other parts of the application and would be difficult to debug. | |
398 // | |
399 // Our strategy is to call waitpid() once up front to check if the process | |
400 // has already exited, otherwise to loop for wait_milliseconds, sleeping for | |
401 // at most 0.25 secs each time using usleep() and then calling waitpid(). | |
402 // | |
403 // usleep() is speced to exit if a signal is received for which a handler | |
404 // has been installed. This means that when a SIGCHLD is sent, it will exit | |
405 // depending on behavior external to this function. | |
406 // | |
407 // This function is used primarily for unit tests, if we want to use it in | |
408 // the application itself it would probably be best to examine other routes. | |
409 int status = -1; | |
410 pid_t ret_pid = HANDLE_EINTR(waitpid(handle, &status, WNOHANG)); | |
411 static const int64 kQuarterSecondInMicroseconds = kMicrosecondsPerSecond/4; | |
412 | |
413 // If the process hasn't exited yet, then sleep and try again. | |
414 Time wakeup_time = Time::Now() + TimeDelta::FromMilliseconds( | |
415 wait_milliseconds); | |
416 while (ret_pid == 0) { | |
417 Time now = Time::Now(); | |
418 if (now > wakeup_time) | |
419 break; | |
420 // Guaranteed to be non-negative! | |
421 int64 sleep_time_usecs = (wakeup_time - now).InMicroseconds(); | |
422 // Don't sleep for more than 0.25 secs at a time. | |
423 if (sleep_time_usecs > kQuarterSecondInMicroseconds) { | |
424 sleep_time_usecs = kQuarterSecondInMicroseconds; | |
425 } | |
426 | |
427 // usleep() will return 0 and set errno to EINTR on receipt of a signal | |
428 // such as SIGCHLD. | |
429 usleep(sleep_time_usecs); | |
430 ret_pid = HANDLE_EINTR(waitpid(handle, &status, WNOHANG)); | |
431 } | |
432 | |
433 if (success) | |
434 *success = (ret_pid != -1); | |
435 | |
436 return status; | |
437 } | |
438 | |
439 } // namespace | |
440 | |
441 bool WaitForSingleProcess(ProcessHandle handle, int64 wait_milliseconds) { | 469 bool WaitForSingleProcess(ProcessHandle handle, int64 wait_milliseconds) { |
442 bool waitpid_success; | 470 bool waitpid_success; |
443 int status; | 471 int status; |
444 if (wait_milliseconds == base::kNoTimeout) | 472 if (wait_milliseconds == base::kNoTimeout) |
445 waitpid_success = (HANDLE_EINTR(waitpid(handle, &status, 0)) != -1); | 473 waitpid_success = (HANDLE_EINTR(waitpid(handle, &status, 0)) != -1); |
446 else | 474 else |
447 status = WaitpidWithTimeout(handle, wait_milliseconds, &waitpid_success); | 475 status = WaitpidWithTimeout(handle, wait_milliseconds, &waitpid_success); |
448 if (status != -1) { | 476 if (status != -1) { |
449 DCHECK(waitpid_success); | 477 DCHECK(waitpid_success); |
450 return WIFEXITED(status); | 478 return WIFEXITED(status); |
(...skipping 187 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
638 const ProcessFilter* filter) { | 666 const ProcessFilter* filter) { |
639 bool exited_cleanly = | 667 bool exited_cleanly = |
640 WaitForProcessesToExit(executable_name, wait_milliseconds, | 668 WaitForProcessesToExit(executable_name, wait_milliseconds, |
641 filter); | 669 filter); |
642 if (!exited_cleanly) | 670 if (!exited_cleanly) |
643 KillProcesses(executable_name, exit_code, filter); | 671 KillProcesses(executable_name, exit_code, filter); |
644 return exited_cleanly; | 672 return exited_cleanly; |
645 } | 673 } |
646 | 674 |
647 } // namespace base | 675 } // namespace base |
OLD | NEW |