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

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

Powered by Google App Engine
This is Rietveld 408576698