Commit e5aa7962 authored by Sigurdur Asgeirsson's avatar Sigurdur Asgeirsson Committed by Commit Bot

RC: Index render process map by RPH ID instead of handle.

This avoids creating a new entry in the map for each process on each
cycle on Windows, which was happening as of my last change.
Add a state variable to keep track of when a measurement cycle is
underway in preparation for allowing measurement requests at arbitrary
times. Replace the delegate class with a simple virtual function, and
use protected access control in combination with a Testing* subclass
to expose class' internal state for testing. This allows removing all
test-specific code from the class declaration.

Bug: 755840
Change-Id: I7658a355525fffe92327bb85151ba124cc81d371
Reviewed-on: https://chromium-review.googlesource.com/1015299
Commit-Queue: Sigurður Ásgeirsson <siggi@chromium.org>
Reviewed-by: default avatarChris Hamilton <chrisha@chromium.org>
Cr-Commit-Position: refs/heads/master@{#551794}
parent 42ed1439
...@@ -36,43 +36,8 @@ RenderProcessInfo::RenderProcessInfo() = default; ...@@ -36,43 +36,8 @@ RenderProcessInfo::RenderProcessInfo() = default;
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;
// TODO(oysteine): Move the multiplier used to avoid precision loss
// into a shared location, when this property gets used.
// Note that the RPH may have been deleted while the CPU metrics were
// acquired on a blocking thread.
content::RenderProcessHost* host = content::RenderProcessHost::FromID(
render_process_info.render_process_host_id);
if (host) {
host->GetProcessResourceCoordinator()->SetCPUUsage(
render_process_info.cpu_usage);
}
}
return true;
}
};
ResourceCoordinatorRenderProcessProbe::ResourceCoordinatorRenderProcessProbe() ResourceCoordinatorRenderProcessProbe::ResourceCoordinatorRenderProcessProbe()
: metrics_handler_( : interval_(kDefaultMeasurementInterval) {
std::make_unique<ResourceCoordinatorRenderProcessMetricsHandler>()),
interval_(kDefaultMeasurementInterval) {
UpdateWithFieldTrialParams(); UpdateWithFieldTrialParams();
} }
...@@ -108,6 +73,7 @@ void ResourceCoordinatorRenderProcessProbe::StartGatherCycle() { ...@@ -108,6 +73,7 @@ void ResourceCoordinatorRenderProcessProbe::StartGatherCycle() {
void ResourceCoordinatorRenderProcessProbe:: void ResourceCoordinatorRenderProcessProbe::
RegisterAliveRenderProcessesOnUIThread() { RegisterAliveRenderProcessesOnUIThread() {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI); DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
DCHECK(!is_gathering_);
++current_gather_cycle_; ++current_gather_cycle_;
...@@ -120,14 +86,15 @@ void ResourceCoordinatorRenderProcessProbe:: ...@@ -120,14 +86,15 @@ void ResourceCoordinatorRenderProcessProbe::
continue; continue;
} }
auto& render_process_info = render_process_info_map_[host->GetID()];
render_process_info.last_gather_cycle_active = current_gather_cycle_;
if (render_process_info.metrics.get() == nullptr) {
DCHECK(!render_process_info.process.IsValid());
// Duplicate the process to retain ownership of it through the thread // Duplicate the process to retain ownership of it through the thread
// bouncing. // bouncing.
base::Process process(host->GetProcess().Duplicate()); render_process_info.process = host->GetProcess().Duplicate();
auto& render_process_info = render_process_info_map_[process.Handle()];
render_process_info.last_gather_cycle_active = current_gather_cycle_;
render_process_info.process = std::move(process);
if (render_process_info.metrics.get() == nullptr) {
#if defined(OS_MACOSX) #if defined(OS_MACOSX)
render_process_info.metrics = base::ProcessMetrics::CreateProcessMetrics( render_process_info.metrics = base::ProcessMetrics::CreateProcessMetrics(
render_process_info.process.Handle(), render_process_info.process.Handle(),
...@@ -140,6 +107,8 @@ void ResourceCoordinatorRenderProcessProbe:: ...@@ -140,6 +107,8 @@ void ResourceCoordinatorRenderProcessProbe::
} }
} }
is_gathering_ = true;
content::BrowserThread::PostTask( content::BrowserThread::PostTask(
content::BrowserThread::IO, FROM_HERE, content::BrowserThread::IO, FROM_HERE,
base::BindOnce(&ResourceCoordinatorRenderProcessProbe:: base::BindOnce(&ResourceCoordinatorRenderProcessProbe::
...@@ -150,6 +119,7 @@ void ResourceCoordinatorRenderProcessProbe:: ...@@ -150,6 +119,7 @@ void ResourceCoordinatorRenderProcessProbe::
void ResourceCoordinatorRenderProcessProbe:: void ResourceCoordinatorRenderProcessProbe::
CollectRenderProcessMetricsOnIOThread() { CollectRenderProcessMetricsOnIOThread() {
DCHECK_CURRENTLY_ON(content::BrowserThread::IO); DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
DCHECK(is_gathering_);
RenderProcessInfoMap::iterator iter = render_process_info_map_.begin(); RenderProcessInfoMap::iterator iter = render_process_info_map_.begin();
while (iter != render_process_info_map_.end()) { while (iter != render_process_info_map_.end()) {
...@@ -177,22 +147,33 @@ void ResourceCoordinatorRenderProcessProbe:: ...@@ -177,22 +147,33 @@ void ResourceCoordinatorRenderProcessProbe::
void ResourceCoordinatorRenderProcessProbe:: void ResourceCoordinatorRenderProcessProbe::
HandleRenderProcessMetricsOnUIThread() { HandleRenderProcessMetricsOnUIThread() {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI); DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
if (metrics_handler_->HandleMetrics(render_process_info_map_)) { DCHECK(is_gathering_);
is_gathering_ = false;
if (HandleMetrics(render_process_info_map_)) {
timer_.Start(FROM_HERE, interval_, this, timer_.Start(FROM_HERE, interval_, this,
&ResourceCoordinatorRenderProcessProbe:: &ResourceCoordinatorRenderProcessProbe::
RegisterAliveRenderProcessesOnUIThread); RegisterAliveRenderProcessesOnUIThread);
} }
} }
bool ResourceCoordinatorRenderProcessProbe:: bool ResourceCoordinatorRenderProcessProbe::HandleMetrics(
AllRenderProcessMeasurementsAreCurrentForTesting() const { const RenderProcessInfoMap& render_process_info_map) {
for (auto& render_process_info_map_entry : render_process_info_map_) { for (auto& render_process_info_map_entry : render_process_info_map) {
auto& render_process_info = render_process_info_map_entry.second; auto& render_process_info = render_process_info_map_entry.second;
if (render_process_info.last_gather_cycle_active != current_gather_cycle_ || // TODO(oysteine): Move the multiplier used to avoid precision loss
render_process_info.cpu_usage < 0.0) { // into a shared location, when this property gets used.
return false;
// Note that the RPH may have been deleted while the CPU metrics were
// acquired on a blocking thread.
content::RenderProcessHost* host = content::RenderProcessHost::FromID(
render_process_info.render_process_host_id);
if (host) {
host->GetProcessResourceCoordinator()->SetCPUUsage(
render_process_info.cpu_usage);
} }
} }
return true; return true;
} }
......
...@@ -30,24 +30,13 @@ struct RenderProcessInfo { ...@@ -30,24 +30,13 @@ struct RenderProcessInfo {
std::unique_ptr<base::ProcessMetrics> metrics; std::unique_ptr<base::ProcessMetrics> metrics;
}; };
using RenderProcessInfoMap = std::map<base::ProcessHandle, RenderProcessInfo>; using RenderProcessInfoMap = std::map<int, 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 // |ResourceCoordinatorRenderProcessProbe| collects measurements about render
// processes and propagates them to the |resource_coordinator| service. // processes and propagates them to the |resource_coordinator| service.
// Currently this is only supported for Chrome Metrics experiments. // Currently this is only supported for Chrome Metrics experiments.
// The measurements are initiated and results are dispatched from the UI
// thread, while acquiring the measurements is done on the IO thread.
class ResourceCoordinatorRenderProcessProbe { class ResourceCoordinatorRenderProcessProbe {
public: public:
// Returns the current |ResourceCoordinatorRenderProcessProbe| instance // Returns the current |ResourceCoordinatorRenderProcessProbe| instance
...@@ -56,9 +45,18 @@ class ResourceCoordinatorRenderProcessProbe { ...@@ -56,9 +45,18 @@ class ResourceCoordinatorRenderProcessProbe {
static bool IsEnabled(); static bool IsEnabled();
// Render process metrics collection cycle: // Starts the automatic, timed process metrics collection cycle.
// (0) Initialize and begin render process metrics collection. // Can only be invoked from the UI thread.
void StartGatherCycle(); void StartGatherCycle();
protected:
// Internal state protected for testing.
friend struct base::LazyInstanceTraitsBase<
ResourceCoordinatorRenderProcessProbe>;
ResourceCoordinatorRenderProcessProbe();
virtual ~ResourceCoordinatorRenderProcessProbe();
// (1) Identify all of the render processes that are active to measure. // (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. // Child render processes can only be discovered in the browser's UI thread.
void RegisterAliveRenderProcessesOnUIThread(); void RegisterAliveRenderProcessesOnUIThread();
...@@ -70,19 +68,15 @@ class ResourceCoordinatorRenderProcessProbe { ...@@ -70,19 +68,15 @@ class ResourceCoordinatorRenderProcessProbe {
// consists of a delayed call to perform (1) via a timer. // consists of a delayed call to perform (1) via a timer.
void HandleRenderProcessMetricsOnUIThread(); void HandleRenderProcessMetricsOnUIThread();
private: // Handle collected metrics. Returns |true| another metrics collection gather
FRIEND_TEST_ALL_PREFIXES(ResourceCoordinatorRenderProcessProbeBrowserTest, // cycle should be initiated. Virtual for testing.
TrackAndMeasureActiveRenderProcesses); // Default implementation sends collected metrics back to the resource
friend struct base::LazyInstanceTraitsBase< // coordinator service and initiates another render process metrics gather
ResourceCoordinatorRenderProcessProbe>; // cycle.
virtual bool HandleMetrics(
ResourceCoordinatorRenderProcessProbe(); const RenderProcessInfoMap& render_process_info_map);
~ResourceCoordinatorRenderProcessProbe();
// Delegate for handling metrics collected after each gather cycle.
std::unique_ptr<RenderProcessMetricsHandler> metrics_handler_;
// A map of currently running ProcessHandles to Process. // A map of currently running render process host IDs to Process.
RenderProcessInfoMap render_process_info_map_; RenderProcessInfoMap render_process_info_map_;
// Time duration between measurements. // Time duration between measurements.
...@@ -95,25 +89,12 @@ class ResourceCoordinatorRenderProcessProbe { ...@@ -95,25 +89,12 @@ class ResourceCoordinatorRenderProcessProbe {
// Number of measurements collected so far. // Number of measurements collected so far.
size_t current_gather_cycle_ = 0u; size_t current_gather_cycle_ = 0u;
// True while a gathering cycle is underways on a background thread.
bool is_gathering_ = false;
// Allows FieldTrial parameters to override defaults. // Allows FieldTrial parameters to override defaults.
void UpdateWithFieldTrialParams(); void UpdateWithFieldTrialParams();
// 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); DISALLOW_COPY_AND_ASSIGN(ResourceCoordinatorRenderProcessProbe);
}; };
......
...@@ -2,6 +2,7 @@ ...@@ -2,6 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
#include <map>
#include <string> #include <string>
#include "base/feature_list.h" #include "base/feature_list.h"
...@@ -22,42 +23,60 @@ ...@@ -22,42 +23,60 @@
namespace resource_coordinator { namespace resource_coordinator {
class MockResourceCoordinatorRenderProcessMetricsHandler class TestingResourceCoordinatorRenderProcessProbe
: public RenderProcessMetricsHandler { : public ResourceCoordinatorRenderProcessProbe {
public: public:
MockResourceCoordinatorRenderProcessMetricsHandler() = default; TestingResourceCoordinatorRenderProcessProbe() = default;
~MockResourceCoordinatorRenderProcessMetricsHandler() override = default; ~TestingResourceCoordinatorRenderProcessProbe() override = default;
bool HandleMetrics( bool HandleMetrics(
const RenderProcessInfoMap& render_process_info_map) override { const RenderProcessInfoMap& render_process_info_map) override {
base::RunLoop::QuitCurrentWhenIdleDeprecated(); current_run_loop_->QuitWhenIdle();
return false; return false;
} }
private: // Returns |true| if all of the elements in |*render_process_info_map_|
DISALLOW_COPY_AND_ASSIGN(MockResourceCoordinatorRenderProcessMetricsHandler); // are up-to-date with |current_gather_cycle_|.
}; bool AllMeasurementsAreAtCurrentCycle() 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;
}
class ResourceCoordinatorRenderProcessProbeBrowserTest size_t current_gather_cycle() const { return current_gather_cycle_; }
: public InProcessBrowserTest { const RenderProcessInfoMap& render_process_info_map() const {
public: return render_process_info_map_;
ResourceCoordinatorRenderProcessProbeBrowserTest() = default; }
~ResourceCoordinatorRenderProcessProbeBrowserTest() override = default;
void StartGatherCycleAndWait() { void StartGatherCycleAndWait() {
base::RunLoop run_loop; base::RunLoop run_loop;
probe_->StartGatherCycle(); current_run_loop_ = &run_loop;
StartGatherCycle();
run_loop.Run(); run_loop.Run();
}
void set_probe( current_run_loop_ = nullptr;
resource_coordinator::ResourceCoordinatorRenderProcessProbe* probe) {
probe_ = probe;
} }
private: private:
resource_coordinator::ResourceCoordinatorRenderProcessProbe* probe_; base::RunLoop* current_run_loop_ = nullptr;
DISALLOW_COPY_AND_ASSIGN(TestingResourceCoordinatorRenderProcessProbe);
};
class ResourceCoordinatorRenderProcessProbeBrowserTest
: public InProcessBrowserTest {
public:
ResourceCoordinatorRenderProcessProbeBrowserTest() = default;
~ResourceCoordinatorRenderProcessProbeBrowserTest() override = default;
private:
DISALLOW_COPY_AND_ASSIGN(ResourceCoordinatorRenderProcessProbeBrowserTest); DISALLOW_COPY_AND_ASSIGN(ResourceCoordinatorRenderProcessProbeBrowserTest);
}; };
...@@ -69,21 +88,18 @@ IN_PROC_BROWSER_TEST_F(ResourceCoordinatorRenderProcessProbeBrowserTest, ...@@ -69,21 +88,18 @@ IN_PROC_BROWSER_TEST_F(ResourceCoordinatorRenderProcessProbeBrowserTest,
features::kGRCRenderProcessCPUProfiling}, features::kGRCRenderProcessCPUProfiling},
{}); {});
resource_coordinator::ResourceCoordinatorRenderProcessProbe probe; TestingResourceCoordinatorRenderProcessProbe probe;
probe.set_render_process_metrics_handler_for_testing(
std::make_unique<MockResourceCoordinatorRenderProcessMetricsHandler>());
set_probe(&probe);
ASSERT_TRUE(embedded_test_server()->Start()); ASSERT_TRUE(embedded_test_server()->Start());
EXPECT_EQ(0u, probe.current_gather_cycle_for_testing()); EXPECT_EQ(0u, probe.current_gather_cycle());
// A tab is already open when the test begins. // A tab is already open when the test begins.
StartGatherCycleAndWait(); probe.StartGatherCycleAndWait();
EXPECT_EQ(1u, probe.current_gather_cycle_for_testing()); EXPECT_EQ(1u, probe.current_gather_cycle());
size_t initial_size = probe.render_process_info_map_for_testing().size(); size_t initial_size = probe.render_process_info_map().size();
EXPECT_LE(1u, initial_size); EXPECT_LE(1u, initial_size);
EXPECT_GE(2u, initial_size); // If a spare RenderProcessHost is present. EXPECT_GE(2u, initial_size); // If a spare RenderProcessHost is present.
EXPECT_TRUE(probe.AllRenderProcessMeasurementsAreCurrentForTesting()); EXPECT_TRUE(probe.AllMeasurementsAreAtCurrentCycle());
// Open a second tab and complete a navigation. // Open a second tab and complete a navigation.
ui_test_utils::NavigateToURLWithDisposition( ui_test_utils::NavigateToURLWithDisposition(
...@@ -91,11 +107,31 @@ IN_PROC_BROWSER_TEST_F(ResourceCoordinatorRenderProcessProbeBrowserTest, ...@@ -91,11 +107,31 @@ IN_PROC_BROWSER_TEST_F(ResourceCoordinatorRenderProcessProbeBrowserTest,
WindowOpenDisposition::NEW_FOREGROUND_TAB, WindowOpenDisposition::NEW_FOREGROUND_TAB,
ui_test_utils::BROWSER_TEST_WAIT_FOR_TAB | ui_test_utils::BROWSER_TEST_WAIT_FOR_TAB |
ui_test_utils::BROWSER_TEST_WAIT_FOR_NAVIGATION); ui_test_utils::BROWSER_TEST_WAIT_FOR_NAVIGATION);
StartGatherCycleAndWait(); probe.StartGatherCycleAndWait();
EXPECT_EQ(2u, probe.current_gather_cycle_for_testing()); EXPECT_EQ(2u, probe.current_gather_cycle());
EXPECT_EQ(initial_size + 1u, EXPECT_EQ(initial_size + 1u, probe.render_process_info_map().size());
probe.render_process_info_map_for_testing().size()); EXPECT_TRUE(probe.AllMeasurementsAreAtCurrentCycle());
EXPECT_TRUE(probe.AllRenderProcessMeasurementsAreCurrentForTesting());
// Verify that the elements in the map are reused across multiple
// measurement cycles.
std::map<int, const RenderProcessInfo*> info_map;
for (const auto& entry : probe.render_process_info_map()) {
const int key = entry.first;
const RenderProcessInfo& info = entry.second;
EXPECT_EQ(key, info.render_process_host_id);
EXPECT_TRUE(info_map.insert(std::make_pair(entry.first, &info)).second);
}
size_t info_map_size = info_map.size();
probe.StartGatherCycleAndWait();
EXPECT_EQ(info_map_size, info_map.size());
for (const auto& entry : probe.render_process_info_map()) {
const int key = entry.first;
const RenderProcessInfo& info = entry.second;
EXPECT_EQ(&info, info_map[key]);
}
// Kill one of the two open tabs. // Kill one of the two open tabs.
EXPECT_TRUE(browser() EXPECT_TRUE(browser()
...@@ -104,10 +140,10 @@ IN_PROC_BROWSER_TEST_F(ResourceCoordinatorRenderProcessProbeBrowserTest, ...@@ -104,10 +140,10 @@ IN_PROC_BROWSER_TEST_F(ResourceCoordinatorRenderProcessProbeBrowserTest,
->GetMainFrame() ->GetMainFrame()
->GetProcess() ->GetProcess()
->FastShutdownIfPossible()); ->FastShutdownIfPossible());
StartGatherCycleAndWait(); probe.StartGatherCycleAndWait();
EXPECT_EQ(3u, probe.current_gather_cycle_for_testing()); EXPECT_EQ(4u, probe.current_gather_cycle());
EXPECT_EQ(initial_size, probe.render_process_info_map_for_testing().size()); EXPECT_EQ(initial_size, probe.render_process_info_map().size());
EXPECT_TRUE(probe.AllRenderProcessMeasurementsAreCurrentForTesting()); EXPECT_TRUE(probe.AllMeasurementsAreAtCurrentCycle());
} }
} // namespace resource_coordinator } // namespace resource_coordinator
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