Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 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 "chrome/browser/process_singleton.h" | 5 #include "chrome/browser/process_singleton.h" |
| 6 | 6 |
| 7 #include <windows.h> | 7 #include <windows.h> |
| 8 #include <shellapi.h> | 8 #include <shellapi.h> |
| 9 #include <stddef.h> | 9 #include <stddef.h> |
| 10 | 10 |
| 11 #include "base/base_paths.h" | 11 #include "base/base_paths.h" |
| 12 #include "base/bind.h" | 12 #include "base/bind.h" |
| 13 #include "base/command_line.h" | 13 #include "base/command_line.h" |
| 14 #include "base/debug/activity_tracker.h" | |
| 14 #include "base/files/file_path.h" | 15 #include "base/files/file_path.h" |
| 15 #include "base/macros.h" | 16 #include "base/macros.h" |
| 16 #include "base/metrics/histogram_macros.h" | 17 #include "base/metrics/histogram_macros.h" |
| 17 #include "base/process/process.h" | 18 #include "base/process/process.h" |
| 18 #include "base/process/process_info.h" | 19 #include "base/process/process_info.h" |
| 19 #include "base/strings/string_number_conversions.h" | 20 #include "base/strings/string_number_conversions.h" |
| 20 #include "base/strings/utf_string_conversions.h" | 21 #include "base/strings/utf_string_conversions.h" |
| 21 #include "base/time/time.h" | 22 #include "base/time/time.h" |
| 22 #include "base/win/registry.h" | 23 #include "base/win/registry.h" |
| 23 #include "base/win/scoped_handle.h" | 24 #include "base/win/scoped_handle.h" |
| (...skipping 147 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 171 return true; | 172 return true; |
| 172 } | 173 } |
| 173 | 174 |
| 174 bool DisplayShouldKillMessageBox() { | 175 bool DisplayShouldKillMessageBox() { |
| 175 return chrome::ShowQuestionMessageBox( | 176 return chrome::ShowQuestionMessageBox( |
| 176 NULL, l10n_util::GetStringUTF16(IDS_PRODUCT_NAME), | 177 NULL, l10n_util::GetStringUTF16(IDS_PRODUCT_NAME), |
| 177 l10n_util::GetStringUTF16(IDS_BROWSER_HUNGBROWSER_MESSAGE)) != | 178 l10n_util::GetStringUTF16(IDS_BROWSER_HUNGBROWSER_MESSAGE)) != |
| 178 chrome::MESSAGE_BOX_RESULT_NO; | 179 chrome::MESSAGE_BOX_RESULT_NO; |
| 179 } | 180 } |
| 180 | 181 |
| 182 // Function was copied from Process::Terminate. | |
| 183 void TerminateProcessWithHistograms(const base::Process& process, | |
| 184 int exit_code) { | |
| 185 DCHECK(process.IsValid()); | |
| 186 base::TimeTicks start_time = base::TimeTicks::Now(); | |
| 187 bool result = (::TerminateProcess(process.Handle(), exit_code) != FALSE); | |
| 188 DWORD terminate_error = 0; | |
| 189 if (result) { | |
| 190 DWORD wait_error = 0; | |
| 191 // The process may not end immediately due to pending I/O | |
| 192 DWORD wait_result = ::WaitForSingleObject(process.Handle(), 60 * 1000); | |
| 193 if (wait_result != WAIT_OBJECT_0) { | |
| 194 if (wait_result == WAIT_FAILED) | |
| 195 wait_error = ::GetLastError(); | |
| 196 UMA_HISTOGRAM_ENUMERATION( | |
| 197 "Chrome.ProcessSingleton.RemoteProcessInteractionResult", | |
| 198 ProcessSingleton::TERMINATE_WAIT_TIMEOUT, | |
| 199 ProcessSingleton::MAX_HUNG_REMOTE_PROCESS_ACTION); | |
| 200 DPLOG(ERROR) << "Error waiting for process exit"; | |
| 201 } else { | |
| 202 UMA_HISTOGRAM_ENUMERATION( | |
| 203 "Chrome.ProcessSingleton.RemoteProcessInteractionResult", | |
| 204 ProcessSingleton::TERMINATE_SUCCEEDED, | |
| 205 ProcessSingleton::MAX_HUNG_REMOTE_PROCESS_ACTION); | |
| 206 } | |
| 207 UMA_HISTOGRAM_TIMES("Chrome.ProcessSingleton.TerminateProcessTime", | |
| 208 base::TimeTicks::Now() - start_time); | |
| 209 UMA_HISTOGRAM_SPARSE_SLOWLY( | |
| 210 "Chrome.ProcessSingleton.TerminationWaitErrorCode.Windows", wait_error); | |
| 211 base::debug::GlobalActivityTracker::RecordProcessExitIfEnabled( | |
| 212 process.Pid(), exit_code); | |
| 213 } else { | |
| 214 terminate_error = ::GetLastError(); | |
| 215 UMA_HISTOGRAM_ENUMERATION( | |
| 216 "Chrome.ProcessSingleton.RemoteProcessInteractionResult", | |
| 217 ProcessSingleton::TERMINATE_FAILED, | |
| 218 ProcessSingleton::MAX_HUNG_REMOTE_PROCESS_ACTION); | |
| 219 DPLOG(ERROR) << "Unable to terminate process"; | |
| 220 } | |
| 221 UMA_HISTOGRAM_SPARSE_SLOWLY( | |
| 222 "Chrome.ProcessSingleton.ProcessTerminateErrorCode.Windows", | |
|
gab
2017/05/10 15:16:50
These histograms will be examined filtered per pla
Alexey Seren
2017/05/11 13:42:05
Unfortunately we cannot do it because each platfor
gab
2017/05/11 15:10:03
s/OSAgnosticErrno/PosixErrno/ (maybe there's alrea
| |
| 223 terminate_error); | |
| 224 } | |
| 225 | |
| 181 } // namespace | 226 } // namespace |
| 182 | 227 |
| 183 // Microsoft's Softricity virtualization breaks the sandbox processes. | 228 // Microsoft's Softricity virtualization breaks the sandbox processes. |
| 184 // So, if we detect the Softricity DLL we use WMI Win32_Process.Create to | 229 // So, if we detect the Softricity DLL we use WMI Win32_Process.Create to |
| 185 // break out of the virtualization environment. | 230 // break out of the virtualization environment. |
| 186 // http://code.google.com/p/chromium/issues/detail?id=43650 | 231 // http://code.google.com/p/chromium/issues/detail?id=43650 |
| 187 bool ProcessSingleton::EscapeVirtualization( | 232 bool ProcessSingleton::EscapeVirtualization( |
| 188 const base::FilePath& user_data_dir) { | 233 const base::FilePath& user_data_dir) { |
| 189 if (::GetModuleHandle(L"sftldr_wow64.dll") || | 234 if (::GetModuleHandle(L"sftldr_wow64.dll") || |
| 190 ::GetModuleHandle(L"sftldr.dll")) { | 235 ::GetModuleHandle(L"sftldr.dll")) { |
| (...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 235 return LOCK_ERROR; | 280 return LOCK_ERROR; |
| 236 } else if (!remote_window_) { | 281 } else if (!remote_window_) { |
| 237 return PROCESS_NONE; | 282 return PROCESS_NONE; |
| 238 } | 283 } |
| 239 | 284 |
| 240 switch (chrome::AttemptToNotifyRunningChrome(remote_window_, false)) { | 285 switch (chrome::AttemptToNotifyRunningChrome(remote_window_, false)) { |
| 241 case chrome::NOTIFY_SUCCESS: | 286 case chrome::NOTIFY_SUCCESS: |
| 242 return PROCESS_NOTIFIED; | 287 return PROCESS_NOTIFIED; |
| 243 case chrome::NOTIFY_FAILED: | 288 case chrome::NOTIFY_FAILED: |
| 244 remote_window_ = NULL; | 289 remote_window_ = NULL; |
| 290 UMA_HISTOGRAM_ENUMERATION( | |
| 291 "Chrome.ProcessSingleton.RemoteProcessInteractionResult", | |
| 292 RUNNING_PROCESS_NOTIFY_FAILED, MAX_HUNG_REMOTE_PROCESS_ACTION); | |
| 245 return PROCESS_NONE; | 293 return PROCESS_NONE; |
| 246 case chrome::NOTIFY_WINDOW_HUNG: | 294 case chrome::NOTIFY_WINDOW_HUNG: |
| 247 // Fall through and potentially terminate the hung browser. | 295 // Fall through and potentially terminate the hung browser. |
| 248 break; | 296 break; |
| 249 } | 297 } |
| 250 | 298 |
| 251 // The window is hung. | 299 // The window is hung. |
| 252 DWORD process_id = 0; | 300 DWORD process_id = 0; |
| 253 DWORD thread_id = ::GetWindowThreadProcessId(remote_window_, &process_id); | 301 DWORD thread_id = ::GetWindowThreadProcessId(remote_window_, &process_id); |
| 254 if (!thread_id || !process_id) { | 302 if (!thread_id || !process_id) { |
| 303 UMA_HISTOGRAM_ENUMERATION( | |
| 304 "Chrome.ProcessSingleton.RemoteProcessInteractionResult", | |
| 305 REMOTE_PROCESS_NOT_FOUND, MAX_HUNG_REMOTE_PROCESS_ACTION); | |
| 255 remote_window_ = NULL; | 306 remote_window_ = NULL; |
| 256 return PROCESS_NONE; | 307 return PROCESS_NONE; |
| 257 } | 308 } |
| 258 | 309 |
| 259 // Get a handle to the process that created the window. | 310 // Get a handle to the process that created the window. |
| 260 base::Process process = base::Process::Open(process_id); | 311 base::Process process = base::Process::Open(process_id); |
| 261 | 312 |
| 262 // TODO(manzagop): capture a hang report. | 313 // TODO(manzagop): capture a hang report. |
| 263 | 314 |
| 264 // Scan for every window to find a visible one. | 315 // Scan for every window to find a visible one. |
| 265 bool visible_window = false; | 316 bool visible_window = false; |
| 266 ::EnumThreadWindows(thread_id, | 317 ::EnumThreadWindows(thread_id, |
| 267 &BrowserWindowEnumeration, | 318 &BrowserWindowEnumeration, |
| 268 reinterpret_cast<LPARAM>(&visible_window)); | 319 reinterpret_cast<LPARAM>(&visible_window)); |
| 269 | 320 |
| 270 // If there is a visible browser window, ask the user before killing it. | 321 // If there is a visible browser window, ask the user before killing it. |
| 271 if (visible_window && !should_kill_remote_process_callback_.Run()) { | 322 if (visible_window && !should_kill_remote_process_callback_.Run()) { |
| 272 // The user denied. Quit silently. | 323 // The user denied. Quit silently. |
| 273 return PROCESS_NOTIFIED; | 324 return PROCESS_NOTIFIED; |
| 274 } | 325 } |
| 326 UMA_HISTOGRAM_ENUMERATION( | |
| 327 "Chrome.ProcessSingleton.RemoteHungProcessTerminateReason", | |
| 328 visible_window ? USER_ACCEPTED_TERMINATION : NO_VISIBLE_WINDOW_FOUND, | |
| 329 MAX_REMOTE_HUNG_PROCESS_TERMINATE_REASON); | |
| 275 | 330 |
| 276 // Time to take action. Kill the browser process. | 331 // Time to take action. Kill the browser process. |
| 277 process.Terminate(content::RESULT_CODE_HUNG, true); | 332 TerminateProcessWithHistograms(process, content::RESULT_CODE_HUNG); |
| 333 | |
| 278 remote_window_ = NULL; | 334 remote_window_ = NULL; |
| 279 return PROCESS_NONE; | 335 return PROCESS_NONE; |
| 280 } | 336 } |
| 281 | 337 |
| 282 ProcessSingleton::NotifyResult | 338 ProcessSingleton::NotifyResult |
| 283 ProcessSingleton::NotifyOtherProcessOrCreate() { | 339 ProcessSingleton::NotifyOtherProcessOrCreate() { |
| 284 const base::TimeTicks begin_ticks = base::TimeTicks::Now(); | 340 const base::TimeTicks begin_ticks = base::TimeTicks::Now(); |
| 285 for (int i = 0; i < 2; ++i) { | 341 for (int i = 0; i < 2; ++i) { |
| 286 if (Create()) { | 342 if (Create()) { |
| 287 UMA_HISTOGRAM_MEDIUM_TIMES("Chrome.ProcessSingleton.TimeToCreate", | 343 UMA_HISTOGRAM_MEDIUM_TIMES("Chrome.ProcessSingleton.TimeToCreate", |
| (...skipping 83 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 371 return window_.hwnd() != NULL; | 427 return window_.hwnd() != NULL; |
| 372 } | 428 } |
| 373 | 429 |
| 374 void ProcessSingleton::Cleanup() { | 430 void ProcessSingleton::Cleanup() { |
| 375 } | 431 } |
| 376 | 432 |
| 377 void ProcessSingleton::OverrideShouldKillRemoteProcessCallbackForTesting( | 433 void ProcessSingleton::OverrideShouldKillRemoteProcessCallbackForTesting( |
| 378 const ShouldKillRemoteProcessCallback& display_dialog_callback) { | 434 const ShouldKillRemoteProcessCallback& display_dialog_callback) { |
| 379 should_kill_remote_process_callback_ = display_dialog_callback; | 435 should_kill_remote_process_callback_ = display_dialog_callback; |
| 380 } | 436 } |
| OLD | NEW |