Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(348)

Side by Side Diff: chrome/browser/process_singleton_posix.cc

Issue 2871793003: Added histograms on process singleton create when remote process exists and we cannot notify it (Closed)
Patch Set: Updated NotifyOtherProcessNoSuicide test to send right histogram Created 3 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
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 93 matching lines...) Expand 10 before | Expand all | Expand 10 after
104 using content::BrowserThread; 104 using content::BrowserThread;
105 105
106 namespace { 106 namespace {
107 107
108 // Timeout for the current browser process to respond. 20 seconds should be 108 // Timeout for the current browser process to respond. 20 seconds should be
109 // enough. 109 // enough.
110 const int kTimeoutInSeconds = 20; 110 const int kTimeoutInSeconds = 20;
111 // Number of retries to notify the browser. 20 retries over 20 seconds = 1 try 111 // Number of retries to notify the browser. 20 retries over 20 seconds = 1 try
112 // per second. 112 // per second.
113 const int kRetryAttempts = 20; 113 const int kRetryAttempts = 20;
114 static bool g_disable_prompt; 114 static bool g_disable_prompt = false;
115 static bool g_skip_is_chrome_process_check = false;
gab 2017/05/15 16:58:39 static and namespace{} are redundant, remove stati
Alexey Seren 2017/05/15 19:20:25 Acknowledged.
115 const char kStartToken[] = "START"; 116 const char kStartToken[] = "START";
116 const char kACKToken[] = "ACK"; 117 const char kACKToken[] = "ACK";
117 const char kShutdownToken[] = "SHUTDOWN"; 118 const char kShutdownToken[] = "SHUTDOWN";
118 const char kTokenDelimiter = '\0'; 119 const char kTokenDelimiter = '\0';
119 const int kMaxMessageLength = 32 * 1024; 120 const int kMaxMessageLength = 32 * 1024;
120 const int kMaxACKMessageLength = arraysize(kShutdownToken) - 1; 121 const int kMaxACKMessageLength = arraysize(kShutdownToken) - 1;
121 122
122 const char kLockDelimiter = '-'; 123 const char kLockDelimiter = '-';
123 124
124 // Set the close-on-exec bit on a file descriptor. 125 // Set the close-on-exec bit on a file descriptor.
(...skipping 206 matching lines...) Expand 10 before | Expand all | Expand 10 after
331 return true; 332 return true;
332 #endif 333 #endif
333 334
334 NOTREACHED(); 335 NOTREACHED();
335 return false; 336 return false;
336 } 337 }
337 338
338 bool IsChromeProcess(pid_t pid) { 339 bool IsChromeProcess(pid_t pid) {
339 base::FilePath other_chrome_path(base::GetProcessExecutablePath(pid)); 340 base::FilePath other_chrome_path(base::GetProcessExecutablePath(pid));
340 return (!other_chrome_path.empty() && 341 return (!other_chrome_path.empty() &&
341 other_chrome_path.BaseName() == 342 (g_skip_is_chrome_process_check ||
Alexey Seren 2017/05/15 08:40:48 Do not check remote process name if g_skip_is_chro
342 base::FilePath(chrome::kBrowserProcessExecutableName)); 343 other_chrome_path.BaseName() ==
344 base::FilePath(chrome::kBrowserProcessExecutableName)));
343 } 345 }
344 346
345 // A helper class to hold onto a socket. 347 // A helper class to hold onto a socket.
346 class ScopedSocket { 348 class ScopedSocket {
347 public: 349 public:
348 ScopedSocket() : fd_(-1) { Reset(); } 350 ScopedSocket() : fd_(-1) { Reset(); }
349 ~ScopedSocket() { Close(); } 351 ~ScopedSocket() { Close(); }
350 int fd() { return fd_; } 352 int fd() { return fd_; }
351 void Reset() { 353 void Reset() {
352 Close(); 354 Close();
(...skipping 104 matching lines...) Expand 10 before | Expand all | Expand 10 after
457 // version has run. 459 // version has run.
458 if (!base::DeleteFile(lock_path, false)) { 460 if (!base::DeleteFile(lock_path, false)) {
459 PLOG(ERROR) << "Could not delete old singleton lock."; 461 PLOG(ERROR) << "Could not delete old singleton lock.";
460 return false; 462 return false;
461 } 463 }
462 464
463 return SymlinkPath(symlink_content, lock_path); 465 return SymlinkPath(symlink_content, lock_path);
464 } 466 }
465 #endif // defined(OS_MACOSX) 467 #endif // defined(OS_MACOSX)
466 468
469 void SendRemoteProcessInteractionResultHistogram(
470 ProcessSingleton::RemoteProcessInteractionResult result) {
471 UMA_HISTOGRAM_ENUMERATION(
472 "Chrome.ProcessSingleton.RemoteProcessInteractionResult", result,
473 ProcessSingleton::REMOTE_PROCESS_INTERACTION_RESULT_COUNT);
474 }
475
476 void SendRemoteHungProcessTerminateReasonHistogram(
477 ProcessSingleton::RemoteHungProcessTerminateReason reason) {
478 UMA_HISTOGRAM_ENUMERATION(
479 "Chrome.ProcessSingleton.RemoteHungProcessTerminateReason", reason,
480 ProcessSingleton::REMOTE_HUNG_PROCESS_TERMINATE_REASON_COUNT);
481 }
482
467 } // namespace 483 } // namespace
468 484
469 /////////////////////////////////////////////////////////////////////////////// 485 ///////////////////////////////////////////////////////////////////////////////
470 // ProcessSingleton::LinuxWatcher 486 // ProcessSingleton::LinuxWatcher
471 // A helper class for a Linux specific implementation of the process singleton. 487 // 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 488 // This class sets up a listener on the singleton socket and handles parsing
473 // messages that come in on the singleton socket. 489 // messages that come in on the singleton socket.
474 class ProcessSingleton::LinuxWatcher 490 class ProcessSingleton::LinuxWatcher
475 : public base::RefCountedThreadSafe<ProcessSingleton::LinuxWatcher, 491 : public base::RefCountedThreadSafe<ProcessSingleton::LinuxWatcher,
476 BrowserThread::DeleteOnIOThread> { 492 BrowserThread::DeleteOnIOThread> {
(...skipping 292 matching lines...) Expand 10 before | Expand all | Expand 10 after
769 std::string hostname; 785 std::string hostname;
770 int pid; 786 int pid;
771 if (!ParseLockPath(lock_path_, &hostname, &pid)) { 787 if (!ParseLockPath(lock_path_, &hostname, &pid)) {
772 // No lockfile exists. 788 // No lockfile exists.
773 return PROCESS_NONE; 789 return PROCESS_NONE;
774 } 790 }
775 791
776 if (hostname.empty()) { 792 if (hostname.empty()) {
777 // Invalid lockfile. 793 // Invalid lockfile.
778 UnlinkPath(lock_path_); 794 UnlinkPath(lock_path_);
795 SendRemoteProcessInteractionResultHistogram(INVALID_LOCK_FILE);
779 return PROCESS_NONE; 796 return PROCESS_NONE;
780 } 797 }
781 798
782 if (hostname != net::GetHostName() && !IsChromeProcess(pid)) { 799 if (hostname != net::GetHostName() && !IsChromeProcess(pid)) {
783 // Locked by process on another host. If the user selected to unlock 800 // Locked by process on another host. If the user selected to unlock
784 // the profile, try to continue; otherwise quit. 801 // the profile, try to continue; otherwise quit.
785 if (DisplayProfileInUseError(lock_path_, hostname, pid)) { 802 if (DisplayProfileInUseError(lock_path_, hostname, pid)) {
786 UnlinkPath(lock_path_); 803 UnlinkPath(lock_path_);
804 SendRemoteProcessInteractionResultHistogram(PROFILE_UNLOCKED);
787 return PROCESS_NONE; 805 return PROCESS_NONE;
788 } 806 }
789 return PROFILE_IN_USE; 807 return PROFILE_IN_USE;
790 } 808 }
791 809
792 if (!IsChromeProcess(pid)) { 810 if (!IsChromeProcess(pid)) {
793 // Orphaned lockfile (no process with pid, or non-chrome process.) 811 // Orphaned lockfile (no process with pid, or non-chrome process.)
794 UnlinkPath(lock_path_); 812 UnlinkPath(lock_path_);
813 SendRemoteProcessInteractionResultHistogram(ORPHANED_LOCK_FILE);
795 return PROCESS_NONE; 814 return PROCESS_NONE;
796 } 815 }
797 816
798 if (IsSameChromeInstance(pid)) { 817 if (IsSameChromeInstance(pid)) {
799 // Orphaned lockfile (pid is part of same chrome instance we are, even 818 // Orphaned lockfile (pid is part of same chrome instance we are, even
800 // though we haven't tried to create a lockfile yet). 819 // though we haven't tried to create a lockfile yet).
801 UnlinkPath(lock_path_); 820 UnlinkPath(lock_path_);
821 SendRemoteProcessInteractionResultHistogram(SAME_BROWSER_INSTANCE);
802 return PROCESS_NONE; 822 return PROCESS_NONE;
803 } 823 }
804 824
805 if (retries == retry_attempts) { 825 if (retries == retry_attempts) {
806 // Retries failed. Kill the unresponsive chrome process and continue. 826 // Retries failed. Kill the unresponsive chrome process and continue.
807 if (!kill_unresponsive || !KillProcessByLockPath()) 827 if (!kill_unresponsive || !KillProcessByLockPath())
808 return PROFILE_IN_USE; 828 return PROFILE_IN_USE;
829 SendRemoteHungProcessTerminateReasonHistogram(NOTIFY_ATTEMPTS_EXCEEDED);
809 return PROCESS_NONE; 830 return PROCESS_NONE;
810 } 831 }
811 832
812 base::PlatformThread::Sleep(sleep_interval); 833 base::PlatformThread::Sleep(sleep_interval);
813 } 834 }
814 835
815 timeval socket_timeout = TimeDeltaToTimeVal(timeout); 836 timeval socket_timeout = TimeDeltaToTimeVal(timeout);
816 setsockopt(socket.fd(), 837 setsockopt(socket.fd(),
817 SOL_SOCKET, 838 SOL_SOCKET,
818 SO_SNDTIMEO, 839 SO_SNDTIMEO,
(...skipping 15 matching lines...) Expand all
834 it != argv.end(); ++it) { 855 it != argv.end(); ++it) {
835 to_send.push_back(kTokenDelimiter); 856 to_send.push_back(kTokenDelimiter);
836 to_send.append(*it); 857 to_send.append(*it);
837 } 858 }
838 859
839 // Send the message 860 // Send the message
840 if (!WriteToSocket(socket.fd(), to_send.data(), to_send.length())) { 861 if (!WriteToSocket(socket.fd(), to_send.data(), to_send.length())) {
841 // Try to kill the other process, because it might have been dead. 862 // Try to kill the other process, because it might have been dead.
842 if (!kill_unresponsive || !KillProcessByLockPath()) 863 if (!kill_unresponsive || !KillProcessByLockPath())
843 return PROFILE_IN_USE; 864 return PROFILE_IN_USE;
865 SendRemoteHungProcessTerminateReasonHistogram(SOCKET_WRITE_FAILED);
844 return PROCESS_NONE; 866 return PROCESS_NONE;
845 } 867 }
846 868
847 if (shutdown(socket.fd(), SHUT_WR) < 0) 869 if (shutdown(socket.fd(), SHUT_WR) < 0)
848 PLOG(ERROR) << "shutdown() failed"; 870 PLOG(ERROR) << "shutdown() failed";
849 871
850 // Read ACK message from the other process. It might be blocked for a certain 872 // 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. 873 // timeout, to make sure the other process has enough time to return ACK.
852 char buf[kMaxACKMessageLength + 1]; 874 char buf[kMaxACKMessageLength + 1];
853 ssize_t len = ReadFromSocket(socket.fd(), buf, kMaxACKMessageLength, timeout); 875 ssize_t len = ReadFromSocket(socket.fd(), buf, kMaxACKMessageLength, timeout);
854 876
855 // Failed to read ACK, the other process might have been frozen. 877 // Failed to read ACK, the other process might have been frozen.
856 if (len <= 0) { 878 if (len <= 0) {
857 if (!kill_unresponsive || !KillProcessByLockPath()) 879 if (!kill_unresponsive || !KillProcessByLockPath())
858 return PROFILE_IN_USE; 880 return PROFILE_IN_USE;
881 SendRemoteHungProcessTerminateReasonHistogram(SOCKET_READ_FAILED);
859 return PROCESS_NONE; 882 return PROCESS_NONE;
860 } 883 }
861 884
862 buf[len] = '\0'; 885 buf[len] = '\0';
863 if (strncmp(buf, kShutdownToken, arraysize(kShutdownToken) - 1) == 0) { 886 if (strncmp(buf, kShutdownToken, arraysize(kShutdownToken) - 1) == 0) {
864 // The other process is shutting down, it's safe to start a new process. 887 // The other process is shutting down, it's safe to start a new process.
888 SendRemoteProcessInteractionResultHistogram(REMOTE_PROCESS_SHUTTING_DOWN);
865 return PROCESS_NONE; 889 return PROCESS_NONE;
866 } else if (strncmp(buf, kACKToken, arraysize(kACKToken) - 1) == 0) { 890 } else if (strncmp(buf, kACKToken, arraysize(kACKToken) - 1) == 0) {
867 #if defined(TOOLKIT_VIEWS) && defined(OS_LINUX) && !defined(OS_CHROMEOS) 891 #if defined(TOOLKIT_VIEWS) && defined(OS_LINUX) && !defined(OS_CHROMEOS)
868 // Likely NULL in unit tests. 892 // Likely NULL in unit tests.
869 views::LinuxUI* linux_ui = views::LinuxUI::instance(); 893 views::LinuxUI* linux_ui = views::LinuxUI::instance();
870 if (linux_ui) 894 if (linux_ui)
871 linux_ui->NotifyWindowManagerStartupComplete(); 895 linux_ui->NotifyWindowManagerStartupComplete();
872 #endif 896 #endif
873 897
874 // Assume the other process is handling the request. 898 // Assume the other process is handling the request.
(...skipping 63 matching lines...) Expand 10 before | Expand all | Expand 10 after
938 962
939 void ProcessSingleton::OverrideKillCallbackForTesting( 963 void ProcessSingleton::OverrideKillCallbackForTesting(
940 const base::Callback<void(int)>& callback) { 964 const base::Callback<void(int)>& callback) {
941 kill_callback_ = callback; 965 kill_callback_ = callback;
942 } 966 }
943 967
944 void ProcessSingleton::DisablePromptForTesting() { 968 void ProcessSingleton::DisablePromptForTesting() {
945 g_disable_prompt = true; 969 g_disable_prompt = true;
946 } 970 }
947 971
972 void ProcessSingleton::SkipIsChromeProcessCheckForTesting() {
973 g_skip_is_chrome_process_check = true;
974 }
975
948 bool ProcessSingleton::Create() { 976 bool ProcessSingleton::Create() {
949 int sock; 977 int sock;
950 sockaddr_un addr; 978 sockaddr_un addr;
951 979
952 // The symlink lock is pointed to the hostname and process id, so other 980 // The symlink lock is pointed to the hostname and process id, so other
953 // processes can find it out. 981 // processes can find it out.
954 base::FilePath symlink_content(base::StringPrintf( 982 base::FilePath symlink_content(base::StringPrintf(
955 "%s%c%u", 983 "%s%c%u",
956 net::GetHostName().c_str(), 984 net::GetHostName().c_str(),
957 kLockDelimiter, 985 kLockDelimiter,
(...skipping 92 matching lines...) Expand 10 before | Expand all | Expand 10 after
1050 } 1078 }
1051 return true; 1079 return true;
1052 } 1080 }
1053 1081
1054 bool ProcessSingleton::KillProcessByLockPath() { 1082 bool ProcessSingleton::KillProcessByLockPath() {
1055 std::string hostname; 1083 std::string hostname;
1056 int pid; 1084 int pid;
1057 ParseLockPath(lock_path_, &hostname, &pid); 1085 ParseLockPath(lock_path_, &hostname, &pid);
1058 1086
1059 if (!hostname.empty() && hostname != net::GetHostName()) { 1087 if (!hostname.empty() && hostname != net::GetHostName()) {
1060 return DisplayProfileInUseError(lock_path_, hostname, pid); 1088 bool res = DisplayProfileInUseError(lock_path_, hostname, pid);
1089 if (res)
1090 SendRemoteProcessInteractionResultHistogram(PROFILE_UNLOCKED_BEFORE_KILL);
1091 return res;
1061 } 1092 }
1062 UnlinkPath(lock_path_); 1093 UnlinkPath(lock_path_);
1063 1094
1064 if (IsSameChromeInstance(pid)) 1095 if (IsSameChromeInstance(pid)) {
1096 SendRemoteProcessInteractionResultHistogram(
1097 SAME_BROWSER_INSTANCE_BEFORE_KILL);
1065 return true; 1098 return true;
1099 }
1066 1100
1067 if (pid > 0) { 1101 if (pid > 0) {
1068 kill_callback_.Run(pid); 1102 kill_callback_.Run(pid);
1069 return true; 1103 return true;
1070 } 1104 }
1071 1105
1106 SendRemoteProcessInteractionResultHistogram(FAILED_TO_EXTRACT_PID);
1107
1072 LOG(ERROR) << "Failed to extract pid from path: " << lock_path_.value(); 1108 LOG(ERROR) << "Failed to extract pid from path: " << lock_path_.value();
1073 return true; 1109 return true;
1074 } 1110 }
1075 1111
1076 void ProcessSingleton::KillProcess(int pid) { 1112 void ProcessSingleton::KillProcess(int pid) {
1077 // TODO(james.su@gmail.com): Is SIGKILL ok? 1113 // TODO(james.su@gmail.com): Is SIGKILL ok?
1078 int rv = kill(static_cast<base::ProcessHandle>(pid), SIGKILL); 1114 int rv = kill(static_cast<base::ProcessHandle>(pid), SIGKILL);
1079 // ESRCH = No Such Process (can happen if the other process is already in 1115 // 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). 1116 // progress of shutting down and finishes before we try to kill it).
1081 DCHECK(rv == 0 || errno == ESRCH) << "Error killing process: " 1117 DCHECK(rv == 0 || errno == ESRCH) << "Error killing process: "
1082 << base::safe_strerror(errno); 1118 << base::safe_strerror(errno);
1119
1120 int error_code = (rv == 0) ? 0 : errno;
1121 UMA_HISTOGRAM_SPARSE_SLOWLY(
1122 "Chrome.ProcessSingleton.TerminateProcessErrorCode.Posix", error_code);
1123
1124 RemoteProcessInteractionResult action = TERMINATE_SUCCEEDED;
1125 if (rv != 0) {
1126 switch (error_code) {
1127 case ESRCH:
1128 action = REMOTE_PROCESS_NOT_FOUND;
1129 break;
1130 case EPERM:
1131 action = TERMINATE_NOT_ENOUGH_PERMISSIONS;
1132 break;
1133 default:
1134 action = TERMINATE_FAILED;
1135 break;
1136 }
1137 }
1138 SendRemoteProcessInteractionResultHistogram(action);
1083 } 1139 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698