| Index: services/resource_coordinator/coordination_unit/tab_cpu_usage_observer.cc
|
| diff --git a/services/resource_coordinator/coordination_unit/tab_cpu_usage_observer.cc b/services/resource_coordinator/coordination_unit/tab_cpu_usage_observer.cc
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..2659b8e3c7c3f70234bc5129545202401cb63a23
|
| --- /dev/null
|
| +++ b/services/resource_coordinator/coordination_unit/tab_cpu_usage_observer.cc
|
| @@ -0,0 +1,250 @@
|
| +// 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 "services/resource_coordinator/coordination_unit/tab_cpu_usage_observer.h"
|
| +
|
| +#include <stdint.h>
|
| +
|
| +#include <ostream>
|
| +#include <utility>
|
| +
|
| +#include "base/strings/string_number_conversions.h"
|
| +#include "base/values.h"
|
| +#include "services/resource_coordinator/coordination_unit/coordination_unit_impl.h"
|
| +#include "services/resource_coordinator/coordination_unit/coordination_unit_manager.h"
|
| +#include "services/resource_coordinator/coordination_unit/process_coordination_unit_impl.h"
|
| +#include "services/resource_coordinator/public/cpp/coordination_unit_id.h"
|
| +#include "services/resource_coordinator/public/interfaces/coordination_unit.mojom.h"
|
| +
|
| +namespace resource_coordinator {
|
| +
|
| +namespace {
|
| +
|
| +const char* kCPUUsageMetricsName = "TabCPUUsage";
|
| +const char* kCPUProfilingIntervalInSecondsEntryName =
|
| + "CPUProfilingIntervalInSeconds";
|
| +const char* kMaxCPUUsageMeasurementTicksEntryName =
|
| + "MaxCPUUsageMeasurementTicks";
|
| +
|
| +} // namespace
|
| +
|
| +const size_t TabObserver::kCPUUsageMeasurementTimeInSeconds = 30u;
|
| +const size_t TabObserver::kMaxCPUUsageMeasurementTicks =
|
| + TabObserver::kCPUUsageMeasurementTimeInSeconds /
|
| + ProcessCoordinationUnitImpl::kCPUProfilingIntervalInSeconds;
|
| +
|
| +TabObserver::TabObserver() : cpu_measurement_ticks_(0u) {}
|
| +
|
| +TabObserver::~TabObserver() = default;
|
| +
|
| +void TabObserver::AttributeCPUUsage(double cpu_usage) {
|
| + DCHECK_NE(nullptr, ukm_entry_builder_.get());
|
| +
|
| + // Each |cpu_usage| measurement is assigned a metric name
|
| + // that corresponds to the |cpu_measurement_ticks_| value
|
| + // it was observed at.
|
| + ukm_entry_builder_->AddMetric(
|
| + base::Uint64ToString(cpu_measurement_ticks_++).c_str(),
|
| + static_cast<int>(cpu_usage));
|
| +}
|
| +
|
| +bool TabObserver::IsObserving() {
|
| + return source_id_ && ukm_entry_builder_.get() != nullptr &&
|
| + cpu_measurement_ticks_ < kMaxCPUUsageMeasurementTicks;
|
| +}
|
| +
|
| +void TabObserver::RestartObserving() {
|
| + cpu_measurement_ticks_ = 0u;
|
| +
|
| + ukm_entry_builder_ =
|
| + tab_cpu_usage_observer_->coordination_unit_manager()->GetUkmEntryBuilder(
|
| + source_id_, kCPUUsageMetricsName);
|
| + ukm_entry_builder_->AddMetric(
|
| + kCPUProfilingIntervalInSecondsEntryName,
|
| + ProcessCoordinationUnitImpl::kCPUProfilingIntervalInSeconds);
|
| + ukm_entry_builder_->AddMetric(kMaxCPUUsageMeasurementTicksEntryName,
|
| + kMaxCPUUsageMeasurementTicks);
|
| +}
|
| +
|
| +void TabObserver::UkmSourceIdChanged(ukm::SourceId source_id) {
|
| + source_id_ = source_id;
|
| +
|
| + // Receiving a new ukm::SourceId implies the tab has navigated to
|
| + // a new page thus initiating a new observation period for the tab.
|
| + RestartObserving();
|
| +}
|
| +
|
| +ProcessObserver::ProcessObserver() = default;
|
| +
|
| +ProcessObserver::~ProcessObserver() = default;
|
| +
|
| +void ProcessObserver::RecalculateAttributableTabs(
|
| + const CoordinationUnitImpl* process_coordination_unit,
|
| + const CoordinationUnitImpl* frame_coordination_unit) {
|
| + attributable_tabs_.clear();
|
| +
|
| + DCHECK(tab_cpu_usage_observer_);
|
| + std::unordered_map<CoordinationUnitID, TabObserver>&
|
| + registered_tab_observers = tab_cpu_usage_observer_->tab_observers();
|
| + // Any WebContents CoordinationUnit that is a parent of any of the
|
| + // Process's children that are FrameCoordinationUnits is considered to
|
| + // to be attributable for the Process's measured CPU usage.
|
| + for (auto* child : process_coordination_unit->children()) {
|
| + if (child->id().type == CoordinationUnitType::kFrame) {
|
| + for (auto* frame_parent : child->parents()) {
|
| + if (frame_parent->id().type == CoordinationUnitType::kWebContents) {
|
| + auto tab_observer_it =
|
| + registered_tab_observers.find(frame_parent->id());
|
| + DCHECK(tab_observer_it != registered_tab_observers.end());
|
| +
|
| + // It is possible that another frame within the tab has inserted the
|
| + // tab CoordinationUnit so checking insertion success is unecessary.
|
| + attributable_tabs_.insert(
|
| + std::make_pair(frame_parent->id(), &tab_observer_it->second));
|
| + }
|
| + }
|
| + }
|
| + }
|
| +}
|
| +
|
| +void ProcessObserver::CPUUsageUpdated(double cpu_usage) {
|
| + if (attributable_tabs_.empty()) {
|
| + return;
|
| + }
|
| +
|
| + // CPU attribution for multiple tabs within a process is currently naive in
|
| + // that the measured process CPU utilization is attributed equally.
|
| + double tab_cpu_usage =
|
| + cpu_usage / static_cast<double>(attributable_tabs_.size());
|
| +
|
| + for (auto& tab_observer : attributable_tabs_) {
|
| + if (tab_observer.second->IsObserving()) {
|
| + tab_observer.second->AttributeCPUUsage(tab_cpu_usage);
|
| + }
|
| + }
|
| +}
|
| +
|
| +TabCPUUsageObserver::TabCPUUsageObserver() = default;
|
| +
|
| +TabCPUUsageObserver::~TabCPUUsageObserver() = default;
|
| +
|
| +bool TabCPUUsageObserver::ShouldObserve(
|
| + const CoordinationUnitImpl* coordination_unit) {
|
| + // This observer is only interested in tracking render processes and tabs.
|
| + CoordinationUnitType cu_type = coordination_unit->id().type;
|
| + return cu_type == CoordinationUnitType::kProcess ||
|
| + cu_type == CoordinationUnitType::kWebContents;
|
| +}
|
| +
|
| +void TabCPUUsageObserver::TabCoordinationUnitCreated(
|
| + const CoordinationUnitImpl* tab_coordination_unit) {
|
| + const CoordinationUnitID& tab_cu_id = tab_coordination_unit->id();
|
| + DCHECK_EQ(0u, tab_observers_.count(tab_cu_id));
|
| + auto& tab_observer = tab_observers_[tab_cu_id];
|
| + tab_observer.SetTabCPUUsageObserver(this);
|
| +}
|
| +
|
| +void TabCPUUsageObserver::ProcessCoordinationUnitCreated(
|
| + const CoordinationUnitImpl* process_coordination_unit) {
|
| + const CoordinationUnitID& process_cu_id = process_coordination_unit->id();
|
| + DCHECK_EQ(0u, process_observers_.count(process_cu_id));
|
| + ProcessObserver& process_observer = process_observers_[process_cu_id];
|
| + process_observer.SetTabCPUUsageObserver(this);
|
| +}
|
| +
|
| +void TabCPUUsageObserver::OnCoordinationUnitCreated(
|
| + const CoordinationUnitImpl* coordination_unit) {
|
| + if (coordination_unit->id().type == CoordinationUnitType::kWebContents) {
|
| + TabCoordinationUnitCreated(coordination_unit);
|
| + } else if (coordination_unit->id().type == CoordinationUnitType::kProcess) {
|
| + ProcessCoordinationUnitCreated(coordination_unit);
|
| + }
|
| +}
|
| +
|
| +void TabCPUUsageObserver::TabUkmSourceIdChanged(
|
| + const CoordinationUnitImpl* tab_coordination_unit) {
|
| + const CoordinationUnitID& tab_cu_id = tab_coordination_unit->id();
|
| + DCHECK_EQ(1u, tab_observers_.count(tab_cu_id));
|
| + TabObserver& tab_observer = tab_observers_[tab_cu_id];
|
| + ukm::SourceId source_id;
|
| + base::StringToInt64(
|
| + tab_coordination_unit->GetProperty(mojom::PropertyType::kTabUkmSourceId)
|
| + .GetString(),
|
| + &source_id);
|
| + tab_observer.UkmSourceIdChanged(source_id);
|
| +}
|
| +
|
| +void TabCPUUsageObserver::ProcessCPUUsageUpdated(
|
| + const CoordinationUnitImpl* process_coordination_unit) {
|
| + const CoordinationUnitID& process_cu_id = process_coordination_unit->id();
|
| + DCHECK_EQ(1u, process_observers_.count(process_cu_id));
|
| + ProcessObserver& process_observer = process_observers_[process_cu_id];
|
| + process_observer.CPUUsageUpdated(
|
| + process_coordination_unit
|
| + ->GetProperty(mojom::PropertyType::kProcessCPUUsage)
|
| + .GetDouble());
|
| +}
|
| +
|
| +void TabCPUUsageObserver::OnPropertyChanged(
|
| + const CoordinationUnitImpl* coordination_unit,
|
| + mojom::PropertyType property) {
|
| + const CoordinationUnitID& cu_id = coordination_unit->id();
|
| + if (cu_id.type == CoordinationUnitType::kWebContents &&
|
| + property == mojom::PropertyType::kTabUkmSourceId) {
|
| + TabUkmSourceIdChanged(coordination_unit);
|
| + } else if (cu_id.type == CoordinationUnitType::kProcess &&
|
| + property == mojom::PropertyType::kProcessCPUUsage) {
|
| + ProcessCPUUsageUpdated(coordination_unit);
|
| + }
|
| +}
|
| +
|
| +void TabCPUUsageObserver::MaybeRecalculateAttributableTabsForProcess(
|
| + const CoordinationUnitImpl* process_coordination_unit,
|
| + const CoordinationUnitImpl* tab_coordination_unit) {
|
| + const CoordinationUnitID& process_cu_id = process_coordination_unit->id();
|
| + if (process_cu_id.type == CoordinationUnitType::kProcess &&
|
| + tab_coordination_unit->id().type == CoordinationUnitType::kFrame) {
|
| + DCHECK_EQ(1u, process_observers_.count(process_cu_id));
|
| + ProcessObserver& process_observer = process_observers_[process_cu_id];
|
| + process_observer.RecalculateAttributableTabs(process_coordination_unit,
|
| + tab_coordination_unit);
|
| + }
|
| +}
|
| +
|
| +void TabCPUUsageObserver::OnChildAdded(
|
| + const CoordinationUnitImpl* coordination_unit,
|
| + const CoordinationUnitImpl* child_coordination_unit) {
|
| + MaybeRecalculateAttributableTabsForProcess(coordination_unit,
|
| + child_coordination_unit);
|
| +}
|
| +
|
| +void TabCPUUsageObserver::OnChildRemoved(
|
| + const CoordinationUnitImpl* coordination_unit,
|
| + const CoordinationUnitImpl* child_coordination_unit) {
|
| + MaybeRecalculateAttributableTabsForProcess(coordination_unit,
|
| + child_coordination_unit);
|
| +}
|
| +
|
| +void TabCPUUsageObserver::RemoveProcessObserver(
|
| + const CoordinationUnitImpl* process_coordination_unit) {
|
| + DCHECK_EQ(1u, process_observers_.count(process_coordination_unit->id()));
|
| + process_observers_.erase(process_coordination_unit->id());
|
| +}
|
| +
|
| +void TabCPUUsageObserver::RemoveTabObserver(
|
| + const CoordinationUnitImpl* tab_coordination_unit) {
|
| + DCHECK_EQ(1u, tab_observers_.count(tab_coordination_unit->id()));
|
| + tab_observers_.erase(tab_coordination_unit->id());
|
| +}
|
| +
|
| +void TabCPUUsageObserver::OnCoordinationUnitWillBeDestroyed(
|
| + const CoordinationUnitImpl* coordination_unit) {
|
| + if (coordination_unit->id().type == CoordinationUnitType::kWebContents) {
|
| + RemoveTabObserver(coordination_unit);
|
| + } else if (coordination_unit->id().type == CoordinationUnitType::kProcess) {
|
| + RemoveProcessObserver(coordination_unit);
|
| + }
|
| +}
|
| +
|
| +} // namespace resource_coordinator
|
|
|