| 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 |