Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(726)

Unified Diff: services/resource_coordinator/coordination_unit/tab_cpu_usage_observer.cc

Issue 2946683002: [GRC] Tab CPU Usage Observer (Closed)
Patch Set: Add unittest Created 3 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
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

Powered by Google App Engine
This is Rietveld 408576698