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

Side by Side Diff: content/browser/renderer_host/render_view_host_impl.cc

Issue 10907182: Add UMA counter for hung renderers on cross-site navigation. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Created 8 years, 3 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 | Annotate | Revision Log
OLDNEW
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 "content/browser/renderer_host/render_view_host_impl.h" 5 #include "content/browser/renderer_host/render_view_host_impl.h"
6 6
7 #include <set> 7 #include <set>
8 #include <string> 8 #include <string>
9 #include <utility> 9 #include <utility>
10 #include <vector> 10 #include <vector>
11 11
12 #include "base/i18n/rtl.h" 12 #include "base/i18n/rtl.h"
13 #include "base/json/json_reader.h" 13 #include "base/json/json_reader.h"
14 #include "base/json/json_writer.h" 14 #include "base/json/json_writer.h"
15 #include "base/message_loop.h" 15 #include "base/message_loop.h"
16 #include "base/metrics/histogram.h"
16 #include "base/stl_util.h" 17 #include "base/stl_util.h"
17 #include "base/string_util.h" 18 #include "base/string_util.h"
18 #include "base/time.h" 19 #include "base/time.h"
19 #include "base/utf_string_conversions.h" 20 #include "base/utf_string_conversions.h"
20 #include "base/values.h" 21 #include "base/values.h"
21 #include "content/browser/accessibility/browser_accessibility_state_impl.h" 22 #include "content/browser/accessibility/browser_accessibility_state_impl.h"
22 #include "content/browser/child_process_security_policy_impl.h" 23 #include "content/browser/child_process_security_policy_impl.h"
23 #include "content/browser/cross_site_request_manager.h" 24 #include "content/browser/cross_site_request_manager.h"
24 #include "content/browser/dom_storage/session_storage_namespace_impl.h" 25 #include "content/browser/dom_storage/session_storage_namespace_impl.h"
25 #include "content/browser/gpu/gpu_surface_tracker.h" 26 #include "content/browser/gpu/gpu_surface_tracker.h"
(...skipping 133 matching lines...) Expand 10 before | Expand all | Expand 10 after
159 waiting_for_drag_context_response_(false), 160 waiting_for_drag_context_response_(false),
160 enabled_bindings_(0), 161 enabled_bindings_(0),
161 pending_request_id_(-1), 162 pending_request_id_(-1),
162 navigations_suspended_(false), 163 navigations_suspended_(false),
163 suspended_nav_message_(NULL), 164 suspended_nav_message_(NULL),
164 is_swapped_out_(swapped_out), 165 is_swapped_out_(swapped_out),
165 run_modal_reply_msg_(NULL), 166 run_modal_reply_msg_(NULL),
166 run_modal_opener_id_(MSG_ROUTING_NONE), 167 run_modal_opener_id_(MSG_ROUTING_NONE),
167 is_waiting_for_beforeunload_ack_(false), 168 is_waiting_for_beforeunload_ack_(false),
168 is_waiting_for_unload_ack_(false), 169 is_waiting_for_unload_ack_(false),
170 has_timed_out_on_unload_(false),
169 unload_ack_is_for_cross_site_transition_(false), 171 unload_ack_is_for_cross_site_transition_(false),
170 are_javascript_messages_suppressed_(false), 172 are_javascript_messages_suppressed_(false),
171 sudden_termination_allowed_(false), 173 sudden_termination_allowed_(false),
172 session_storage_namespace_( 174 session_storage_namespace_(
173 static_cast<SessionStorageNamespaceImpl*>(session_storage)), 175 static_cast<SessionStorageNamespaceImpl*>(session_storage)),
174 save_accessibility_tree_for_testing_(false), 176 save_accessibility_tree_for_testing_(false),
175 render_view_termination_status_(base::TERMINATION_STATUS_STILL_RUNNING) { 177 render_view_termination_status_(base::TERMINATION_STATUS_STILL_RUNNING) {
176 DCHECK(session_storage_namespace_); 178 DCHECK(session_storage_namespace_);
177 DCHECK(instance_); 179 DCHECK(instance_);
178 CHECK(delegate_); // http://crbug.com/82827 180 CHECK(delegate_); // http://crbug.com/82827
(...skipping 214 matching lines...) Expand 10 before | Expand all | Expand 10 after
393 // (if there was a cross-site "close" request pending when the user clicked 395 // (if there was a cross-site "close" request pending when the user clicked
394 // the close button). We want to keep the "for cross site" flag only if 396 // the close button). We want to keep the "for cross site" flag only if
395 // both the old and the new ones are also for cross site. 397 // both the old and the new ones are also for cross site.
396 unload_ack_is_for_cross_site_transition_ = 398 unload_ack_is_for_cross_site_transition_ =
397 unload_ack_is_for_cross_site_transition_ && for_cross_site_transition; 399 unload_ack_is_for_cross_site_transition_ && for_cross_site_transition;
398 } else { 400 } else {
399 // Start the hang monitor in case the renderer hangs in the beforeunload 401 // Start the hang monitor in case the renderer hangs in the beforeunload
400 // handler. 402 // handler.
401 is_waiting_for_beforeunload_ack_ = true; 403 is_waiting_for_beforeunload_ack_ = true;
402 unload_ack_is_for_cross_site_transition_ = for_cross_site_transition; 404 unload_ack_is_for_cross_site_transition_ = for_cross_site_transition;
405 // Increment the in-flight event count, to ensure that input events won't
406 // cancel the timeout timer.
407 increment_in_flight_event_count();
403 StartHangMonitorTimeout(TimeDelta::FromMilliseconds(kUnloadTimeoutMS)); 408 StartHangMonitorTimeout(TimeDelta::FromMilliseconds(kUnloadTimeoutMS));
404 send_should_close_start_time_ = base::TimeTicks::Now(); 409 send_should_close_start_time_ = base::TimeTicks::Now();
405 Send(new ViewMsg_ShouldClose(GetRoutingID())); 410 Send(new ViewMsg_ShouldClose(GetRoutingID()));
406 } 411 }
407 } 412 }
408 413
409 void RenderViewHostImpl::SwapOut(int new_render_process_host_id, 414 void RenderViewHostImpl::SwapOut(int new_render_process_host_id,
410 int new_request_id) { 415 int new_request_id) {
411 // This will be set back to false in OnSwapOutACK, just before we replace 416 // This will be set back to false in OnSwapOutACK, just before we replace
412 // this RVH with the pending RVH. 417 // this RVH with the pending RVH.
413 is_waiting_for_unload_ack_ = true; 418 is_waiting_for_unload_ack_ = true;
414 // Start the hang monitor in case the renderer hangs in the unload handler. 419 // Start the hang monitor in case the renderer hangs in the unload handler.
420 // Increment the in-flight event count, to ensure that input events won't
421 // cancel the timeout timer.
422 increment_in_flight_event_count();
415 StartHangMonitorTimeout(TimeDelta::FromMilliseconds(kUnloadTimeoutMS)); 423 StartHangMonitorTimeout(TimeDelta::FromMilliseconds(kUnloadTimeoutMS));
416 424
417 ViewMsg_SwapOut_Params params; 425 ViewMsg_SwapOut_Params params;
418 params.closing_process_id = GetProcess()->GetID(); 426 params.closing_process_id = GetProcess()->GetID();
419 params.closing_route_id = GetRoutingID(); 427 params.closing_route_id = GetRoutingID();
420 params.new_render_process_host_id = new_render_process_host_id; 428 params.new_render_process_host_id = new_render_process_host_id;
421 params.new_request_id = new_request_id; 429 params.new_request_id = new_request_id;
422 if (IsRenderViewLive()) { 430 if (IsRenderViewLive()) {
423 Send(new ViewMsg_SwapOut(GetRoutingID(), params)); 431 Send(new ViewMsg_SwapOut(GetRoutingID(), params));
424 } else { 432 } else {
425 // This RenderViewHost doesn't have a live renderer, so just skip the unload 433 // This RenderViewHost doesn't have a live renderer, so just skip the unload
426 // event. We must notify the ResourceDispatcherHost on the IO thread, 434 // event. We must notify the ResourceDispatcherHost on the IO thread,
427 // which we will do through the RenderProcessHost's widget helper. 435 // which we will do through the RenderProcessHost's widget helper.
428 GetProcess()->CrossSiteSwapOutACK(params); 436 GetProcess()->SimulateSwapOutACK(params);
429 } 437 }
430 } 438 }
431 439
432 void RenderViewHostImpl::OnSwapOutACK() { 440 void RenderViewHostImpl::OnSwapOutACK(bool timed_out) {
433 // Stop the hang monitor now that the unload handler has finished. 441 // Stop the hang monitor now that the unload handler has finished.
442 decrement_in_flight_event_count();
434 StopHangMonitorTimeout(); 443 StopHangMonitorTimeout();
435 is_waiting_for_unload_ack_ = false; 444 is_waiting_for_unload_ack_ = false;
445 has_timed_out_on_unload_ = timed_out;
436 delegate_->SwappedOut(this); 446 delegate_->SwappedOut(this);
437 } 447 }
438 448
439 void RenderViewHostImpl::WasSwappedOut() { 449 void RenderViewHostImpl::WasSwappedOut() {
440 // Don't bother reporting hung state anymore. 450 // Don't bother reporting hung state anymore.
441 StopHangMonitorTimeout(); 451 StopHangMonitorTimeout();
442 452
453 // If we have timed out on running the unload handler, we consider
454 // the process hung and we should terminate it if there are no other tabs
455 // using the process. If there are other views using this process, the
456 // unresponsive renderer timeout will catch it.
457 bool hung = has_timed_out_on_unload_;
458
443 // Now that we're no longer the active RVH in the tab, start filtering out 459 // Now that we're no longer the active RVH in the tab, start filtering out
444 // most IPC messages. Usually the renderer will have stopped sending 460 // most IPC messages. Usually the renderer will have stopped sending
445 // messages as of OnSwapOutACK. However, we may have timed out waiting 461 // messages as of OnSwapOutACK. However, we may have timed out waiting
446 // for that message, and additional IPC messages may keep streaming in. 462 // for that message, and additional IPC messages may keep streaming in.
447 // We filter them out, as long as that won't cause problems (e.g., we 463 // We filter them out, as long as that won't cause problems (e.g., we
448 // still allow synchronous messages through). 464 // still allow synchronous messages through).
449 SetSwappedOut(true); 465 SetSwappedOut(true);
450 466
467 // If we are not running the renderer in process and no other tab is using
468 // the hung process, consider it eligible to be killed, assuming it is a real
469 // process (unit tests don't have real processes).
470 if (hung) {
471 base::ProcessHandle process_handle = GetProcess()->GetHandle();
472 int views = 0;
473
474 // Count the number of widget hosts for the process, which is equivalent to
475 // views using the process as of this writing.
476 content::RenderProcessHost::RenderWidgetHostsIterator iter(
477 GetProcess()->GetRenderWidgetHostsIterator());
478 for (; !iter.IsAtEnd(); iter.Advance())
479 ++views;
480
481 if (!content::RenderProcessHost::run_renderer_in_process() &&
482 process_handle && views <= 1) {
483 // The process can safely be terminated, only if WebContents sets
484 // SuddenTerminationAllowed, which indicates that the timer has expired.
485 // This is not the case if we load data URLs or about:blank. The reason
486 // is that there is no network requests and this code is hit without
Charlie Reis 2012/09/13 21:50:20 nit: there is -> those have
nasko 2012/09/13 22:09:30 Done.
487 // setting the unresponsiveness timer. This allows a corner case where a
488 // navigation to a data URL will leave a process running, if the
489 // beforeunload handler completes fine, but the unload handler hangs.
490 // At this time, the complexity to solve this edge case is not worthwhile.
491 if (SuddenTerminationAllowed()) {
492 // We should kill the process, but for now, just log the data so we can
493 // diagnose the kill rate and investigate if separate timer is needed.
494 // http://crbug.com/104346.
495
496 // Log a histogram point to help us diagnose how many of those kills
497 // we have performed. 1 is the enum value for RendererType Normal for
498 // the histogram.
499 UMA_HISTOGRAM_PERCENTAGE(
500 "BrowserRenderProcessHost.ChildKillsUnresponsive", 1);
501 }
502 }
503 }
504
451 // Inform the renderer that it can exit if no one else is using it. 505 // Inform the renderer that it can exit if no one else is using it.
452 Send(new ViewMsg_WasSwappedOut(GetRoutingID())); 506 Send(new ViewMsg_WasSwappedOut(GetRoutingID()));
453 } 507 }
454 508
455 void RenderViewHostImpl::ClosePage() { 509 void RenderViewHostImpl::ClosePage() {
456 // Start the hang monitor in case the renderer hangs in the unload handler. 510 // Start the hang monitor in case the renderer hangs in the unload handler.
457 is_waiting_for_unload_ack_ = true; 511 is_waiting_for_unload_ack_ = true;
458 StartHangMonitorTimeout(TimeDelta::FromMilliseconds(kUnloadTimeoutMS)); 512 StartHangMonitorTimeout(TimeDelta::FromMilliseconds(kUnloadTimeoutMS));
459 513
460 if (IsRenderViewLive()) { 514 if (IsRenderViewLive()) {
515 // Since we are sending an IPC message to the renderer, increase the event
516 // count to prevent the hang monitor timeout from being stopped by input
517 // event acknowledgements.
518 increment_in_flight_event_count();
519
461 // TODO(creis): Should this be moved to Shutdown? It may not be called for 520 // TODO(creis): Should this be moved to Shutdown? It may not be called for
462 // RenderViewHosts that have been swapped out. 521 // RenderViewHosts that have been swapped out.
463 content::NotificationService::current()->Notify( 522 content::NotificationService::current()->Notify(
464 content::NOTIFICATION_RENDER_VIEW_HOST_WILL_CLOSE_RENDER_VIEW, 523 content::NOTIFICATION_RENDER_VIEW_HOST_WILL_CLOSE_RENDER_VIEW,
465 content::Source<RenderViewHost>(this), 524 content::Source<RenderViewHost>(this),
466 content::NotificationService::NoDetails()); 525 content::NotificationService::NoDetails());
467 526
468 Send(new ViewMsg_ClosePage(GetRoutingID())); 527 Send(new ViewMsg_ClosePage(GetRoutingID()));
469 } else { 528 } else {
470 // This RenderViewHost doesn't have a live renderer, so just skip the unload 529 // This RenderViewHost doesn't have a live renderer, so just skip the unload
(...skipping 204 matching lines...) Expand 10 before | Expand all | Expand 10 after
675 loop->Run(); 734 loop->Run();
676 return observer.value()->DeepCopy(); 735 return observer.value()->DeepCopy();
677 } 736 }
678 737
679 void RenderViewHostImpl::JavaScriptDialogClosed(IPC::Message* reply_msg, 738 void RenderViewHostImpl::JavaScriptDialogClosed(IPC::Message* reply_msg,
680 bool success, 739 bool success,
681 const string16& user_input) { 740 const string16& user_input) {
682 GetProcess()->SetIgnoreInputEvents(false); 741 GetProcess()->SetIgnoreInputEvents(false);
683 bool is_waiting = 742 bool is_waiting =
684 is_waiting_for_beforeunload_ack_ || is_waiting_for_unload_ack_; 743 is_waiting_for_beforeunload_ack_ || is_waiting_for_unload_ack_;
744
745 // If we are executing as part of (before)unload event handling, we don't
746 // want to use the regular hung_renderer_delay_ms_ if the user has agreed to
747 // leave the current page. In this case, use the regular timeout value used
748 // during the (before)unload handling.
685 if (is_waiting) 749 if (is_waiting)
686 StartHangMonitorTimeout(TimeDelta::FromMilliseconds(kUnloadTimeoutMS)); 750 StartHangMonitorTimeout(TimeDelta::FromMilliseconds(
751 success ? kUnloadTimeoutMS : hung_renderer_delay_ms_));
Charlie Reis 2012/09/13 21:50:20 nit: Needs braces.
nasko 2012/09/13 22:09:30 Done.
687 752
688 ViewHostMsg_RunJavaScriptMessage::WriteReplyParams(reply_msg, 753 ViewHostMsg_RunJavaScriptMessage::WriteReplyParams(reply_msg,
689 success, user_input); 754 success, user_input);
690 Send(reply_msg); 755 Send(reply_msg);
691 756
692 // If we are waiting for an unload or beforeunload ack and the user has 757 // If we are waiting for an unload or beforeunload ack and the user has
693 // suppressed messages, kill the tab immediately; a page that's spamming 758 // suppressed messages, kill the tab immediately; a page that's spamming
694 // alerts in onbeforeunload is presumably malicious, so there's no point in 759 // alerts in onbeforeunload is presumably malicious, so there's no point in
695 // continuing to run its script and dragging out the process. 760 // continuing to run its script and dragging out the process.
696 // This must be done after sending the reply since RenderView can't close 761 // This must be done after sending the reply since RenderView can't close
(...skipping 757 matching lines...) Expand 10 before | Expand all | Expand 10 after
1454 } 1519 }
1455 1520
1456 void RenderViewHostImpl::OnUserGesture() { 1521 void RenderViewHostImpl::OnUserGesture() {
1457 delegate_->OnUserGesture(); 1522 delegate_->OnUserGesture();
1458 } 1523 }
1459 1524
1460 void RenderViewHostImpl::OnMsgShouldCloseACK( 1525 void RenderViewHostImpl::OnMsgShouldCloseACK(
1461 bool proceed, 1526 bool proceed,
1462 const base::TimeTicks& renderer_before_unload_start_time, 1527 const base::TimeTicks& renderer_before_unload_start_time,
1463 const base::TimeTicks& renderer_before_unload_end_time) { 1528 const base::TimeTicks& renderer_before_unload_end_time) {
1529 decrement_in_flight_event_count();
1464 StopHangMonitorTimeout(); 1530 StopHangMonitorTimeout();
1465 // If this renderer navigated while the beforeunload request was in flight, we 1531 // If this renderer navigated while the beforeunload request was in flight, we
1466 // may have cleared this state in OnMsgNavigate, in which case we can ignore 1532 // may have cleared this state in OnMsgNavigate, in which case we can ignore
1467 // this message. 1533 // this message.
1468 if (!is_waiting_for_beforeunload_ack_ || is_swapped_out_) 1534 if (!is_waiting_for_beforeunload_ack_ || is_swapped_out_)
1469 return; 1535 return;
1470 1536
1471 is_waiting_for_beforeunload_ack_ = false; 1537 is_waiting_for_beforeunload_ack_ = false;
1472 1538
1473 RenderViewHostDelegate::RendererManagement* management_delegate = 1539 RenderViewHostDelegate::RendererManagement* management_delegate =
(...skipping 21 matching lines...) Expand all
1495 unload_ack_is_for_cross_site_transition_, proceed, 1561 unload_ack_is_for_cross_site_transition_, proceed,
1496 before_unload_end_time); 1562 before_unload_end_time);
1497 } 1563 }
1498 1564
1499 // If canceled, notify the delegate to cancel its pending navigation entry. 1565 // If canceled, notify the delegate to cancel its pending navigation entry.
1500 if (!proceed) 1566 if (!proceed)
1501 delegate_->DidCancelLoading(); 1567 delegate_->DidCancelLoading();
1502 } 1568 }
1503 1569
1504 void RenderViewHostImpl::OnMsgClosePageACK() { 1570 void RenderViewHostImpl::OnMsgClosePageACK() {
1571 decrement_in_flight_event_count();
1505 ClosePageIgnoringUnloadEvents(); 1572 ClosePageIgnoringUnloadEvents();
1506 } 1573 }
1507 1574
1508 void RenderViewHostImpl::NotifyRendererUnresponsive() { 1575 void RenderViewHostImpl::NotifyRendererUnresponsive() {
1509 delegate_->RendererUnresponsive( 1576 delegate_->RendererUnresponsive(
1510 this, is_waiting_for_beforeunload_ack_ || is_waiting_for_unload_ack_); 1577 this, is_waiting_for_beforeunload_ack_ || is_waiting_for_unload_ack_);
1511 } 1578 }
1512 1579
1513 void RenderViewHostImpl::NotifyRendererResponsive() { 1580 void RenderViewHostImpl::NotifyRendererResponsive() {
1514 delegate_->RendererResponsive(this); 1581 delegate_->RendererResponsive(this);
(...skipping 399 matching lines...) Expand 10 before | Expand all | Expand 10 after
1914 } 1981 }
1915 1982
1916 void RenderViewHostImpl::SetSwappedOut(bool is_swapped_out) { 1983 void RenderViewHostImpl::SetSwappedOut(bool is_swapped_out) {
1917 is_swapped_out_ = is_swapped_out; 1984 is_swapped_out_ = is_swapped_out;
1918 1985
1919 // Whenever we change swap out state, we should not be waiting for 1986 // Whenever we change swap out state, we should not be waiting for
1920 // beforeunload or unload acks. We clear them here to be safe, since they 1987 // beforeunload or unload acks. We clear them here to be safe, since they
1921 // can cause navigations to be ignored in OnMsgNavigate. 1988 // can cause navigations to be ignored in OnMsgNavigate.
1922 is_waiting_for_beforeunload_ack_ = false; 1989 is_waiting_for_beforeunload_ack_ = false;
1923 is_waiting_for_unload_ack_ = false; 1990 is_waiting_for_unload_ack_ = false;
1991 has_timed_out_on_unload_ = false;
1924 } 1992 }
1925 1993
1926 void RenderViewHostImpl::ClearPowerSaveBlockers() { 1994 void RenderViewHostImpl::ClearPowerSaveBlockers() {
1927 STLDeleteValues(&power_save_blockers_); 1995 STLDeleteValues(&power_save_blockers_);
1928 } 1996 }
1929 1997
1930 } // namespace content 1998 } // namespace content
OLDNEW
« no previous file with comments | « content/browser/renderer_host/render_view_host_impl.h ('k') | content/browser/renderer_host/render_widget_helper.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698