OLD | NEW |
1 // Copyright (c) 2010 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2010 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> |
(...skipping 22 matching lines...) Expand all Loading... |
33 #include <crt_externs.h> | 33 #include <crt_externs.h> |
34 #define environ (*_NSGetEnviron()) | 34 #define environ (*_NSGetEnviron()) |
35 #else | 35 #else |
36 extern char** environ; | 36 extern char** environ; |
37 #endif | 37 #endif |
38 | 38 |
39 namespace base { | 39 namespace base { |
40 | 40 |
41 namespace { | 41 namespace { |
42 | 42 |
43 int WaitpidWithTimeout(ProcessHandle handle, int64 wait_milliseconds, | 43 // WaitpidWithTimeout performs a timed waitpid() call. The arguments and return |
44 bool* success) { | 44 // value match waitpid(2). |
| 45 pid_t WaitpidWithTimeout(ProcessHandle handle, int* status, |
| 46 int64 wait_milliseconds) { |
45 // This POSIX version of this function only guarantees that we wait no less | 47 // This POSIX version of this function only guarantees that we wait no less |
46 // than |wait_milliseconds| for the process to exit. The child process may | 48 // than |wait_milliseconds| for the process to exit. The child process may |
47 // exit sometime before the timeout has ended but we may still block for up | 49 // exit sometime before the timeout has ended but we may still block for up |
48 // to 256 milliseconds after the fact. | 50 // to 256 milliseconds after the fact. |
49 // | 51 // |
50 // waitpid() has no direct support on POSIX for specifying a timeout, you can | 52 // waitpid() has no direct support on POSIX for specifying a timeout, you can |
51 // either ask it to block indefinitely or return immediately (WNOHANG). | 53 // either ask it to block indefinitely or return immediately (WNOHANG). |
52 // When a child process terminates a SIGCHLD signal is sent to the parent. | 54 // When a child process terminates a SIGCHLD signal is sent to the parent. |
53 // Catching this signal would involve installing a signal handler which may | 55 // Catching this signal would involve installing a signal handler which may |
54 // affect other parts of the application and would be difficult to debug. | 56 // affect other parts of the application and would be difficult to debug. |
55 // | 57 // |
56 // Our strategy is to call waitpid() once up front to check if the process | 58 // Our strategy is to call waitpid() once up front to check if the process |
57 // has already exited, otherwise to loop for wait_milliseconds, sleeping for | 59 // has already exited, otherwise to loop for wait_milliseconds, sleeping for |
58 // at most 256 milliseconds each time using usleep() and then calling | 60 // at most 256 milliseconds each time using usleep() and then calling |
59 // waitpid(). The amount of time we sleep starts out at 1 milliseconds, and | 61 // waitpid(). The amount of time we sleep starts out at 1 milliseconds, and |
60 // we double it every 4 sleep cycles. | 62 // we double it every 4 sleep cycles. |
61 // | 63 // |
62 // usleep() is speced to exit if a signal is received for which a handler | 64 // usleep() is speced to exit if a signal is received for which a handler |
63 // has been installed. This means that when a SIGCHLD is sent, it will exit | 65 // has been installed. This means that when a SIGCHLD is sent, it will exit |
64 // depending on behavior external to this function. | 66 // depending on behavior external to this function. |
65 // | 67 // |
66 // This function is used primarily for unit tests, if we want to use it in | 68 // This function is used in unit tests and by DidProcessCrash. See the |
67 // the application itself it would probably be best to examine other routes. | 69 // comments in DidProcessCrash about its use in Chrome itself. |
68 int status = -1; | |
69 pid_t ret_pid = HANDLE_EINTR(waitpid(handle, &status, WNOHANG)); | |
70 static const int64 kMaxSleepInMicroseconds = 1 << 18; // ~256 milliseconds. | 70 static const int64 kMaxSleepInMicroseconds = 1 << 18; // ~256 milliseconds. |
71 int64 max_sleep_time_usecs = 1 << 10; // ~1 milliseconds. | 71 int64 max_sleep_time_usecs = 1 << 10; // ~1 milliseconds. |
72 int64 double_sleep_time = 0; | 72 int double_sleep_time = 0; |
| 73 |
| 74 pid_t ret_pid = HANDLE_EINTR(waitpid(handle, status, WNOHANG)); |
| 75 if (ret_pid != 0) |
| 76 return ret_pid; |
73 | 77 |
74 // If the process hasn't exited yet, then sleep and try again. | 78 // If the process hasn't exited yet, then sleep and try again. |
75 Time wakeup_time = Time::Now() + | 79 Time wakeup_time = Time::Now() + |
76 TimeDelta::FromMilliseconds(wait_milliseconds); | 80 TimeDelta::FromMilliseconds(wait_milliseconds); |
77 while (ret_pid == 0) { | 81 while (ret_pid == 0) { |
78 Time now = Time::Now(); | 82 Time now = Time::Now(); |
79 if (now > wakeup_time) | 83 if (now > wakeup_time) |
80 break; | 84 break; |
81 // Guaranteed to be non-negative! | 85 // Guaranteed to be non-negative! |
82 int64 sleep_time_usecs = (wakeup_time - now).InMicroseconds(); | 86 int64 sleep_time_usecs = (wakeup_time - now).InMicroseconds(); |
83 // Sleep for a bit while we wait for the process to finish. | 87 // Sleep for a bit while we wait for the process to finish. |
84 if (sleep_time_usecs > max_sleep_time_usecs) | 88 if (sleep_time_usecs > max_sleep_time_usecs) |
85 sleep_time_usecs = max_sleep_time_usecs; | 89 sleep_time_usecs = max_sleep_time_usecs; |
86 | 90 |
87 // usleep() will return 0 and set errno to EINTR on receipt of a signal | 91 // usleep() will return 0 and set errno to EINTR on receipt of a signal |
88 // such as SIGCHLD. | 92 // such as SIGCHLD. |
89 usleep(sleep_time_usecs); | 93 usleep(sleep_time_usecs); |
90 ret_pid = HANDLE_EINTR(waitpid(handle, &status, WNOHANG)); | 94 ret_pid = HANDLE_EINTR(waitpid(handle, status, WNOHANG)); |
91 | 95 |
92 if ((max_sleep_time_usecs < kMaxSleepInMicroseconds) && | 96 if ((max_sleep_time_usecs < kMaxSleepInMicroseconds) && |
93 (double_sleep_time++ % 4 == 0)) { | 97 (double_sleep_time++ % 4 == 0)) { |
94 max_sleep_time_usecs *= 2; | 98 max_sleep_time_usecs *= 2; |
95 } | 99 } |
96 } | 100 } |
97 | 101 |
98 if (success) | 102 return ret_pid; |
99 *success = (ret_pid != -1); | |
100 | |
101 return status; | |
102 } | 103 } |
103 | 104 |
104 void StackDumpSignalHandler(int signal, siginfo_t* info, ucontext_t* context) { | 105 void StackDumpSignalHandler(int signal, siginfo_t* info, ucontext_t* context) { |
105 LOG(ERROR) << "Received signal " << signal; | 106 LOG(ERROR) << "Received signal " << signal; |
106 debug::StackTrace().PrintBacktrace(); | 107 debug::StackTrace().PrintBacktrace(); |
107 | 108 |
108 // TODO(shess): Port to Linux. | 109 // TODO(shess): Port to Linux. |
109 #if defined(OS_MACOSX) | 110 #if defined(OS_MACOSX) |
110 // TODO(shess): Port to 64-bit. | 111 // TODO(shess): Port to 64-bit. |
111 #if ARCH_CPU_32_BITS | 112 #if ARCH_CPU_32_BITS |
(...skipping 512 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
624 | 625 |
625 return success; | 626 return success; |
626 } | 627 } |
627 | 628 |
628 void RaiseProcessToHighPriority() { | 629 void RaiseProcessToHighPriority() { |
629 // On POSIX, we don't actually do anything here. We could try to nice() or | 630 // On POSIX, we don't actually do anything here. We could try to nice() or |
630 // setpriority() or sched_getscheduler, but these all require extra rights. | 631 // setpriority() or sched_getscheduler, but these all require extra rights. |
631 } | 632 } |
632 | 633 |
633 bool DidProcessCrash(bool* child_exited, ProcessHandle handle) { | 634 bool DidProcessCrash(bool* child_exited, ProcessHandle handle) { |
| 635 // We call DidProcessCrash when we see an EOF from a socket to that child. |
| 636 // Sadly, the kernel is raceable: it will close a dead process's file |
| 637 // descriptors before marking the process as dead. Therefore it's possible to |
| 638 // see the EOF and still have a subsequent waitpid() return zero. (Had I |
| 639 // known that from the beginning, child processing would have been designed |
| 640 // differently.) |
| 641 // |
| 642 // Everything higher up copes with the case where waitpid fails, but we might |
| 643 // lose some crash notifications. In order to minimise this we waitpid() in a |
| 644 // loop with a timeout to try and catch the exit code. In the typical case, |
| 645 // we should only be blocking for microseconds here. |
| 646 // |
| 647 // It's possible for a bad child to close its socket without exiting. Because |
| 648 // we don't want to hang the browser in this case we have a timeout. |
634 int status; | 649 int status; |
635 const pid_t result = HANDLE_EINTR(waitpid(handle, &status, WNOHANG)); | 650 pid_t pid = WaitpidWithTimeout(handle, &status, 250 /* milliseconds */); |
636 if (result == -1) { | 651 |
637 PLOG(ERROR) << "waitpid(" << handle << ")"; | 652 if (pid == 0) { |
| 653 // We waited for the full timeout and the child still isn't dead. |
| 654 LOG(ERROR) << "We janked because we expected child " |
| 655 << handle << " to have exited."; |
638 if (child_exited) | 656 if (child_exited) |
639 *child_exited = false; | 657 *child_exited = false; |
640 return false; | 658 return false; |
641 } else if (result == 0) { | |
642 // the child hasn't exited yet. | |
643 if (child_exited) | |
644 *child_exited = false; | |
645 return false; | |
646 } | 659 } |
647 | 660 |
648 if (child_exited) | 661 if (child_exited) |
649 *child_exited = true; | 662 *child_exited = true; |
650 | 663 |
651 if (WIFSIGNALED(status)) { | 664 if (WIFSIGNALED(status)) { |
652 switch (WTERMSIG(status)) { | 665 switch (WTERMSIG(status)) { |
653 case SIGSEGV: | 666 case SIGSEGV: |
654 case SIGILL: | 667 case SIGILL: |
655 case SIGABRT: | 668 case SIGABRT: |
(...skipping 22 matching lines...) Expand all Loading... |
678 return true; | 691 return true; |
679 } | 692 } |
680 | 693 |
681 // If it didn't exit cleanly, it must have been signaled. | 694 // If it didn't exit cleanly, it must have been signaled. |
682 DCHECK(WIFSIGNALED(status)); | 695 DCHECK(WIFSIGNALED(status)); |
683 return false; | 696 return false; |
684 } | 697 } |
685 | 698 |
686 bool WaitForExitCodeWithTimeout(ProcessHandle handle, int* exit_code, | 699 bool WaitForExitCodeWithTimeout(ProcessHandle handle, int* exit_code, |
687 int64 timeout_milliseconds) { | 700 int64 timeout_milliseconds) { |
688 bool waitpid_success = false; | 701 int status; |
689 int status = WaitpidWithTimeout(handle, timeout_milliseconds, | 702 pid_t pid = WaitpidWithTimeout(handle, &status, timeout_milliseconds); |
690 &waitpid_success); | 703 |
691 if (status == -1) | 704 if (pid <= 0) |
692 return false; | |
693 if (!waitpid_success) | |
694 return false; | 705 return false; |
695 if (!WIFEXITED(status)) | 706 if (!WIFEXITED(status)) |
696 return false; | 707 return false; |
697 if (WIFSIGNALED(status)) { | 708 if (WIFSIGNALED(status)) { |
698 *exit_code = -1; | 709 *exit_code = -1; |
699 return true; | 710 return true; |
700 } | 711 } |
701 *exit_code = WEXITSTATUS(status); | 712 *exit_code = WEXITSTATUS(status); |
702 return true; | 713 return true; |
703 } | 714 } |
704 | 715 |
705 bool WaitForSingleProcess(ProcessHandle handle, int64 wait_milliseconds) { | 716 bool WaitForSingleProcess(ProcessHandle handle, int64 wait_milliseconds) { |
706 bool waitpid_success; | |
707 int status; | 717 int status; |
708 if (wait_milliseconds == base::kNoTimeout) | 718 pid_t pid; |
709 waitpid_success = (HANDLE_EINTR(waitpid(handle, &status, 0)) != -1); | 719 if (wait_milliseconds == base::kNoTimeout) { |
710 else | 720 pid = (HANDLE_EINTR(waitpid(handle, &status, 0)) == handle); |
711 status = WaitpidWithTimeout(handle, wait_milliseconds, &waitpid_success); | |
712 if (status != -1) { | |
713 DCHECK(waitpid_success); | |
714 return WIFEXITED(status); | |
715 } else { | 721 } else { |
| 722 pid = WaitpidWithTimeout(handle, &status, wait_milliseconds); |
| 723 } |
| 724 |
| 725 if (pid <= 0) |
716 return false; | 726 return false; |
| 727 |
| 728 return WIFEXITED(status); |
| 729 } |
| 730 |
| 731 bool CrashAwareSleep(ProcessHandle handle, int64 wait_milliseconds) { |
| 732 int status; |
| 733 pid_t pid = WaitpidWithTimeout(handle, &status, wait_milliseconds); |
| 734 if (pid < 0) { |
| 735 // If waitpid failed then the process probably didn't exist prior to the |
| 736 // call. |
| 737 return true; |
| 738 } else if (pid == 0) { |
| 739 return false; |
| 740 } else { |
| 741 return !(WIFEXITED(status) || WIFSIGNALED(status)); |
717 } | 742 } |
718 } | 743 } |
719 | 744 |
720 bool CrashAwareSleep(ProcessHandle handle, int64 wait_milliseconds) { | |
721 bool waitpid_success; | |
722 int status = WaitpidWithTimeout(handle, wait_milliseconds, &waitpid_success); | |
723 if (status != -1) { | |
724 DCHECK(waitpid_success); | |
725 return !(WIFEXITED(status) || WIFSIGNALED(status)); | |
726 } else { | |
727 // If waitpid returned with an error, then the process doesn't exist | |
728 // (which most probably means it didn't exist before our call). | |
729 return waitpid_success; | |
730 } | |
731 } | |
732 | |
733 int64 TimeValToMicroseconds(const struct timeval& tv) { | 745 int64 TimeValToMicroseconds(const struct timeval& tv) { |
734 static const int kMicrosecondsPerSecond = 1000000; | 746 static const int kMicrosecondsPerSecond = 1000000; |
735 int64 ret = tv.tv_sec; // Avoid (int * int) integer overflow. | 747 int64 ret = tv.tv_sec; // Avoid (int * int) integer overflow. |
736 ret *= kMicrosecondsPerSecond; | 748 ret *= kMicrosecondsPerSecond; |
737 ret += tv.tv_usec; | 749 ret += tv.tv_usec; |
738 return ret; | 750 return ret; |
739 } | 751 } |
740 | 752 |
741 // Executes the application specified by |cl| and wait for it to exit. Stores | 753 // Executes the application specified by |cl| and wait for it to exit. Stores |
742 // the output (stdout) in |output|. If |do_search_path| is set, it searches the | 754 // the output (stdout) in |output|. If |do_search_path| is set, it searches the |
(...skipping 148 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
891 const ProcessFilter* filter) { | 903 const ProcessFilter* filter) { |
892 bool exited_cleanly = | 904 bool exited_cleanly = |
893 WaitForProcessesToExit(executable_name, wait_milliseconds, | 905 WaitForProcessesToExit(executable_name, wait_milliseconds, |
894 filter); | 906 filter); |
895 if (!exited_cleanly) | 907 if (!exited_cleanly) |
896 KillProcesses(executable_name, exit_code, filter); | 908 KillProcesses(executable_name, exit_code, filter); |
897 return exited_cleanly; | 909 return exited_cleanly; |
898 } | 910 } |
899 | 911 |
900 } // namespace base | 912 } // namespace base |
OLD | NEW |