OLD | NEW |
---|---|
1 // Copyright 2014 The Chromium Authors. All rights reserved. | 1 // Copyright 2014 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 // On Linux, when the user tries to launch a second copy of chrome, we check | 5 // On Linux, when the user tries to launch a second copy of chrome, we check |
6 // for a socket in the user's profile directory. If the socket file is open we | 6 // for a socket in the user's profile directory. If the socket file is open we |
7 // send a message to the first chrome browser process with the current | 7 // send a message to the first chrome browser process with the current |
8 // directory and second process command line flags. The second process then | 8 // directory and second process command line flags. The second process then |
9 // exits. | 9 // exits. |
10 // | 10 // |
(...skipping 446 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
457 // version has run. | 457 // version has run. |
458 if (!base::DeleteFile(lock_path, false)) { | 458 if (!base::DeleteFile(lock_path, false)) { |
459 PLOG(ERROR) << "Could not delete old singleton lock."; | 459 PLOG(ERROR) << "Could not delete old singleton lock."; |
460 return false; | 460 return false; |
461 } | 461 } |
462 | 462 |
463 return SymlinkPath(symlink_content, lock_path); | 463 return SymlinkPath(symlink_content, lock_path); |
464 } | 464 } |
465 #endif // defined(OS_MACOSX) | 465 #endif // defined(OS_MACOSX) |
466 | 466 |
467 void SendRemoteProcessInteractionResultHistogram( | |
468 ProcessSingleton::RemoteProcessInteractionResult result) { | |
469 UMA_HISTOGRAM_ENUMERATION( | |
470 "Chrome.ProcessSingleton.RemoteProcessInteractionResult", result, | |
471 ProcessSingleton::REMOTE_PROCESS_INTERACTION_RESULT_COUNT); | |
472 } | |
473 | |
474 void SendRemoteHungProcessTerminateReasonHistogram( | |
475 ProcessSingleton::RemoteHungProcessTerminateReason reason) { | |
476 UMA_HISTOGRAM_ENUMERATION( | |
477 "Chrome.ProcessSingleton.RemoteHungProcessTerminateReason", reason, | |
478 ProcessSingleton::REMOTE_HUNG_PROCESS_TERMINATE_REASON_COUNT); | |
479 } | |
480 | |
467 } // namespace | 481 } // namespace |
468 | 482 |
469 /////////////////////////////////////////////////////////////////////////////// | 483 /////////////////////////////////////////////////////////////////////////////// |
470 // ProcessSingleton::LinuxWatcher | 484 // ProcessSingleton::LinuxWatcher |
471 // A helper class for a Linux specific implementation of the process singleton. | 485 // A helper class for a Linux specific implementation of the process singleton. |
472 // This class sets up a listener on the singleton socket and handles parsing | 486 // This class sets up a listener on the singleton socket and handles parsing |
473 // messages that come in on the singleton socket. | 487 // messages that come in on the singleton socket. |
474 class ProcessSingleton::LinuxWatcher | 488 class ProcessSingleton::LinuxWatcher |
475 : public base::RefCountedThreadSafe<ProcessSingleton::LinuxWatcher, | 489 : public base::RefCountedThreadSafe<ProcessSingleton::LinuxWatcher, |
476 BrowserThread::DeleteOnIOThread> { | 490 BrowserThread::DeleteOnIOThread> { |
(...skipping 292 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
769 std::string hostname; | 783 std::string hostname; |
770 int pid; | 784 int pid; |
771 if (!ParseLockPath(lock_path_, &hostname, &pid)) { | 785 if (!ParseLockPath(lock_path_, &hostname, &pid)) { |
772 // No lockfile exists. | 786 // No lockfile exists. |
773 return PROCESS_NONE; | 787 return PROCESS_NONE; |
774 } | 788 } |
775 | 789 |
776 if (hostname.empty()) { | 790 if (hostname.empty()) { |
777 // Invalid lockfile. | 791 // Invalid lockfile. |
778 UnlinkPath(lock_path_); | 792 UnlinkPath(lock_path_); |
793 SendRemoteProcessInteractionResultHistogram(INVALID_LOCK_FILE); | |
779 return PROCESS_NONE; | 794 return PROCESS_NONE; |
780 } | 795 } |
781 | 796 |
782 if (hostname != net::GetHostName() && !IsChromeProcess(pid)) { | 797 if (hostname != net::GetHostName() && !IsChromeProcess(pid)) { |
783 // Locked by process on another host. If the user selected to unlock | 798 // Locked by process on another host. If the user selected to unlock |
784 // the profile, try to continue; otherwise quit. | 799 // the profile, try to continue; otherwise quit. |
785 if (DisplayProfileInUseError(lock_path_, hostname, pid)) { | 800 if (DisplayProfileInUseError(lock_path_, hostname, pid)) { |
786 UnlinkPath(lock_path_); | 801 UnlinkPath(lock_path_); |
802 SendRemoteProcessInteractionResultHistogram(PROFILE_UNLOCKED); | |
787 return PROCESS_NONE; | 803 return PROCESS_NONE; |
788 } | 804 } |
789 return PROFILE_IN_USE; | 805 return PROFILE_IN_USE; |
790 } | 806 } |
791 | 807 |
792 if (!IsChromeProcess(pid)) { | 808 if (!IsChromeProcess(pid)) { |
793 // Orphaned lockfile (no process with pid, or non-chrome process.) | 809 // Orphaned lockfile (no process with pid, or non-chrome process.) |
794 UnlinkPath(lock_path_); | 810 UnlinkPath(lock_path_); |
811 SendRemoteProcessInteractionResultHistogram(ORPHANED_LOCK_FILE); | |
795 return PROCESS_NONE; | 812 return PROCESS_NONE; |
796 } | 813 } |
797 | 814 |
798 if (IsSameChromeInstance(pid)) { | 815 if (IsSameChromeInstance(pid)) { |
799 // Orphaned lockfile (pid is part of same chrome instance we are, even | 816 // Orphaned lockfile (pid is part of same chrome instance we are, even |
800 // though we haven't tried to create a lockfile yet). | 817 // though we haven't tried to create a lockfile yet). |
801 UnlinkPath(lock_path_); | 818 UnlinkPath(lock_path_); |
819 SendRemoteProcessInteractionResultHistogram(SAME_BROWSER_INSTANCE); | |
802 return PROCESS_NONE; | 820 return PROCESS_NONE; |
803 } | 821 } |
804 | 822 |
805 if (retries == retry_attempts) { | 823 if (retries == retry_attempts) { |
806 // Retries failed. Kill the unresponsive chrome process and continue. | 824 // Retries failed. Kill the unresponsive chrome process and continue. |
807 if (!kill_unresponsive || !KillProcessByLockPath()) | 825 if (!kill_unresponsive || !KillProcessByLockPath()) |
808 return PROFILE_IN_USE; | 826 return PROFILE_IN_USE; |
827 SendRemoteHungProcessTerminateReasonHistogram(NOTIFY_ATTEMPTS_EXCEEDED); | |
809 return PROCESS_NONE; | 828 return PROCESS_NONE; |
810 } | 829 } |
811 | 830 |
812 base::PlatformThread::Sleep(sleep_interval); | 831 base::PlatformThread::Sleep(sleep_interval); |
813 } | 832 } |
814 | 833 |
815 timeval socket_timeout = TimeDeltaToTimeVal(timeout); | 834 timeval socket_timeout = TimeDeltaToTimeVal(timeout); |
816 setsockopt(socket.fd(), | 835 setsockopt(socket.fd(), |
817 SOL_SOCKET, | 836 SOL_SOCKET, |
818 SO_SNDTIMEO, | 837 SO_SNDTIMEO, |
(...skipping 15 matching lines...) Expand all Loading... | |
834 it != argv.end(); ++it) { | 853 it != argv.end(); ++it) { |
835 to_send.push_back(kTokenDelimiter); | 854 to_send.push_back(kTokenDelimiter); |
836 to_send.append(*it); | 855 to_send.append(*it); |
837 } | 856 } |
838 | 857 |
839 // Send the message | 858 // Send the message |
840 if (!WriteToSocket(socket.fd(), to_send.data(), to_send.length())) { | 859 if (!WriteToSocket(socket.fd(), to_send.data(), to_send.length())) { |
841 // Try to kill the other process, because it might have been dead. | 860 // Try to kill the other process, because it might have been dead. |
842 if (!kill_unresponsive || !KillProcessByLockPath()) | 861 if (!kill_unresponsive || !KillProcessByLockPath()) |
843 return PROFILE_IN_USE; | 862 return PROFILE_IN_USE; |
863 SendRemoteHungProcessTerminateReasonHistogram(SOCKET_WRITE_FAILED); | |
844 return PROCESS_NONE; | 864 return PROCESS_NONE; |
845 } | 865 } |
846 | 866 |
847 if (shutdown(socket.fd(), SHUT_WR) < 0) | 867 if (shutdown(socket.fd(), SHUT_WR) < 0) |
848 PLOG(ERROR) << "shutdown() failed"; | 868 PLOG(ERROR) << "shutdown() failed"; |
849 | 869 |
850 // Read ACK message from the other process. It might be blocked for a certain | 870 // Read ACK message from the other process. It might be blocked for a certain |
851 // timeout, to make sure the other process has enough time to return ACK. | 871 // timeout, to make sure the other process has enough time to return ACK. |
852 char buf[kMaxACKMessageLength + 1]; | 872 char buf[kMaxACKMessageLength + 1]; |
853 ssize_t len = ReadFromSocket(socket.fd(), buf, kMaxACKMessageLength, timeout); | 873 ssize_t len = ReadFromSocket(socket.fd(), buf, kMaxACKMessageLength, timeout); |
854 | 874 |
855 // Failed to read ACK, the other process might have been frozen. | 875 // Failed to read ACK, the other process might have been frozen. |
856 if (len <= 0) { | 876 if (len <= 0) { |
857 if (!kill_unresponsive || !KillProcessByLockPath()) | 877 if (!kill_unresponsive || !KillProcessByLockPath()) |
858 return PROFILE_IN_USE; | 878 return PROFILE_IN_USE; |
879 SendRemoteHungProcessTerminateReasonHistogram(SOCKET_READ_FAILED); | |
859 return PROCESS_NONE; | 880 return PROCESS_NONE; |
860 } | 881 } |
861 | 882 |
862 buf[len] = '\0'; | 883 buf[len] = '\0'; |
863 if (strncmp(buf, kShutdownToken, arraysize(kShutdownToken) - 1) == 0) { | 884 if (strncmp(buf, kShutdownToken, arraysize(kShutdownToken) - 1) == 0) { |
864 // The other process is shutting down, it's safe to start a new process. | 885 // The other process is shutting down, it's safe to start a new process. |
886 SendRemoteProcessInteractionResultHistogram(REMOTE_PROCESS_SHUTTING_DOWN); | |
865 return PROCESS_NONE; | 887 return PROCESS_NONE; |
866 } else if (strncmp(buf, kACKToken, arraysize(kACKToken) - 1) == 0) { | 888 } else if (strncmp(buf, kACKToken, arraysize(kACKToken) - 1) == 0) { |
867 #if defined(TOOLKIT_VIEWS) && defined(OS_LINUX) && !defined(OS_CHROMEOS) | 889 #if defined(TOOLKIT_VIEWS) && defined(OS_LINUX) && !defined(OS_CHROMEOS) |
868 // Likely NULL in unit tests. | 890 // Likely NULL in unit tests. |
869 views::LinuxUI* linux_ui = views::LinuxUI::instance(); | 891 views::LinuxUI* linux_ui = views::LinuxUI::instance(); |
870 if (linux_ui) | 892 if (linux_ui) |
871 linux_ui->NotifyWindowManagerStartupComplete(); | 893 linux_ui->NotifyWindowManagerStartupComplete(); |
872 #endif | 894 #endif |
873 | 895 |
874 // Assume the other process is handling the request. | 896 // Assume the other process is handling the request. |
(...skipping 175 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1050 } | 1072 } |
1051 return true; | 1073 return true; |
1052 } | 1074 } |
1053 | 1075 |
1054 bool ProcessSingleton::KillProcessByLockPath() { | 1076 bool ProcessSingleton::KillProcessByLockPath() { |
1055 std::string hostname; | 1077 std::string hostname; |
1056 int pid; | 1078 int pid; |
1057 ParseLockPath(lock_path_, &hostname, &pid); | 1079 ParseLockPath(lock_path_, &hostname, &pid); |
1058 | 1080 |
1059 if (!hostname.empty() && hostname != net::GetHostName()) { | 1081 if (!hostname.empty() && hostname != net::GetHostName()) { |
1060 return DisplayProfileInUseError(lock_path_, hostname, pid); | 1082 bool res = DisplayProfileInUseError(lock_path_, hostname, pid); |
1083 if (res) | |
1084 SendRemoteProcessInteractionResultHistogram(PROFILE_UNLOCKED); | |
gab
2017/05/12 18:40:59
Feels wrong to share histogram for two different c
Alexey Seren
2017/05/15 08:40:48
Acknowledged.
| |
1085 return res; | |
1061 } | 1086 } |
1062 UnlinkPath(lock_path_); | 1087 UnlinkPath(lock_path_); |
1063 | 1088 |
1064 if (IsSameChromeInstance(pid)) | 1089 if (IsSameChromeInstance(pid)) { |
1090 SendRemoteProcessInteractionResultHistogram(SAME_BROWSER_INSTANCE); | |
gab
2017/05/12 18:40:59
ditto
Alexey Seren
2017/05/15 08:40:48
Acknowledged.
| |
1065 return true; | 1091 return true; |
1092 } | |
1066 | 1093 |
1067 if (pid > 0) { | 1094 if (pid > 0) { |
1068 kill_callback_.Run(pid); | 1095 kill_callback_.Run(pid); |
1069 return true; | 1096 return true; |
1070 } | 1097 } |
1071 | 1098 |
1099 SendRemoteProcessInteractionResultHistogram(FAILED_TO_EXTRACT_PID); | |
1100 | |
1072 LOG(ERROR) << "Failed to extract pid from path: " << lock_path_.value(); | 1101 LOG(ERROR) << "Failed to extract pid from path: " << lock_path_.value(); |
1073 return true; | 1102 return true; |
1074 } | 1103 } |
1075 | 1104 |
1076 void ProcessSingleton::KillProcess(int pid) { | 1105 void ProcessSingleton::KillProcess(int pid) { |
1077 // TODO(james.su@gmail.com): Is SIGKILL ok? | 1106 // TODO(james.su@gmail.com): Is SIGKILL ok? |
1078 int rv = kill(static_cast<base::ProcessHandle>(pid), SIGKILL); | 1107 int rv = kill(static_cast<base::ProcessHandle>(pid), SIGKILL); |
1079 // ESRCH = No Such Process (can happen if the other process is already in | 1108 // ESRCH = No Such Process (can happen if the other process is already in |
1080 // progress of shutting down and finishes before we try to kill it). | 1109 // progress of shutting down and finishes before we try to kill it). |
1081 DCHECK(rv == 0 || errno == ESRCH) << "Error killing process: " | 1110 DCHECK(rv == 0 || errno == ESRCH) << "Error killing process: " |
1082 << base::safe_strerror(errno); | 1111 << base::safe_strerror(errno); |
1112 | |
1113 int error_code = (rv == 0) ? 0 : errno; | |
1114 UMA_HISTOGRAM_SPARSE_SLOWLY( | |
1115 "Chrome.ProcessSingleton.TerminateProcessErrorCode.Posix", error_code); | |
1116 | |
1117 RemoteProcessInteractionResult action = TERMINATE_SUCCEEDED; | |
1118 if (rv != 0) { | |
1119 switch (error_code) { | |
1120 case ESRCH: | |
1121 action = REMOTE_PROCESS_NOT_FOUND; | |
1122 break; | |
1123 case EPERM: | |
1124 action = TERMINATE_NOT_ENOUGH_PERMISSIONS; | |
1125 break; | |
1126 default: | |
1127 action = TERMINATE_FAILED; | |
1128 break; | |
1129 } | |
1130 } | |
1131 SendRemoteProcessInteractionResultHistogram(action); | |
1083 } | 1132 } |
OLD | NEW |