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..6310507fb4b38a692b800d214acae184873b2d74 |
--- /dev/null |
+++ b/services/resource_coordinator/coordination_unit/tab_cpu_usage_observer.cc |
@@ -0,0 +1,192 @@ |
+// 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/values.h" |
+#include "services/resource_coordinator/coordination_unit/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 { |
+ |
+const unsigned int TabObserver::kMaxCPUUsageMeasurementTicks = 3; |
+ |
+TabObserver::TabObserver() : cpu_measurement_ticks_(0u) {} |
+ |
+TabObserver::~TabObserver() = default; |
+ |
+void TabObserver::AttributeCPUUsage(double cpu_usage) { |
+ ++cpu_measurement_ticks_; |
+} |
+ |
+bool TabObserver::IsObserving() { |
+ return !url_.empty() && cpu_measurement_ticks_ < kMaxCPUUsageMeasurementTicks; |
+} |
+ |
+void TabObserver::TabURLChanged( |
+ const CoordinationUnitImpl* tab_coordination_unit, |
+ mojom::PropertyType tab_url_property) { |
+ DCHECK(tab_coordination_unit->id().type == |
+ CoordinationUnitType::kWebContents); |
+ DCHECK(tab_url_property == mojom::PropertyType::kTabURL); |
+ |
+ base::Value tab_url_value = |
+ tab_coordination_unit->GetProperty(tab_url_property); |
+ DCHECK(tab_url_value.is_string()); |
+ url_ = tab_url_value.GetString(); |
+ |
+ // Reset ticks |
+ cpu_measurement_ticks_ = 0u; |
+} |
+ |
+ProcessObserver::ProcessObserver() = default; |
+ |
+ProcessObserver::~ProcessObserver() = default; |
+ |
+void ProcessObserver::RecalculateAttributableTabs( |
+ const CoordinationUnitImpl* process_coordination_unit, |
+ const CoordinationUnitImpl* frame_coordination_unit) { |
+ DCHECK(process_coordination_unit->id().type == |
+ CoordinationUnitType::kProcess); |
+ DCHECK(frame_coordination_unit->id().type == CoordinationUnitType::kFrame); |
+ |
+ 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 child Frame CoordinationUnit is considered to attributable |
+ // for the Process's 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) { |
+ // Another frame within a tab may have previously inserted its, so |
+ // its acceptable to not check if the insertion was successful. |
+ auto tab_observer_it = |
+ registered_tab_observers.find(frame_parent->id()); |
+ DCHECK(tab_observer_it != registered_tab_observers.end()); |
+ attributable_tabs_.insert( |
+ std::make_pair(frame_parent->id(), &tab_observer_it->second)); |
+ } |
+ } |
+ } |
+ } |
+} |
+ |
+void ProcessObserver::NewCPUUsageMeasurement( |
+ const CoordinationUnitImpl* process_coordination_unit, |
+ mojom::PropertyType process_cpu_usage_property) { |
+ DCHECK(process_coordination_unit->id().type == |
+ CoordinationUnitType::kProcess); |
+ DCHECK(process_cpu_usage_property == mojom::PropertyType::kProcessCPUUsage); |
+ |
+ if (attributable_tabs_.empty()) { |
+ return; |
+ } |
+ |
+ base::Value process_cpu_usage_value = |
+ process_coordination_unit->GetProperty(process_cpu_usage_property); |
+ DCHECK(process_cpu_usage_value.is_double()); |
+ |
+ // CPU attribution for multiple tabs within a process is currently naive in |
+ // that the measured process CPU utilization is attributed equally amongst the |
+ // tabs. |
+ double tab_cpu_usage = process_cpu_usage_value.GetDouble() / |
+ 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; |
+ |
+void TabCPUUsageObserver::TabCoordinationUnitCreated( |
+ CoordinationUnitImpl* tab_coordination_unit) { |
+ DCHECK(!tab_observers_.count(tab_coordination_unit->id())); |
+ |
+ auto& tab_observer = tab_observers_[tab_coordination_unit->id()]; |
+ tab_observer.SetTabCPUUsageObserver(this); |
+ |
+ tab_coordination_unit->on_property_changed_event_listeners().AddListener( |
+ base::Bind(&TabObserver::TabURLChanged, base::Unretained(&tab_observer)), |
+ mojom::PropertyType::kTabURL); |
+ |
+ tab_coordination_unit->on_will_be_destroyed_event_listeners().AddListener( |
+ base::Bind(&TabCPUUsageObserver::RemoveTabObserver, |
+ base::Unretained(this))); |
+} |
+ |
+void TabCPUUsageObserver::ProcessCoordinationUnitCreated( |
+ CoordinationUnitImpl* process_coordination_unit) { |
+ DCHECK(!process_observers_.count(process_coordination_unit->id())); |
+ ProcessObserver& process_observer = |
+ process_observers_[process_coordination_unit->id()]; |
+ process_observer.SetTabCPUUsageObserver(this); |
+ |
+ process_coordination_unit->on_add_child_event_listeners().AddListener( |
+ base::Bind(&ProcessObserver::RecalculateAttributableTabs, |
+ base::Unretained(&process_observer)), |
+ CoordinationUnitType::kFrame); |
+ |
+ process_coordination_unit->on_remove_child_event_listeners().AddListener( |
+ base::Bind(&ProcessObserver::RecalculateAttributableTabs, |
+ base::Unretained(&process_observer)), |
+ CoordinationUnitType::kFrame); |
+ |
+ process_coordination_unit->on_property_changed_event_listeners().AddListener( |
+ base::Bind(&ProcessObserver::NewCPUUsageMeasurement, |
+ base::Unretained(&process_observer)), |
+ mojom::PropertyType::kProcessCPUUsage); |
Zhen Wang
2017/06/16 22:47:45
I see your point here that one may want to listen
|
+ |
+ process_coordination_unit->on_will_be_destroyed_event_listeners().AddListener( |
+ base::Bind(&TabCPUUsageObserver::RemoveProcessObserver, |
+ base::Unretained(this))); |
+} |
+ |
+void TabCPUUsageObserver::CoordinationUnitCreated( |
+ CoordinationUnitImpl* coordination_unit) { |
+ switch (coordination_unit->id().type) { |
+ case CoordinationUnitType::kWebContents: |
+ TabCoordinationUnitCreated(coordination_unit); |
+ break; |
+ case CoordinationUnitType::kProcess: |
+ ProcessCoordinationUnitCreated(coordination_unit); |
+ break; |
+ case CoordinationUnitType::kFrame: |
+ break; |
+ default: |
+ break; |
+ } |
+ |
+ if (coordination_unit->id().type != CoordinationUnitType::kProcess) { |
+ return; |
+ } |
+} |
+ |
+void TabCPUUsageObserver::RemoveProcessObserver( |
+ const CoordinationUnitImpl* process_coordination_unit) { |
+ DCHECK(process_observers_.count(process_coordination_unit->id())); |
+ process_observers_.erase(process_coordination_unit->id()); |
+} |
+ |
+void TabCPUUsageObserver::RemoveTabObserver( |
+ const CoordinationUnitImpl* tab_coordination_unit) { |
+ DCHECK(tab_observers_.count(tab_coordination_unit->id())); |
+ tab_observers_.erase(tab_coordination_unit->id()); |
+} |
+ |
+} // namespace resource_coordinator |