Chromium Code Reviews| 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 |