Commit e7d18c79 authored by Ulan Degenbaev's avatar Ulan Degenbaev Committed by Commit Bot

Notify the browser process about a bloated renderer via the GRC.

The renderer sends a RendererIsBloated event to the GRC service.
The GRC turns the event into a PageIsBloated signal and sends the
signal to the tab manager in the browser process. The tab manager
then reloads the bloated tab.

Subsequent CL will add a notification from the tab manager to the
web contents, so that it can show an inforbar after the reload.

Bug: 835806
Change-Id: I23d0987094f1c6864534abeb57f8cce3273172ff
Reviewed-on: https://chromium-review.googlesource.com/1073419
Commit-Queue: Ulan Degenbaev <ulan@chromium.org>
Reviewed-by: default avatarWill Harris <wfh@chromium.org>
Reviewed-by: default avatarSteven Holte <holte@chromium.org>
Reviewed-by: default avatarKentaro Hara <haraken@chromium.org>
Reviewed-by: default avatarScott Violet <sky@chromium.org>
Reviewed-by: default avatarChris Hamilton <chrisha@chromium.org>
Reviewed-by: default avatarFrançois Doray <fdoray@chromium.org>
Cr-Commit-Position: refs/heads/master@{#565680}
parent 8d1bf34d
...@@ -46,6 +46,15 @@ void PageSignalReceiver::NotifyPageAlmostIdle( ...@@ -46,6 +46,15 @@ void PageSignalReceiver::NotifyPageAlmostIdle(
observer.OnPageAlmostIdle(web_contents_iter->second); observer.OnPageAlmostIdle(web_contents_iter->second);
} }
void PageSignalReceiver::NotifyRendererIsBloated(
const CoordinationUnitID& page_cu_id) {
auto web_contents_iter = cu_id_web_contents_map_.find(page_cu_id);
if (web_contents_iter == cu_id_web_contents_map_.end())
return;
for (auto& observer : observers_)
observer.OnRendererIsBloated(web_contents_iter->second);
}
void PageSignalReceiver::SetExpectedTaskQueueingDuration( void PageSignalReceiver::SetExpectedTaskQueueingDuration(
const CoordinationUnitID& page_cu_id, const CoordinationUnitID& page_cu_id,
base::TimeDelta duration) { base::TimeDelta duration) {
......
...@@ -25,6 +25,7 @@ class PageSignalObserver { ...@@ -25,6 +25,7 @@ class PageSignalObserver {
// managed by the client. Thus the clients are responsible for checking the // managed by the client. Thus the clients are responsible for checking the
// passed |web_contents| by themselves. // passed |web_contents| by themselves.
virtual void OnPageAlmostIdle(content::WebContents* web_contents) {} virtual void OnPageAlmostIdle(content::WebContents* web_contents) {}
virtual void OnRendererIsBloated(content::WebContents* web_contents) {}
virtual void OnExpectedTaskQueueingDurationSet( virtual void OnExpectedTaskQueueingDurationSet(
content::WebContents* web_contents, content::WebContents* web_contents,
base::TimeDelta duration) {} base::TimeDelta duration) {}
...@@ -52,6 +53,7 @@ class PageSignalReceiver : public mojom::PageSignalReceiver { ...@@ -52,6 +53,7 @@ class PageSignalReceiver : public mojom::PageSignalReceiver {
// mojom::PageSignalReceiver implementation. // mojom::PageSignalReceiver implementation.
void NotifyPageAlmostIdle(const CoordinationUnitID& page_cu_id) override; void NotifyPageAlmostIdle(const CoordinationUnitID& page_cu_id) override;
void NotifyRendererIsBloated(const CoordinationUnitID& page_cu_id) override;
void SetExpectedTaskQueueingDuration(const CoordinationUnitID& page_cu_id, void SetExpectedTaskQueueingDuration(const CoordinationUnitID& page_cu_id,
base::TimeDelta duration) override; base::TimeDelta duration) override;
void SetLifecycleState(const CoordinationUnitID& page_cu_id, void SetLifecycleState(const CoordinationUnitID& page_cu_id,
......
...@@ -28,7 +28,6 @@ ...@@ -28,7 +28,6 @@
#include "content/public/browser/render_frame_host.h" #include "content/public/browser/render_frame_host.h"
#include "content/public/browser/render_process_host.h" #include "content/public/browser/render_process_host.h"
#include "content/public/browser/web_contents.h" #include "content/public/browser/web_contents.h"
#include "content/public/common/page_importance_signals.h"
#include "url/gurl.h" #include "url/gurl.h"
namespace resource_coordinator { namespace resource_coordinator {
...@@ -45,7 +44,20 @@ bool IsFrozenOrPendingFreeze(LifecycleUnitState state) { ...@@ -45,7 +44,20 @@ bool IsFrozenOrPendingFreeze(LifecycleUnitState state) {
state == LifecycleUnitState::PENDING_FREEZE; state == LifecycleUnitState::PENDING_FREEZE;
} }
} // namespace // These values are persisted to logs. Entries should not be renumbered and
// numeric values should never be reused.
enum class BloatedRendererHandlingInBrowser {
kReloaded = 0,
kCannotReload = 1,
kCannotShutdown = 2,
kMaxValue = kCannotShutdown
};
void RecordBloatedRendererHandling(BloatedRendererHandlingInBrowser handling) {
UMA_HISTOGRAM_ENUMERATION("BloatedRenderer.HandlingInBrowser", handling);
}
} // anonymous namespace
TabLifecycleUnitSource::TabLifecycleUnit::TabLifecycleUnit( TabLifecycleUnitSource::TabLifecycleUnit::TabLifecycleUnit(
base::ObserverList<TabLifecycleObserver>* observers, base::ObserverList<TabLifecycleObserver>* observers,
...@@ -524,6 +536,54 @@ bool TabLifecycleUnitSource::TabLifecycleUnit::DiscardTab() { ...@@ -524,6 +536,54 @@ bool TabLifecycleUnitSource::TabLifecycleUnit::DiscardTab() {
return Discard(DiscardReason::kExternal); return Discard(DiscardReason::kExternal);
} }
bool TabLifecycleUnitSource::TabLifecycleUnit::CanReloadBloatedTab() {
// Can't reload a tab that isn't in a TabStripModel, which is needed for
// showing an infobar.
if (!tab_strip_model_)
return false;
if (GetWebContents()->IsCrashed())
return false;
// Do not reload tabs that don't have a valid URL (most probably they have
// just been opened and discarding them would lose the URL).
if (!GetWebContents()->GetLastCommittedURL().is_valid() ||
GetWebContents()->GetLastCommittedURL().is_empty()) {
return false;
}
// Do not reload tabs in which the user has entered text in a form.
if (GetWebContents()->GetPageImportanceSignals().had_form_interaction)
return false;
// TODO(ulan): Check if the navigation controller has POST data.
return true;
}
void TabLifecycleUnitSource::TabLifecycleUnit::ReloadBloatedTab() {
if (CanReloadBloatedTab()) {
const size_t expected_page_count = 1u;
const bool skip_unload_handlers = true;
if (GetRenderProcessHost()->FastShutdownIfPossible(expected_page_count,
skip_unload_handlers)) {
// TODO(ulan): Notify the WebContents that the page is bloated to give
// it a chance to show the infobar after the reload.
const bool check_for_repost = true;
GetWebContents()->GetController().Reload(content::ReloadType::NORMAL,
check_for_repost);
RecordBloatedRendererHandling(
BloatedRendererHandlingInBrowser::kReloaded);
} else {
RecordBloatedRendererHandling(
BloatedRendererHandlingInBrowser::kCannotShutdown);
}
} else {
RecordBloatedRendererHandling(
BloatedRendererHandlingInBrowser::kCannotReload);
}
}
bool TabLifecycleUnitSource::TabLifecycleUnit::FreezeTab() { bool TabLifecycleUnitSource::TabLifecycleUnit::FreezeTab() {
return Freeze(); return Freeze();
} }
......
...@@ -5,6 +5,7 @@ ...@@ -5,6 +5,7 @@
#ifndef CHROME_BROWSER_RESOURCE_COORDINATOR_TAB_LIFECYCLE_UNIT_H_ #ifndef CHROME_BROWSER_RESOURCE_COORDINATOR_TAB_LIFECYCLE_UNIT_H_
#define CHROME_BROWSER_RESOURCE_COORDINATOR_TAB_LIFECYCLE_UNIT_H_ #define CHROME_BROWSER_RESOURCE_COORDINATOR_TAB_LIFECYCLE_UNIT_H_
#include "base/gtest_prod_util.h"
#include "base/macros.h" #include "base/macros.h"
#include "base/observer_list.h" #include "base/observer_list.h"
#include "base/time/time.h" #include "base/time/time.h"
...@@ -15,6 +16,7 @@ ...@@ -15,6 +16,7 @@
#include "chrome/browser/resource_coordinator/time.h" #include "chrome/browser/resource_coordinator/time.h"
#include "content/public/browser/visibility.h" #include "content/public/browser/visibility.h"
#include "content/public/browser/web_contents_observer.h" #include "content/public/browser/web_contents_observer.h"
#include "content/public/common/page_importance_signals.h"
class TabStripModel; class TabStripModel;
...@@ -76,6 +78,10 @@ class TabLifecycleUnitSource::TabLifecycleUnit ...@@ -76,6 +78,10 @@ class TabLifecycleUnitSource::TabLifecycleUnit
// unit. // unit.
void UpdateLifecycleState(mojom::LifecycleState state); void UpdateLifecycleState(mojom::LifecycleState state);
// Reloads the tab because its renderer is bloated and shows an infobar
// explaining that it was reloaded because it ran out of memory.
void ReloadBloatedTab();
// LifecycleUnit: // LifecycleUnit:
TabLifecycleUnitExternal* AsTabLifecycleUnitExternal() override; TabLifecycleUnitExternal* AsTabLifecycleUnitExternal() override;
base::string16 GetTitle() const override; base::string16 GetTitle() const override;
...@@ -111,6 +117,13 @@ class TabLifecycleUnitSource::TabLifecycleUnit ...@@ -111,6 +117,13 @@ class TabLifecycleUnitSource::TabLifecycleUnit
friend class TabLifecycleUnitSource; friend class TabLifecycleUnitSource;
private: private:
FRIEND_TEST_ALL_PREFIXES(TabLifecycleUnitTest, CanReloadBloatedTab);
FRIEND_TEST_ALL_PREFIXES(TabLifecycleUnitTest, CannotReloadBloatedTabCrashed);
FRIEND_TEST_ALL_PREFIXES(TabLifecycleUnitTest,
CannotReloadBloatedTabInvalidURL);
FRIEND_TEST_ALL_PREFIXES(TabLifecycleUnitTest,
CannotReloadBloatedTabPendingUserInteraction);
// Determines if the tab is a media tab, and populates an optional // Determines if the tab is a media tab, and populates an optional
// |decision_details| with full details. // |decision_details| with full details.
bool IsMediaTabImpl(DecisionDetails* decision_details) const; bool IsMediaTabImpl(DecisionDetails* decision_details) const;
...@@ -137,6 +150,8 @@ class TabLifecycleUnitSource::TabLifecycleUnit ...@@ -137,6 +150,8 @@ class TabLifecycleUnitSource::TabLifecycleUnit
void DidStartLoading() override; void DidStartLoading() override;
void OnVisibilityChanged(content::Visibility visibility) override; void OnVisibilityChanged(content::Visibility visibility) override;
bool CanReloadBloatedTab();
// List of observers to notify when the discarded state or the auto- // List of observers to notify when the discarded state or the auto-
// discardable state of this tab changes. // discardable state of this tab changes.
base::ObserverList<TabLifecycleObserver>* observers_; base::ObserverList<TabLifecycleObserver>* observers_;
......
...@@ -229,6 +229,11 @@ void TabLifecycleUnitSource::OnLifecycleStateChanged( ...@@ -229,6 +229,11 @@ void TabLifecycleUnitSource::OnLifecycleStateChanged(
lifecycle_unit->UpdateLifecycleState(state); lifecycle_unit->UpdateLifecycleState(state);
} }
void TabLifecycleUnitSource::OnRendererIsBloated(
content::WebContents* web_contents) {
GetTabLifecycleUnit(web_contents)->ReloadBloatedTab();
}
} // namespace resource_coordinator } // namespace resource_coordinator
DEFINE_WEB_CONTENTS_USER_DATA_KEY( DEFINE_WEB_CONTENTS_USER_DATA_KEY(
......
...@@ -114,6 +114,7 @@ class TabLifecycleUnitSource : public BrowserListObserver, ...@@ -114,6 +114,7 @@ class TabLifecycleUnitSource : public BrowserListObserver,
// PageSignalObserver: // PageSignalObserver:
void OnLifecycleStateChanged(content::WebContents* web_contents, void OnLifecycleStateChanged(content::WebContents* web_contents,
mojom::LifecycleState state) override; mojom::LifecycleState state) override;
void OnRendererIsBloated(content::WebContents* web_contents) override;
// Tracks the BrowserList and all TabStripModels. // Tracks the BrowserList and all TabStripModels.
BrowserTabStripTracker browser_tab_strip_tracker_; BrowserTabStripTracker browser_tab_strip_tracker_;
......
...@@ -332,4 +332,39 @@ TEST_F(TabLifecycleUnitTest, NotifiedOfWebContentsVisibilityChanges) { ...@@ -332,4 +332,39 @@ TEST_F(TabLifecycleUnitTest, NotifiedOfWebContentsVisibilityChanges) {
tab_lifecycle_unit.RemoveObserver(&observer); tab_lifecycle_unit.RemoveObserver(&observer);
} }
TEST_F(TabLifecycleUnitTest, CanReloadBloatedTab) {
TabLifecycleUnit tab_lifecycle_unit(&observers_, web_contents_,
tab_strip_model_.get());
EXPECT_TRUE(tab_lifecycle_unit.CanReloadBloatedTab());
}
TEST_F(TabLifecycleUnitTest, CannotReloadBloatedTabCrashed) {
TabLifecycleUnit tab_lifecycle_unit(&observers_, web_contents_,
tab_strip_model_.get());
web_contents_->SetIsCrashed(base::TERMINATION_STATUS_PROCESS_CRASHED, 0);
EXPECT_FALSE(tab_lifecycle_unit.CanReloadBloatedTab());
}
TEST_F(TabLifecycleUnitTest, CannotReloadBloatedTabInvalidURL) {
TabLifecycleUnit tab_lifecycle_unit(&observers_, web_contents_,
tab_strip_model_.get());
content::WebContentsTester::For(web_contents_)
->SetLastCommittedURL(GURL("invalid :)"));
EXPECT_FALSE(tab_lifecycle_unit.CanReloadBloatedTab());
}
TEST_F(TabLifecycleUnitTest, CannotReloadBloatedTabPendingUserInteraction) {
TabLifecycleUnit tab_lifecycle_unit(&observers_, web_contents_,
tab_strip_model_.get());
content::PageImportanceSignals signals;
signals.had_form_interaction = true;
content::WebContentsTester::For(web_contents_)
->SetPageImportanceSignals(signals);
EXPECT_FALSE(tab_lifecycle_unit.CanReloadBloatedTab());
}
} // namespace resource_coordinator } // namespace resource_coordinator
...@@ -118,6 +118,12 @@ class TabManagerTest : public InProcessBrowserTest { ...@@ -118,6 +118,12 @@ class TabManagerTest : public InProcessBrowserTest {
->UpdateLifecycleState(mojom::LifecycleState::kFrozen); ->UpdateLifecycleState(mojom::LifecycleState::kFrozen);
} }
void ReloadBloatedTab(content::WebContents* contents) {
static_cast<TabLifecycleUnitSource::TabLifecycleUnit*>(
TabLifecycleUnitExternal::FromWebContents(contents))
->ReloadBloatedTab();
}
base::SimpleTestTickClock test_clock_; base::SimpleTestTickClock test_clock_;
ScopedSetTickClockForTesting scoped_set_tick_clock_for_testing_; ScopedSetTickClockForTesting scoped_set_tick_clock_for_testing_;
}; };
...@@ -1115,6 +1121,25 @@ IN_PROC_BROWSER_TEST_F(TabManagerTest, ...@@ -1115,6 +1121,25 @@ IN_PROC_BROWSER_TEST_F(TabManagerTest,
EXPECT_TRUE(IsTabDiscarded(browser4->tab_strip_model()->GetWebContentsAt(1))); EXPECT_TRUE(IsTabDiscarded(browser4->tab_strip_model()->GetWebContentsAt(1)));
} }
// TODO(ulan): Enable the test after fixing crbug.com/850921.
IN_PROC_BROWSER_TEST_F(TabManagerTest, DISABLED_ReloadBloatedTab) {
content::WindowedNotificationObserver load(
content::NOTIFICATION_NAV_ENTRY_COMMITTED,
content::NotificationService::AllSources());
OpenURLParams url(GURL(chrome::kChromeUIAboutURL), content::Referrer(),
WindowOpenDisposition::CURRENT_TAB,
ui::PAGE_TRANSITION_TYPED, false);
browser()->OpenURL(url);
load.Wait();
content::WindowedNotificationObserver reload(
content::NOTIFICATION_NAV_ENTRY_COMMITTED,
content::NotificationService::AllSources());
auto* tsm = browser()->tab_strip_model();
ReloadBloatedTab(tsm->GetWebContentsAt(0));
reload.Wait();
}
} // namespace resource_coordinator } // namespace resource_coordinator
#endif // OS_WIN || OS_MAXOSX || OS_LINUX || defined(OS_CHROMEOS) #endif // OS_WIN || OS_MAXOSX || OS_LINUX || defined(OS_CHROMEOS)
...@@ -156,6 +156,9 @@ class WebContentsTester { ...@@ -156,6 +156,9 @@ class WebContentsTester {
// Resets the state around PauseSubresourceLoadingCalled. // Resets the state around PauseSubresourceLoadingCalled.
virtual void ResetPauseSubresourceLoadingCalled() = 0; virtual void ResetPauseSubresourceLoadingCalled() = 0;
// Sets the return value of GetPageImportanceSignals().
virtual void SetPageImportanceSignals(PageImportanceSignals signals) = 0;
}; };
} // namespace content } // namespace content
......
...@@ -449,4 +449,8 @@ void TestWebContents::ResetPauseSubresourceLoadingCalled() { ...@@ -449,4 +449,8 @@ void TestWebContents::ResetPauseSubresourceLoadingCalled() {
pause_subresource_loading_called_ = false; pause_subresource_loading_called_ = false;
} }
void TestWebContents::SetPageImportanceSignals(PageImportanceSignals signals) {
page_importance_signals_ = signals;
}
} // namespace content } // namespace content
...@@ -150,6 +150,8 @@ class TestWebContents : public WebContentsImpl, public WebContentsTester { ...@@ -150,6 +150,8 @@ class TestWebContents : public WebContentsImpl, public WebContentsTester {
void TestDidFinishLoad(const GURL& url); void TestDidFinishLoad(const GURL& url);
void SetPageImportanceSignals(PageImportanceSignals signals) override;
protected: protected:
// The deprecated WebContentsTester still needs to subclass this. // The deprecated WebContentsTester still needs to subclass this.
explicit TestWebContents(BrowserContext* browser_context); explicit TestWebContents(BrowserContext* browser_context);
......
...@@ -67,6 +67,10 @@ void ProcessCoordinationUnitImpl::SetPID(int64_t pid) { ...@@ -67,6 +67,10 @@ void ProcessCoordinationUnitImpl::SetPID(int64_t pid) {
SetProperty(mojom::PropertyType::kPID, pid); SetProperty(mojom::PropertyType::kPID, pid);
} }
void ProcessCoordinationUnitImpl::OnRendererIsBloated() {
SendEvent(mojom::Event::kRendererIsBloated);
}
const std::set<FrameCoordinationUnitImpl*>& const std::set<FrameCoordinationUnitImpl*>&
ProcessCoordinationUnitImpl::GetFrameCoordinationUnits() const { ProcessCoordinationUnitImpl::GetFrameCoordinationUnits() const {
return frame_coordination_units_; return frame_coordination_units_;
...@@ -86,6 +90,11 @@ ProcessCoordinationUnitImpl::GetAssociatedPageCoordinationUnits() const { ...@@ -86,6 +90,11 @@ ProcessCoordinationUnitImpl::GetAssociatedPageCoordinationUnits() const {
return page_cus; return page_cus;
} }
void ProcessCoordinationUnitImpl::OnEventReceived(mojom::Event event) {
for (auto& observer : observers())
observer.OnProcessEventReceived(this, event);
}
void ProcessCoordinationUnitImpl::OnPropertyChanged( void ProcessCoordinationUnitImpl::OnPropertyChanged(
const mojom::PropertyType property_type, const mojom::PropertyType property_type,
int64_t value) { int64_t value) {
......
...@@ -35,6 +35,7 @@ class ProcessCoordinationUnitImpl ...@@ -35,6 +35,7 @@ class ProcessCoordinationUnitImpl
void SetLaunchTime(base::Time launch_time) override; void SetLaunchTime(base::Time launch_time) override;
void SetMainThreadTaskLoadIsLow(bool main_thread_task_load_is_low) override; void SetMainThreadTaskLoadIsLow(bool main_thread_task_load_is_low) override;
void SetPID(int64_t pid) override; void SetPID(int64_t pid) override;
void OnRendererIsBloated() override;
// Private implementation properties. // Private implementation properties.
void set_private_footprint_kb(uint64_t private_footprint_kb) { void set_private_footprint_kb(uint64_t private_footprint_kb) {
...@@ -54,6 +55,7 @@ class ProcessCoordinationUnitImpl ...@@ -54,6 +55,7 @@ class ProcessCoordinationUnitImpl
friend class FrameCoordinationUnitImpl; friend class FrameCoordinationUnitImpl;
// CoordinationUnitInterface implementation. // CoordinationUnitInterface implementation.
void OnEventReceived(mojom::Event event) override;
void OnPropertyChanged(mojom::PropertyType property_type, void OnPropertyChanged(mojom::PropertyType property_type,
int64_t value) override; int64_t value) override;
......
...@@ -6,6 +6,7 @@ ...@@ -6,6 +6,7 @@
#include <utility> #include <utility>
#include "base/metrics/histogram_macros.h"
#include "services/resource_coordinator/coordination_unit/frame_coordination_unit_impl.h" #include "services/resource_coordinator/coordination_unit/frame_coordination_unit_impl.h"
#include "services/resource_coordinator/coordination_unit/page_coordination_unit_impl.h" #include "services/resource_coordinator/coordination_unit/page_coordination_unit_impl.h"
#include "services/resource_coordinator/coordination_unit/process_coordination_unit_impl.h" #include "services/resource_coordinator/coordination_unit/process_coordination_unit_impl.h"
...@@ -20,6 +21,24 @@ namespace resource_coordinator { ...@@ -20,6 +21,24 @@ namespace resource_coordinator {
receiver->METHOD(__VA_ARGS__); \ receiver->METHOD(__VA_ARGS__); \
}); });
namespace {
// These values are persisted to logs. Entries should not be renumbered and
// numeric values should never be reused.
enum class BloatedRendererHandlingInResourceCoordinator {
kForwardedToBrowser = 0,
kIgnoredDueToMultiplePages = 1,
kMaxValue = kIgnoredDueToMultiplePages
};
void RecordBloatedRendererHandling(
BloatedRendererHandlingInResourceCoordinator handling) {
UMA_HISTOGRAM_ENUMERATION("BloatedRenderer.HandlingInResourceCoordinator",
handling);
}
} // anonymous namespace
// static // static
constexpr base::TimeDelta PageSignalGeneratorImpl::kLoadedAndIdlingTimeout = constexpr base::TimeDelta PageSignalGeneratorImpl::kLoadedAndIdlingTimeout =
base::TimeDelta::FromSeconds(1); base::TimeDelta::FromSeconds(1);
...@@ -55,6 +74,7 @@ void PageSignalGeneratorImpl::AddReceiver( ...@@ -55,6 +74,7 @@ void PageSignalGeneratorImpl::AddReceiver(
// Process CUs should be observed for: // Process CUs should be observed for:
// 1- kExpectedTaskQueueingDuration property for reporting EQT // 1- kExpectedTaskQueueingDuration property for reporting EQT
// 2- kMainThreadTaskLoadIsLow property changes for PageAlmostIdle detection // 2- kMainThreadTaskLoadIsLow property changes for PageAlmostIdle detection
// 3- kRendererIsBloated event for reloading bloated pages.
bool PageSignalGeneratorImpl::ShouldObserve( bool PageSignalGeneratorImpl::ShouldObserve(
const CoordinationUnitBase* coordination_unit) { const CoordinationUnitBase* coordination_unit) {
auto cu_type = coordination_unit->id().type; auto cu_type = coordination_unit->id().type;
...@@ -175,6 +195,26 @@ void PageSignalGeneratorImpl::OnPageEventReceived( ...@@ -175,6 +195,26 @@ void PageSignalGeneratorImpl::OnPageEventReceived(
page_data->idling_timer.Stop(); page_data->idling_timer.Stop();
} }
void PageSignalGeneratorImpl::OnProcessEventReceived(
const ProcessCoordinationUnitImpl* process_cu,
const mojom::Event event) {
if (event == mojom::Event::kRendererIsBloated) {
std::set<PageCoordinationUnitImpl*> page_cus =
process_cu->GetAssociatedPageCoordinationUnits();
// Currently bloated renderer handling supports only a single page.
if (page_cus.size() == 1u) {
auto* page_cu = *page_cus.begin();
DISPATCH_PAGE_SIGNAL(receivers_, NotifyRendererIsBloated, page_cu->id());
RecordBloatedRendererHandling(
BloatedRendererHandlingInResourceCoordinator::kForwardedToBrowser);
} else {
RecordBloatedRendererHandling(
BloatedRendererHandlingInResourceCoordinator::
kIgnoredDueToMultiplePages);
}
}
}
void PageSignalGeneratorImpl::BindToInterface( void PageSignalGeneratorImpl::BindToInterface(
resource_coordinator::mojom::PageSignalGeneratorRequest request, resource_coordinator::mojom::PageSignalGeneratorRequest request,
const service_manager::BindSourceInfo& source_info) { const service_manager::BindSourceInfo& source_info) {
......
...@@ -64,6 +64,8 @@ class PageSignalGeneratorImpl : public CoordinationUnitGraphObserver, ...@@ -64,6 +64,8 @@ class PageSignalGeneratorImpl : public CoordinationUnitGraphObserver,
const mojom::Event event) override; const mojom::Event event) override;
void OnPageEventReceived(const PageCoordinationUnitImpl* page_cu, void OnPageEventReceived(const PageCoordinationUnitImpl* page_cu,
const mojom::Event event) override; const mojom::Event event) override;
void OnProcessEventReceived(const ProcessCoordinationUnitImpl* page_cu,
const mojom::Event event) override;
void BindToInterface( void BindToInterface(
resource_coordinator::mojom::PageSignalGeneratorRequest request, resource_coordinator::mojom::PageSignalGeneratorRequest request,
......
...@@ -7,6 +7,8 @@ ...@@ -7,6 +7,8 @@
#include "base/run_loop.h" #include "base/run_loop.h"
#include "base/test/scoped_feature_list.h" #include "base/test/scoped_feature_list.h"
#include "base/test/simple_test_tick_clock.h" #include "base/test/simple_test_tick_clock.h"
#include "mojo/public/cpp/bindings/interface_request.h"
#include "mojo/public/cpp/bindings/strong_binding.h"
#include "services/resource_coordinator/coordination_unit/coordination_unit_test_harness.h" #include "services/resource_coordinator/coordination_unit/coordination_unit_test_harness.h"
#include "services/resource_coordinator/coordination_unit/frame_coordination_unit_impl.h" #include "services/resource_coordinator/coordination_unit/frame_coordination_unit_impl.h"
#include "services/resource_coordinator/coordination_unit/mock_coordination_unit_graphs.h" #include "services/resource_coordinator/coordination_unit/mock_coordination_unit_graphs.h"
...@@ -18,6 +20,8 @@ ...@@ -18,6 +20,8 @@
#include "testing/gmock/include/gmock/gmock.h" #include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h" #include "testing/gtest/include/gtest/gtest.h"
using ::testing::_;
namespace resource_coordinator { namespace resource_coordinator {
class MockPageSignalGeneratorImpl : public PageSignalGeneratorImpl { class MockPageSignalGeneratorImpl : public PageSignalGeneratorImpl {
...@@ -50,6 +54,7 @@ class MockPageSignalReceiver : public mojom::PageSignalReceiver { ...@@ -50,6 +54,7 @@ class MockPageSignalReceiver : public mojom::PageSignalReceiver {
mojom::LifecycleState) override {} mojom::LifecycleState) override {}
MOCK_METHOD1(NotifyNonPersistentNotificationCreated, MOCK_METHOD1(NotifyNonPersistentNotificationCreated,
void(const CoordinationUnitID& page_cu_id)); void(const CoordinationUnitID& page_cu_id));
MOCK_METHOD1(NotifyRendererIsBloated, void(const CoordinationUnitID& cu_id));
private: private:
mojo::Binding<mojom::PageSignalReceiver> binding_; mojo::Binding<mojom::PageSignalReceiver> binding_;
...@@ -309,4 +314,40 @@ TEST_F(PageSignalGeneratorImplTest, NonPersistentNotificationCreatedEvent) { ...@@ -309,4 +314,40 @@ TEST_F(PageSignalGeneratorImplTest, NonPersistentNotificationCreatedEvent) {
::testing::Mock::VerifyAndClear(&mock_receiver); ::testing::Mock::VerifyAndClear(&mock_receiver);
} }
TEST_F(PageSignalGeneratorImplTest, NotifyRendererIsBloatedSinglePage) {
MockSinglePageInSingleProcessCoordinationUnitGraph cu_graph(
coordination_unit_graph());
auto* process = cu_graph.process.get();
auto* psg = page_signal_generator();
// Create a mock receiver and register it against the psg.
mojom::PageSignalReceiverPtr mock_receiver_ptr;
MockPageSignalReceiver mock_receiver(mojo::MakeRequest(&mock_receiver_ptr));
psg->AddReceiver(std::move(mock_receiver_ptr));
base::RunLoop run_loop;
EXPECT_CALL(mock_receiver, NotifyRendererIsBloated(_));
process->OnRendererIsBloated();
run_loop.RunUntilIdle();
::testing::Mock::VerifyAndClear(&mock_receiver);
}
TEST_F(PageSignalGeneratorImplTest, NotifyRendererIsBloatedMultiplePages) {
MockMultiplePagesInSingleProcessCoordinationUnitGraph cu_graph(
coordination_unit_graph());
auto* process = cu_graph.process.get();
auto* psg = page_signal_generator();
// Create a mock receiver and register it against the psg.
mojom::PageSignalReceiverPtr mock_receiver_ptr;
MockPageSignalReceiver mock_receiver(mojo::MakeRequest(&mock_receiver_ptr));
psg->AddReceiver(std::move(mock_receiver_ptr));
base::RunLoop run_loop;
EXPECT_CALL(mock_receiver, NotifyRendererIsBloated(_)).Times(0);
process->OnRendererIsBloated();
run_loop.RunUntilIdle();
::testing::Mock::VerifyAndClear(&mock_receiver);
}
} // namespace resource_coordinator } // namespace resource_coordinator
...@@ -108,6 +108,12 @@ interface ProcessCoordinationUnit { ...@@ -108,6 +108,12 @@ interface ProcessCoordinationUnit {
SetLaunchTime(mojo_base.mojom.Time launch_time); SetLaunchTime(mojo_base.mojom.Time launch_time);
SetMainThreadTaskLoadIsLow(bool main_thread_task_load_is_low); SetMainThreadTaskLoadIsLow(bool main_thread_task_load_is_low);
SetPID(int64 pid); SetPID(int64 pid);
// Event signals.
// Fired when one of the renderer heaps is about to exceed its limit.
// If nothing is done, then the renderer is likely to crash with OOM.
OnRendererIsBloated();
}; };
// There is exactly one SystemCoordinationUnit at all times. // There is exactly one SystemCoordinationUnit at all times.
......
...@@ -16,6 +16,7 @@ import "mojo/public/mojom/base/time.mojom"; ...@@ -16,6 +16,7 @@ import "mojo/public/mojom/base/time.mojom";
// PageSignalGenerator::AddReceiver. // PageSignalGenerator::AddReceiver.
interface PageSignalReceiver { interface PageSignalReceiver {
NotifyPageAlmostIdle(CoordinationUnitID page_cu_id); NotifyPageAlmostIdle(CoordinationUnitID page_cu_id);
NotifyRendererIsBloated(CoordinationUnitID page_cu_id);
SetExpectedTaskQueueingDuration(CoordinationUnitID page_cu_id, SetExpectedTaskQueueingDuration(CoordinationUnitID page_cu_id,
mojo_base.mojom.TimeDelta duration); mojo_base.mojom.TimeDelta duration);
SetLifecycleState(CoordinationUnitID page_cu_id, SetLifecycleState(CoordinationUnitID page_cu_id,
......
...@@ -17,6 +17,8 @@ enum Event { ...@@ -17,6 +17,8 @@ enum Event {
// This signal is sent to a SystemCU when all ProcessCU CPU usage estimates // This signal is sent to a SystemCU when all ProcessCU CPU usage estimates
// have been updated and are coherent. // have been updated and are coherent.
kProcessCPUUsageReady, kProcessCPUUsageReady,
// This signal is set to the renderer ProcessCU.
kRendererIsBloated,
}; };
// Defines the Storage property keys that can be get/set on the // Defines the Storage property keys that can be get/set on the
......
...@@ -5,6 +5,7 @@ ...@@ -5,6 +5,7 @@
#include "third_party/blink/renderer/controller/bloated_renderer_detector.h" #include "third_party/blink/renderer/controller/bloated_renderer_detector.h"
#include "third_party/blink/renderer/platform/bindings/v8_per_isolate_data.h" #include "third_party/blink/renderer/platform/bindings/v8_per_isolate_data.h"
#include "third_party/blink/renderer/platform/instrumentation/resource_coordinator/renderer_resource_coordinator.h"
namespace blink { namespace blink {
...@@ -26,7 +27,7 @@ BloatedRendererDetector::OnNearV8HeapLimitOnMainThreadImpl() { ...@@ -26,7 +27,7 @@ BloatedRendererDetector::OnNearV8HeapLimitOnMainThreadImpl() {
if (uptime.InMinutes() < kMinimumUptimeInMinutes) { if (uptime.InMinutes() < kMinimumUptimeInMinutes) {
return NearV8HeapLimitHandling::kIgnoredDueToSmallUptime; return NearV8HeapLimitHandling::kIgnoredDueToSmallUptime;
} }
// TODO(ulan): Send message to the browser. RendererResourceCoordinator::Get().OnRendererIsBloated();
return NearV8HeapLimitHandling::kForwardedToBrowser; return NearV8HeapLimitHandling::kForwardedToBrowser;
} }
......
...@@ -62,4 +62,10 @@ void RendererResourceCoordinator::SetMainThreadTaskLoadIsLow( ...@@ -62,4 +62,10 @@ void RendererResourceCoordinator::SetMainThreadTaskLoadIsLow(
service_->SetMainThreadTaskLoadIsLow(main_thread_task_load_is_low); service_->SetMainThreadTaskLoadIsLow(main_thread_task_load_is_low);
} }
void RendererResourceCoordinator::OnRendererIsBloated() {
if (!service_)
return;
service_->OnRendererIsBloated();
}
} // namespace blink } // namespace blink
...@@ -30,6 +30,7 @@ class PLATFORM_EXPORT RendererResourceCoordinator ...@@ -30,6 +30,7 @@ class PLATFORM_EXPORT RendererResourceCoordinator
void SetExpectedTaskQueueingDuration(base::TimeDelta); void SetExpectedTaskQueueingDuration(base::TimeDelta);
void SetMainThreadTaskLoadIsLow(bool); void SetMainThreadTaskLoadIsLow(bool);
void OnRendererIsBloated();
protected: protected:
RendererResourceCoordinator(); RendererResourceCoordinator();
......
...@@ -3311,6 +3311,17 @@ uploading your change for review. These are checked by presubmit scripts. ...@@ -3311,6 +3311,17 @@ uploading your change for review. These are checked by presubmit scripts.
<int value="5" label="Blacklist disabled."/> <int value="5" label="Blacklist disabled."/>
</enum> </enum>
<enum name="BloatedRendererHandlingInBrowser">
<int value="0" label="Reloaded the bloated tab"/>
<int value="1" label="Cannot reload the bloated tab"/>
<int value="2" label="Cannot shutdown the renderer process"/>
</enum>
<enum name="BloatedRendererHandlingInResourceCoordinator">
<int value="0" label="Forwarded handling to the browser process"/>
<int value="1" label="Ignored due to multiple pages in the renderer process"/>
</enum>
<enum name="BlobBrokenReason"> <enum name="BlobBrokenReason">
<int value="0" label="Unknown"/> <int value="0" label="Unknown"/>
<int value="1" label="There is not enough memory to store this blob"/> <int value="1" label="There is not enough memory to store this blob"/>
...@@ -8742,6 +8742,23 @@ uploading your change for review. ...@@ -8742,6 +8742,23 @@ uploading your change for review.
</summary> </summary>
</histogram> </histogram>
<histogram name="BloatedRenderer.HandlingInBrowser"
enum="BloatedRendererHandlingInBrowser">
<owner>ulan@chromium.org</owner>
<summary>
Records how a bloated renderer was handled in the browser process.
</summary>
</histogram>
<histogram name="BloatedRenderer.HandlingInResourceCoordinator"
enum="BloatedRendererHandlingInResourceCoordinator">
<owner>ulan@chromium.org</owner>
<summary>
Records how a bloated renderer was handled in the resource coordinator
service.
</summary>
</histogram>
<histogram name="BloatedRenderer.V8.NearV8HeapLimitHandling" <histogram name="BloatedRenderer.V8.NearV8HeapLimitHandling"
enum="NearV8HeapLimitHandling"> enum="NearV8HeapLimitHandling">
<owner>ulan@chromium.org</owner> <owner>ulan@chromium.org</owner>
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment