blob: cbc90adcf4c4f7c3a4d3333cb53958d82b191208 [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/performance_manager/graph/graph.h"
#include <utility>
#include "base/bind.h"
#include "base/bind_helpers.h"
#include "base/macros.h"
#include "chrome/browser/performance_manager/graph/frame_node_impl.h"
#include "chrome/browser/performance_manager/graph/node_base.h"
#include "chrome/browser/performance_manager/graph/page_node_impl.h"
#include "chrome/browser/performance_manager/graph/process_node_impl.h"
#include "chrome/browser/performance_manager/graph/system_node_impl.h"
#include "chrome/browser/performance_manager/observers/coordination_unit_graph_observer.h"
#include "services/resource_coordinator/public/cpp/coordination_unit_types.h"
namespace ukm {
class UkmEntryBuilder;
} // namespace ukm
namespace performance_manager {
Graph::Graph() = default;
Graph::~Graph() {
// Because the graph has ownership of the CUs, and because the process CUs
// unregister on destruction, there is reentrancy to this class on
// destruction. The order of operations here is optimized to minimize the work
// done on destruction, as well as to make sure the cleanup is independent of
// the declaration order of member variables.
// Kill all the observers first.
observers_.clear();
// Then clear up the CUs to ensure this happens before the PID map is
// destructed.
coordination_units_.clear();
DCHECK_EQ(0u, processes_by_pid_.size());
}
void Graph::RegisterObserver(std::unique_ptr<GraphObserver> observer) {
observer->set_coordination_unit_graph(this);
observers_.push_back(std::move(observer));
}
void Graph::OnNodeCreated(NodeBase* coordination_unit) {
for (auto& observer : observers_) {
if (observer->ShouldObserve(coordination_unit)) {
coordination_unit->AddObserver(observer.get());
observer->OnNodeCreated(coordination_unit);
}
}
}
void Graph::OnBeforeNodeDestroyed(NodeBase* coordination_unit) {
coordination_unit->BeforeDestroyed();
}
SystemNodeImpl* Graph::FindOrCreateSystemNode() {
if (!system_node_) {
// Create the singleton SystemCU instance. Ownership is taken by the graph.
resource_coordinator::CoordinationUnitID id(
resource_coordinator::CoordinationUnitType::kSystem,
resource_coordinator::CoordinationUnitID::RANDOM_ID);
system_node_ = std::make_unique<SystemNodeImpl>(id, this);
AddNewNode(system_node_.get());
}
return system_node_.get();
}
NodeBase* Graph::GetNodeByID(
const resource_coordinator::CoordinationUnitID cu_id) {
const auto& it = coordination_units_.find(cu_id);
if (it == coordination_units_.end())
return nullptr;
return it->second;
}
ProcessNodeImpl* Graph::GetProcessNodeByPid(base::ProcessId pid) {
auto it = processes_by_pid_.find(pid);
if (it == processes_by_pid_.end())
return nullptr;
return ProcessNodeImpl::FromNodeBase(it->second);
}
std::vector<ProcessNodeImpl*> Graph::GetAllProcessNodes() {
return GetAllNodesOfType<ProcessNodeImpl>();
}
std::vector<FrameNodeImpl*> Graph::GetAllFrameNodes() {
return GetAllNodesOfType<FrameNodeImpl>();
}
std::vector<PageNodeImpl*> Graph::GetAllPageNodes() {
return GetAllNodesOfType<PageNodeImpl>();
}
size_t Graph::GetNodeAttachedDataCountForTesting(NodeBase* node,
const void* key) const {
if (!node && !key)
return node_attached_data_map_.size();
size_t count = 0;
for (auto& node_data : node_attached_data_map_) {
if (node && node_data.first.first != node)
continue;
if (key && node_data.first.second != key)
continue;
++count;
}
return count;
}
void Graph::AddNewNode(NodeBase* new_cu) {
auto it = coordination_units_.emplace(new_cu->id(), new_cu);
DCHECK(it.second); // Inserted successfully
NodeBase* added_cu = it.first->second;
OnNodeCreated(added_cu);
}
void Graph::DestroyNode(NodeBase* cu) {
OnBeforeNodeDestroyed(cu);
// Remove any node attached data affiliated with this node.
auto lower = node_attached_data_map_.lower_bound(std::make_pair(cu, nullptr));
auto upper =
node_attached_data_map_.lower_bound(std::make_pair(cu + 1, nullptr));
node_attached_data_map_.erase(lower, upper);
// Before removing the node itself.
size_t erased = coordination_units_.erase(cu->id());
DCHECK_EQ(1u, erased);
}
void Graph::BeforeProcessPidChange(ProcessNodeImpl* process,
base::ProcessId new_pid) {
// On Windows, PIDs are aggressively reused, and because not all process
// creation/death notifications are synchronized, it's possible for more than
// one CU to have the same PID. To handle this, the second and subsequent
// registration override earlier registrations, while unregistration will only
// unregister the current holder of the PID.
if (process->process_id() != base::kNullProcessId) {
auto it = processes_by_pid_.find(process->process_id());
if (it != processes_by_pid_.end() && it->second == process)
processes_by_pid_.erase(it);
}
if (new_pid != base::kNullProcessId)
processes_by_pid_[new_pid] = process;
}
template <typename CUType>
std::vector<CUType*> Graph::GetAllNodesOfType() {
const auto type = CUType::Type();
std::vector<CUType*> ret;
for (const auto& el : coordination_units_) {
if (el.first.type == type)
ret.push_back(CUType::FromNodeBase(el.second));
}
return ret;
}
} // namespace performance_manager