blob: bfe17a829eba71f5d1c0422cf03435774716e995 [file] [log] [blame]
// 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/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/process_resource_coordinator.h"
#include "services/resource_coordinator/public/cpp/resource_coordinator_features.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;
// TODO(oysteine): Move the multiplier used to avoid precision loss
// into a shared location, when this property gets used.
render_process_info.host->GetProcessResourceCoordinator()->SetCPUUsage(
render_process_info.cpu_usage);
}
return true;
}
};
ResourceCoordinatorRenderProcessProbe::ResourceCoordinatorRenderProcessProbe()
: metrics_handler_(
base::MakeUnique<ResourceCoordinatorRenderProcessMetricsHandler>()),
interval_ms_(
base::TimeDelta::FromSeconds(kDefaultMeasurementIntervalInSeconds)) {
UpdateWithFieldTrialParams();
}
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;
}
void ResourceCoordinatorRenderProcessProbe::UpdateWithFieldTrialParams() {
int64_t interval_ms = GetGRCRenderProcessCPUProfilingIntervalInMs();
if (interval_ms > 0) {
interval_ms_ = base::TimeDelta::FromMilliseconds(interval_ms);
}
}
} // namespace resource_coordinator