Commit da6644e3 authored by Matthew Halpern's avatar Matthew Halpern Committed by Commit Bot

[GRC] Decouple Render Process CPU Measurement from CU Graph

Currently GRC's process CPU measurement mechanisms are tightly
integrated with the process coordination unit implementation.
This CL moves the CPU usage measurement from the resource_coordinator
service to the content_browser service.

Decoupling process CPU measurement from the CU model allows the
coordination units to focus on representing browser state within GRC,
rather than updating it. In GRC terms, the migrated render process CPU
measurement infrastructure is a "probe" that externally triggers
updates to the CU graph.

A new feature flag has been added for enabling render process
CPU usage profiling. To do so, run:

out/Default/chrome 
  --enable-features=GlobalResourceCoordinator,GRCRenderProcessCPUProfiling \
  http://example.com

BUG=691886,741768

Change-Id: I2fc7609c6963776a167c5f76abe078dedb0e2efe
Reviewed-on: https://chromium-review.googlesource.com/565906
Commit-Queue: Matthew Halpern <matthalp@google.com>
Reviewed-by: default avatarZhen Wang <zhenw@chromium.org>
Reviewed-by: default avatarScott Violet <sky@chromium.org>
Reviewed-by: default avatarOystein Eftevaag <oysteine@chromium.org>
Cr-Commit-Position: refs/heads/master@{#488352}
parent 96c3f1f1
...@@ -1171,6 +1171,8 @@ split_static_library("browser") { ...@@ -1171,6 +1171,8 @@ split_static_library("browser") {
"renderer_host/chrome_render_widget_host_view_mac_history_swiper.mm", "renderer_host/chrome_render_widget_host_view_mac_history_swiper.mm",
"renderer_preferences_util.cc", "renderer_preferences_util.cc",
"renderer_preferences_util.h", "renderer_preferences_util.h",
"resource_coordinator/resource_coordinator_render_process_probe.cc",
"resource_coordinator/resource_coordinator_render_process_probe.h",
"resource_coordinator/resource_coordinator_web_contents_observer.cc", "resource_coordinator/resource_coordinator_web_contents_observer.cc",
"resource_coordinator/resource_coordinator_web_contents_observer.h", "resource_coordinator/resource_coordinator_web_contents_observer.h",
"resource_delegate_mac.h", "resource_delegate_mac.h",
......
...@@ -89,6 +89,7 @@ ...@@ -89,6 +89,7 @@
#include "chrome/browser/profiles/profile_attributes_storage.h" #include "chrome/browser/profiles/profile_attributes_storage.h"
#include "chrome/browser/profiles/profile_manager.h" #include "chrome/browser/profiles/profile_manager.h"
#include "chrome/browser/profiles/profiles_state.h" #include "chrome/browser/profiles/profiles_state.h"
#include "chrome/browser/resource_coordinator/resource_coordinator_render_process_probe.h"
#include "chrome/browser/resource_coordinator/tab_manager.h" #include "chrome/browser/resource_coordinator/tab_manager.h"
#include "chrome/browser/sessions/chrome_serialized_navigation_driver.h" #include "chrome/browser/sessions/chrome_serialized_navigation_driver.h"
#include "chrome/browser/shell_integration.h" #include "chrome/browser/shell_integration.h"
...@@ -1957,6 +1958,9 @@ bool ChromeBrowserMainParts::MainMessageLoopRun(int* result_code) { ...@@ -1957,6 +1958,9 @@ bool ChromeBrowserMainParts::MainMessageLoopRun(int* result_code) {
performance_monitor::PerformanceMonitor::GetInstance()->StartGatherCycle(); performance_monitor::PerformanceMonitor::GetInstance()->StartGatherCycle();
resource_coordinator::ResourceCoordinatorRenderProcessProbe::GetInstance()
->StartGatherCycle();
metrics::MetricsService::SetExecutionPhase( metrics::MetricsService::SetExecutionPhase(
metrics::ExecutionPhase::MAIN_MESSAGE_LOOP_RUN, metrics::ExecutionPhase::MAIN_MESSAGE_LOOP_RUN,
g_browser_process->local_state()); g_browser_process->local_state());
......
// Copyright 2017 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/resource_coordinator/resource_coordinator_render_process_probe.h"
#include <vector>
#include "base/bind.h"
#include "base/memory/ptr_util.h"
#include "base/metrics/field_trial_params.h"
#include "base/strings/string_number_conversions.h"
#include "base/values.h"
#include "build/build_config.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/render_process_host.h"
#include "content/public/common/service_manager_connection.h"
#include "services/resource_coordinator/public/cpp/resource_coordinator_features.h"
#include "services/resource_coordinator/public/cpp/resource_coordinator_interface.h"
#include "services/resource_coordinator/public/interfaces/coordination_unit.mojom.h"
#if defined(OS_MACOSX)
#include "content/public/browser/browser_child_process_host.h"
#endif
namespace resource_coordinator {
namespace {
const int kDefaultMeasurementIntervalInSeconds = 1;
base::LazyInstance<ResourceCoordinatorRenderProcessProbe>::DestructorAtExit
g_probe = LAZY_INSTANCE_INITIALIZER;
} // namespace
RenderProcessInfo::RenderProcessInfo() = default;
RenderProcessInfo::~RenderProcessInfo() = default;
RenderProcessMetricsHandler::RenderProcessMetricsHandler() = default;
RenderProcessMetricsHandler::~RenderProcessMetricsHandler() = default;
class ResourceCoordinatorRenderProcessMetricsHandler
: public RenderProcessMetricsHandler {
public:
ResourceCoordinatorRenderProcessMetricsHandler() = default;
~ResourceCoordinatorRenderProcessMetricsHandler() override = default;
// Send collected metrics back to the |resource_coordinator| service
// and initiates another render process metrics gather cycle.
bool HandleMetrics(
const RenderProcessInfoMap& render_process_info_map) override {
for (auto& render_process_info_map_entry : render_process_info_map) {
auto& render_process_info = render_process_info_map_entry.second;
render_process_info.host->GetProcessResourceCoordinator()->SetProperty(
mojom::PropertyType::kCPUUsage,
base::MakeUnique<base::Value>(render_process_info.cpu_usage));
}
return true;
}
};
ResourceCoordinatorRenderProcessProbe::ResourceCoordinatorRenderProcessProbe()
: metrics_handler_(
base::MakeUnique<ResourceCoordinatorRenderProcessMetricsHandler>()),
interval_ms_(
base::TimeDelta::FromSeconds(kDefaultMeasurementIntervalInSeconds)) {}
ResourceCoordinatorRenderProcessProbe::
~ResourceCoordinatorRenderProcessProbe() = default;
// static
ResourceCoordinatorRenderProcessProbe*
ResourceCoordinatorRenderProcessProbe::GetInstance() {
return g_probe.Pointer();
}
// static
bool ResourceCoordinatorRenderProcessProbe::IsEnabled() {
// Check that service_manager is active, GRC is enabled,
// and render process CPU profiling is enabled.
return content::ServiceManagerConnection::GetForProcess() != nullptr &&
resource_coordinator::IsResourceCoordinatorEnabled() &&
base::FeatureList::IsEnabled(features::kGRCRenderProcessCPUProfiling);
}
void ResourceCoordinatorRenderProcessProbe::StartGatherCycle() {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
if (!ResourceCoordinatorRenderProcessProbe::IsEnabled()) {
return;
}
timer_.Start(FROM_HERE, base::TimeDelta(), this,
&ResourceCoordinatorRenderProcessProbe::
RegisterAliveRenderProcessesOnUIThread);
}
void ResourceCoordinatorRenderProcessProbe::
RegisterAliveRenderProcessesOnUIThread() {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
++current_gather_cycle_;
for (content::RenderProcessHost::iterator rph_iter =
content::RenderProcessHost::AllHostsIterator();
!rph_iter.IsAtEnd(); rph_iter.Advance()) {
content::RenderProcessHost* host = rph_iter.GetCurrentValue();
base::ProcessHandle handle = host->GetHandle();
// Process may not be valid yet.
if (handle == base::kNullProcessHandle) {
continue;
}
auto& render_process_info = render_process_info_map_[handle];
render_process_info.last_gather_cycle_active = current_gather_cycle_;
if (render_process_info.metrics.get() == nullptr) {
#if defined(OS_MACOSX)
render_process_info.metrics = base::ProcessMetrics::CreateProcessMetrics(
handle, content::BrowserChildProcessHost::GetPortProvider());
#else
render_process_info.metrics =
base::ProcessMetrics::CreateProcessMetrics(handle);
#endif
render_process_info.host = host;
}
}
content::BrowserThread::PostTask(
content::BrowserThread::IO, FROM_HERE,
base::BindOnce(&ResourceCoordinatorRenderProcessProbe::
CollectRenderProcessMetricsOnIOThread,
base::Unretained(this)));
}
void ResourceCoordinatorRenderProcessProbe::
CollectRenderProcessMetricsOnIOThread() {
DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
RenderProcessInfoMap::iterator iter = render_process_info_map_.begin();
while (iter != render_process_info_map_.end()) {
auto& render_process_info = iter->second;
// If the last gather cycle the render process was marked as active is
// not current then it is assumed dead and should not be measured anymore.
if (render_process_info.last_gather_cycle_active == current_gather_cycle_) {
render_process_info.cpu_usage =
render_process_info.metrics->GetPlatformIndependentCPUUsage();
++iter;
} else {
render_process_info_map_.erase(iter++);
continue;
}
}
content::BrowserThread::PostTask(
content::BrowserThread::UI, FROM_HERE,
base::BindOnce(&ResourceCoordinatorRenderProcessProbe::
HandleRenderProcessMetricsOnUIThread,
base::Unretained(this)));
}
void ResourceCoordinatorRenderProcessProbe::
HandleRenderProcessMetricsOnUIThread() {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
if (metrics_handler_->HandleMetrics(render_process_info_map_)) {
timer_.Start(FROM_HERE, interval_ms_, this,
&ResourceCoordinatorRenderProcessProbe::
RegisterAliveRenderProcessesOnUIThread);
}
}
bool ResourceCoordinatorRenderProcessProbe::
AllRenderProcessMeasurementsAreCurrentForTesting() const {
for (auto& render_process_info_map_entry : render_process_info_map_) {
auto& render_process_info = render_process_info_map_entry.second;
if (render_process_info.last_gather_cycle_active != current_gather_cycle_ ||
render_process_info.cpu_usage < 0.0) {
return false;
}
}
return true;
}
} // namespace resource_coordinator
// Copyright 2017 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CHROME_BROWSER_RESOURCE_COORDINATOR_RESOURCE_COORDINATOR_RENDER_PROCESS_PROBE_H_
#define CHROME_BROWSER_RESOURCE_COORDINATOR_RESOURCE_COORDINATOR_RENDER_PROCESS_PROBE_H_
#include <map>
#include <memory>
#include "base/lazy_instance.h"
#include "base/macros.h"
#include "base/process/process_handle.h"
#include "base/process/process_metrics.h"
#include "base/timer/timer.h"
namespace content {
class RenderProcessHost;
}
namespace resource_coordinator {
struct RenderProcessInfo {
RenderProcessInfo();
~RenderProcessInfo();
double cpu_usage = -1.0;
content::RenderProcessHost* host = nullptr;
size_t last_gather_cycle_active;
std::unique_ptr<base::ProcessMetrics> metrics;
};
using RenderProcessInfoMap = std::map<base::ProcessHandle, RenderProcessInfo>;
// A delegate class for handling metrics collected after each
// |ResourceCoordinatorRenderProcessProbe| gather cycle.
class RenderProcessMetricsHandler {
public:
RenderProcessMetricsHandler();
virtual ~RenderProcessMetricsHandler();
// Handle collected metrics. Returns |true| if the
// |ResourceCoordinatorRenderProcessProbe| should initiate another
// metrics collection gather cycle.
virtual bool HandleMetrics(
const RenderProcessInfoMap& render_process_info_map) = 0;
};
// |ResourceCoordinatorRenderProcessProbe| collects measurements about render
// processes and propagates them to the |resource_coordinator| service.
// Currently this is only supported for Chrome Metrics experiments.
class ResourceCoordinatorRenderProcessProbe {
public:
// Returns the current |ResourceCoordinatorRenderProcessProbe| instance
// if one exists; otherwise it constructs a new instance.
static ResourceCoordinatorRenderProcessProbe* GetInstance();
static bool IsEnabled();
// Render process metrics collection cycle:
// (0) Initialize and begin render process metrics collection.
void StartGatherCycle();
// (1) Identify all of the render processes that are active to measure.
// Child render processes can only be discovered in the browser's UI thread.
void RegisterAliveRenderProcessesOnUIThread();
// (2) Collect render process metrics.
void CollectRenderProcessMetricsOnIOThread();
// (3) Send the collected render process metrics to the appropriate
// coordination units in the |resource_coordinator| service. Then
// initiates the next render process metrics collection cycle, which
// consists of a delayed call to perform (1) via a timer.
void HandleRenderProcessMetricsOnUIThread();
private:
FRIEND_TEST_ALL_PREFIXES(ResourceCoordinatorRenderProcessProbeBrowserTest,
TrackAndMeasureActiveRenderProcesses);
friend struct base::LazyInstanceTraitsBase<
ResourceCoordinatorRenderProcessProbe>;
ResourceCoordinatorRenderProcessProbe();
~ResourceCoordinatorRenderProcessProbe();
// Delegate for handling metrics collected after each gather cycle.
std::unique_ptr<RenderProcessMetricsHandler> metrics_handler_;
// A map of currently running ProcessHandles to Process.
RenderProcessInfoMap render_process_info_map_;
// Time duration between measurements in milliseconds.
base::TimeDelta interval_ms_;
// Timer to signal the |ResourceCoordinatorRenderProcessProbe| instance
// to conduct its measurements as a regular interval;
base::OneShotTimer timer_;
// Number of measurements collected so far.
size_t current_gather_cycle_ = 0u;
// Settings/getters for testing.
void set_render_process_metrics_handler_for_testing(
std::unique_ptr<RenderProcessMetricsHandler> metrics_handler) {
metrics_handler_ = std::move(metrics_handler);
}
const RenderProcessInfoMap& render_process_info_map_for_testing() const {
return render_process_info_map_;
}
size_t current_gather_cycle_for_testing() const {
return current_gather_cycle_;
}
// Returns |true| if all of the elements in |render_process_info_map_|
// are up-to-date with the |current_gather_cycle_|.
bool AllRenderProcessMeasurementsAreCurrentForTesting() const;
DISALLOW_COPY_AND_ASSIGN(ResourceCoordinatorRenderProcessProbe);
};
} // namespace resource_coordinator
#endif // CHROME_BROWSER_RESOURCE_COORDINATOR_RESOURCE_COORDINATOR_RENDER_PROCESS_PROBE_H_
// Copyright 2017 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <string>
#include "base/feature_list.h"
#include "base/macros.h"
#include "base/memory/ptr_util.h"
#include "base/run_loop.h"
#include "base/test/scoped_feature_list.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/resource_coordinator/resource_coordinator_render_process_probe.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/tabs/tab_strip_model.h"
#include "chrome/test/base/in_process_browser_test.h"
#include "chrome/test/base/ui_test_utils.h"
#include "content/public/browser/render_process_host.h"
#include "content/public/test/test_utils.h"
#include "services/resource_coordinator/public/cpp/resource_coordinator_features.h"
#include "url/gurl.h"
namespace resource_coordinator {
class MockResourceCoordinatorRenderProcessMetricsHandler
: public RenderProcessMetricsHandler {
public:
MockResourceCoordinatorRenderProcessMetricsHandler() = default;
~MockResourceCoordinatorRenderProcessMetricsHandler() override = default;
bool HandleMetrics(
const RenderProcessInfoMap& render_process_info_map) override {
base::MessageLoop::current()->QuitWhenIdle();
return false;
}
private:
DISALLOW_COPY_AND_ASSIGN(MockResourceCoordinatorRenderProcessMetricsHandler);
};
class ResourceCoordinatorRenderProcessProbeBrowserTest
: public InProcessBrowserTest {
public:
ResourceCoordinatorRenderProcessProbeBrowserTest() = default;
~ResourceCoordinatorRenderProcessProbeBrowserTest() override = default;
void StartGatherCycleAndWait() {
base::RunLoop run_loop;
probe_->StartGatherCycle();
run_loop.Run();
}
void set_probe(
resource_coordinator::ResourceCoordinatorRenderProcessProbe* probe) {
probe_ = probe;
}
private:
resource_coordinator::ResourceCoordinatorRenderProcessProbe* probe_;
DISALLOW_COPY_AND_ASSIGN(ResourceCoordinatorRenderProcessProbeBrowserTest);
};
IN_PROC_BROWSER_TEST_F(ResourceCoordinatorRenderProcessProbeBrowserTest,
TrackAndMeasureActiveRenderProcesses) {
// Ensure that the |resource_coordinator| service is enabled.
base::test::ScopedFeatureList feature_list;
feature_list.InitWithFeatures({features::kGlobalResourceCoordinator,
features::kGRCRenderProcessCPUProfiling},
{});
resource_coordinator::ResourceCoordinatorRenderProcessProbe probe;
probe.set_render_process_metrics_handler_for_testing(
base::MakeUnique<MockResourceCoordinatorRenderProcessMetricsHandler>());
set_probe(&probe);
ASSERT_TRUE(embedded_test_server()->Start());
EXPECT_EQ(0u, probe.current_gather_cycle_for_testing());
// A tab is already open when the test begins.
StartGatherCycleAndWait();
EXPECT_EQ(1u, probe.current_gather_cycle_for_testing());
EXPECT_EQ(1u, probe.render_process_info_map_for_testing().size());
EXPECT_TRUE(probe.AllRenderProcessMeasurementsAreCurrentForTesting());
// Open a second tab and complete a navigation.
ui_test_utils::NavigateToURLWithDisposition(
browser(), embedded_test_server()->GetURL("/title1.html"),
WindowOpenDisposition::NEW_FOREGROUND_TAB,
ui_test_utils::BROWSER_TEST_WAIT_FOR_TAB |
ui_test_utils::BROWSER_TEST_WAIT_FOR_NAVIGATION);
StartGatherCycleAndWait();
EXPECT_EQ(2u, probe.current_gather_cycle_for_testing());
EXPECT_EQ(2u, probe.render_process_info_map_for_testing().size());
EXPECT_TRUE(probe.AllRenderProcessMeasurementsAreCurrentForTesting());
// Kill one of the two open tabs.
EXPECT_TRUE(browser()
->tab_strip_model()
->GetActiveWebContents()
->GetRenderProcessHost()
->FastShutdownIfPossible());
StartGatherCycleAndWait();
EXPECT_EQ(3u, probe.current_gather_cycle_for_testing());
EXPECT_EQ(1u, probe.render_process_info_map_for_testing().size());
EXPECT_TRUE(probe.AllRenderProcessMeasurementsAreCurrentForTesting());
}
} // namespace resource_coordinator
...@@ -1424,6 +1424,7 @@ test("browser_tests") { ...@@ -1424,6 +1424,7 @@ test("browser_tests") {
"../browser/renderer_context_menu/spelling_menu_observer_browsertest.cc", "../browser/renderer_context_menu/spelling_menu_observer_browsertest.cc",
"../browser/renderer_host/render_process_host_chrome_browsertest.cc", "../browser/renderer_host/render_process_host_chrome_browsertest.cc",
"../browser/repost_form_warning_browsertest.cc", "../browser/repost_form_warning_browsertest.cc",
"../browser/resource_coordinator/resource_coordinator_render_process_probe_browsertest.cc",
"../browser/resource_coordinator/tab_manager_browsertest.cc", "../browser/resource_coordinator/tab_manager_browsertest.cc",
"../browser/resource_coordinator/tab_manager_observer_browsertest.cc", "../browser/resource_coordinator/tab_manager_observer_browsertest.cc",
"../browser/safe_browsing/chrome_cleaner/reporter_runner_browsertest_win.cc", "../browser/safe_browsing/chrome_cleaner/reporter_runner_browsertest_win.cc",
......
...@@ -10,6 +10,10 @@ namespace features { ...@@ -10,6 +10,10 @@ namespace features {
const base::Feature kGlobalResourceCoordinator{ const base::Feature kGlobalResourceCoordinator{
"GlobalResourceCoordinator", base::FEATURE_ENABLED_BY_DEFAULT}; "GlobalResourceCoordinator", base::FEATURE_ENABLED_BY_DEFAULT};
// Enable render process CPU profiling for GRC.
const base::Feature kGRCRenderProcessCPUProfiling{
"GRCRenderProcessCPUProfiling", base::FEATURE_DISABLED_BY_DEFAULT};
} // namespace features } // namespace features
namespace resource_coordinator { namespace resource_coordinator {
......
...@@ -17,6 +17,8 @@ namespace features { ...@@ -17,6 +17,8 @@ namespace features {
// in the .cc file. // in the .cc file.
extern const SERVICES_RESOURCE_COORDINATOR_PUBLIC_CPP_EXPORT base::Feature extern const SERVICES_RESOURCE_COORDINATOR_PUBLIC_CPP_EXPORT base::Feature
kGlobalResourceCoordinator; kGlobalResourceCoordinator;
extern const SERVICES_RESOURCE_COORDINATOR_PUBLIC_CPP_EXPORT base::Feature
kGRCRenderProcessCPUProfiling;
} // namespace features } // namespace features
......
...@@ -40,6 +40,7 @@ class SERVICES_RESOURCE_COORDINATOR_PUBLIC_CPP_EXPORT ...@@ -40,6 +40,7 @@ class SERVICES_RESOURCE_COORDINATOR_PUBLIC_CPP_EXPORT
std::unique_ptr<base::Value> value); std::unique_ptr<base::Value> value);
void AddChild(const ResourceCoordinatorInterface& child); void AddChild(const ResourceCoordinatorInterface& child);
void RemoveChild(const ResourceCoordinatorInterface& child); void RemoveChild(const ResourceCoordinatorInterface& child);
CoordinationUnitID id() const { return cu_id_; } CoordinationUnitID id() const { return cu_id_; }
private: private:
......
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