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 void SendRemoteProcessInteractionResultHistogram( | |
183 ProcessSingleton::RemoteProcessInteractionResult result) { | |
184 UMA_HISTOGRAM_ENUMERATION( | |
185 "Chrome.ProcessSingleton.RemoteProcessInteractionResult", result, | |
186 ProcessSingleton::REMOTE_PROCESS_INTERACTION_RESULT_COUNT); | |
187 } | |
188 | |
189 // Function was copied from Process::Terminate. | |
190 void TerminateProcessWithHistograms(const base::Process& process, | |
191 int exit_code) { | |
192 DCHECK(process.IsValid()); | |
193 base::TimeTicks start_time = base::TimeTicks::Now(); | |
194 bool result = (::TerminateProcess(process.Handle(), exit_code) != FALSE); | |
195 DWORD terminate_error = 0; | |
196 if (result) { | |
197 DWORD wait_error = 0; | |
198 // The process may not end immediately due to pending I/O | |
199 DWORD wait_result = ::WaitForSingleObject(process.Handle(), 60 * 1000); | |
200 if (wait_result != WAIT_OBJECT_0) { | |
201 if (wait_result == WAIT_FAILED) | |
202 wait_error = ::GetLastError(); | |
203 SendRemoteProcessInteractionResultHistogram( | |
204 ProcessSingleton::TERMINATE_WAIT_TIMEOUT); | |
205 DPLOG(ERROR) << "Error waiting for process exit"; | |
206 } else { | |
207 SendRemoteProcessInteractionResultHistogram( | |
208 ProcessSingleton::TERMINATE_SUCCEEDED); | |
209 } | |
210 base::debug::GlobalActivityTracker::RecordProcessExitIfEnabled( | |
211 process.Pid(), exit_code); | |
212 UMA_HISTOGRAM_TIMES("Chrome.ProcessSingleton.TerminateProcessTime", | |
213 base::TimeTicks::Now() - start_time); | |
214 UMA_HISTOGRAM_SPARSE_SLOWLY( | |
215 "Chrome.ProcessSingleton.TerminationWaitErrorCode.Windows", wait_error); | |
216 } else { | |
217 terminate_error = ::GetLastError(); | |
218 SendRemoteProcessInteractionResultHistogram( | |
219 ProcessSingleton::TERMINATE_FAILED); | |
220 DPLOG(ERROR) << "Unable to terminate process"; | |
221 } | |
222 UMA_HISTOGRAM_SPARSE_SLOWLY( | |
223 "Chrome.ProcessSingleton.ProcessTerminateErrorCode.Windows", | |
224 terminate_error); | |
225 } | |
226 | |
181 } // namespace | 227 } // namespace |
182 | 228 |
183 // Microsoft's Softricity virtualization breaks the sandbox processes. | 229 // Microsoft's Softricity virtualization breaks the sandbox processes. |
184 // So, if we detect the Softricity DLL we use WMI Win32_Process.Create to | 230 // So, if we detect the Softricity DLL we use WMI Win32_Process.Create to |
185 // break out of the virtualization environment. | 231 // break out of the virtualization environment. |
186 // http://code.google.com/p/chromium/issues/detail?id=43650 | 232 // http://code.google.com/p/chromium/issues/detail?id=43650 |
187 bool ProcessSingleton::EscapeVirtualization( | 233 bool ProcessSingleton::EscapeVirtualization( |
188 const base::FilePath& user_data_dir) { | 234 const base::FilePath& user_data_dir) { |
189 if (::GetModuleHandle(L"sftldr_wow64.dll") || | 235 if (::GetModuleHandle(L"sftldr_wow64.dll") || |
190 ::GetModuleHandle(L"sftldr.dll")) { | 236 ::GetModuleHandle(L"sftldr.dll")) { |
(...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
235 return LOCK_ERROR; | 281 return LOCK_ERROR; |
236 } else if (!remote_window_) { | 282 } else if (!remote_window_) { |
237 return PROCESS_NONE; | 283 return PROCESS_NONE; |
238 } | 284 } |
239 | 285 |
240 switch (chrome::AttemptToNotifyRunningChrome(remote_window_, false)) { | 286 switch (chrome::AttemptToNotifyRunningChrome(remote_window_, false)) { |
241 case chrome::NOTIFY_SUCCESS: | 287 case chrome::NOTIFY_SUCCESS: |
242 return PROCESS_NOTIFIED; | 288 return PROCESS_NOTIFIED; |
243 case chrome::NOTIFY_FAILED: | 289 case chrome::NOTIFY_FAILED: |
244 remote_window_ = NULL; | 290 remote_window_ = NULL; |
291 SendRemoteProcessInteractionResultHistogram( | |
292 RUNNING_PROCESS_NOTIFY_FAILED); | |
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) { |
255 remote_window_ = NULL; | 303 remote_window_ = NULL; |
304 SendRemoteProcessInteractionResultHistogram(REMOTE_PROCESS_NOT_FOUND); | |
256 return PROCESS_NONE; | 305 return PROCESS_NONE; |
257 } | 306 } |
258 | 307 |
259 // Get a handle to the process that created the window. | 308 // Get a handle to the process that created the window. |
260 base::Process process = base::Process::Open(process_id); | 309 base::Process process = base::Process::Open(process_id); |
261 | 310 |
262 // TODO(manzagop): capture a hang report. | |
Alexey Seren
2017/05/11 14:48:46
Seems like this has been done in current CL.
manzagop (departed)
2017/05/11 16:21:55
Acknowledged.
| |
263 | |
264 // Scan for every window to find a visible one. | 311 // Scan for every window to find a visible one. |
265 bool visible_window = false; | 312 bool visible_window = false; |
266 ::EnumThreadWindows(thread_id, | 313 ::EnumThreadWindows(thread_id, |
267 &BrowserWindowEnumeration, | 314 &BrowserWindowEnumeration, |
268 reinterpret_cast<LPARAM>(&visible_window)); | 315 reinterpret_cast<LPARAM>(&visible_window)); |
269 | 316 |
270 // If there is a visible browser window, ask the user before killing it. | 317 // If there is a visible browser window, ask the user before killing it. |
271 if (visible_window && !should_kill_remote_process_callback_.Run()) { | 318 if (visible_window && !should_kill_remote_process_callback_.Run()) { |
272 // The user denied. Quit silently. | 319 // The user denied. Quit silently. |
273 return PROCESS_NOTIFIED; | 320 return PROCESS_NOTIFIED; |
274 } | 321 } |
322 UMA_HISTOGRAM_ENUMERATION( | |
323 "Chrome.ProcessSingleton.RemoteHungProcessTerminateReason", | |
324 visible_window ? USER_ACCEPTED_TERMINATION : NO_VISIBLE_WINDOW_FOUND, | |
325 REMOTE_HUNG_PROCESS_TERMINATE_REASON_COUNT); | |
275 | 326 |
276 // Time to take action. Kill the browser process. | 327 // Time to take action. Kill the browser process. |
277 process.Terminate(content::RESULT_CODE_HUNG, true); | 328 TerminateProcessWithHistograms(process, content::RESULT_CODE_HUNG); |
329 | |
278 remote_window_ = NULL; | 330 remote_window_ = NULL; |
279 return PROCESS_NONE; | 331 return PROCESS_NONE; |
280 } | 332 } |
281 | 333 |
282 ProcessSingleton::NotifyResult | 334 ProcessSingleton::NotifyResult |
283 ProcessSingleton::NotifyOtherProcessOrCreate() { | 335 ProcessSingleton::NotifyOtherProcessOrCreate() { |
284 const base::TimeTicks begin_ticks = base::TimeTicks::Now(); | 336 const base::TimeTicks begin_ticks = base::TimeTicks::Now(); |
285 for (int i = 0; i < 2; ++i) { | 337 for (int i = 0; i < 2; ++i) { |
286 if (Create()) { | 338 if (Create()) { |
287 UMA_HISTOGRAM_MEDIUM_TIMES("Chrome.ProcessSingleton.TimeToCreate", | 339 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; | 423 return window_.hwnd() != NULL; |
372 } | 424 } |
373 | 425 |
374 void ProcessSingleton::Cleanup() { | 426 void ProcessSingleton::Cleanup() { |
375 } | 427 } |
376 | 428 |
377 void ProcessSingleton::OverrideShouldKillRemoteProcessCallbackForTesting( | 429 void ProcessSingleton::OverrideShouldKillRemoteProcessCallbackForTesting( |
378 const ShouldKillRemoteProcessCallback& display_dialog_callback) { | 430 const ShouldKillRemoteProcessCallback& display_dialog_callback) { |
379 should_kill_remote_process_callback_ = display_dialog_callback; | 431 should_kill_remote_process_callback_ = display_dialog_callback; |
380 } | 432 } |
OLD | NEW |