blob: 76dd47f48fab7f386589f7e3f89d2c27714e19c3 [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/graph_node_provider_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"
#include "services/service_manager/public/cpp/bind_source_info.h"
#include "services/service_manager/public/cpp/binder_registry.h"
namespace ukm {
class UkmEntryBuilder;
} // namespace ukm
namespace performance_manager {
Graph::Graph()
: system_coordination_unit_id_(
resource_coordinator::CoordinationUnitType::kSystem,
resource_coordinator::CoordinationUnitID::RANDOM_ID) {}
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::OnStart(service_manager::BinderRegistryWithArgs<
const service_manager::BindSourceInfo&>* registry,
service_manager::ServiceKeepalive* keepalive) {
// Create the singleton CoordinationUnitProvider.
provider_ = std::make_unique<GraphNodeProviderImpl>(keepalive, this);
registry->AddInterface(base::BindRepeating(
&GraphNodeProviderImpl::Bind, base::Unretained(provider_.get())));
}
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();
}
FrameNodeImpl* Graph::CreateFrameNode(
const resource_coordinator::CoordinationUnitID& id,
std::unique_ptr<service_manager::ServiceKeepaliveRef> service_ref) {
return FrameNodeImpl::Create(id, this, std::move(service_ref));
}
PageNodeImpl* Graph::CreatePageNode(
const resource_coordinator::CoordinationUnitID& id,
std::unique_ptr<service_manager::ServiceKeepaliveRef> service_ref) {
return PageNodeImpl::Create(id, this, std::move(service_ref));
}
ProcessNodeImpl* Graph::CreateProcessNode(
const resource_coordinator::CoordinationUnitID& id,
std::unique_ptr<service_manager::ServiceKeepaliveRef> service_ref) {
return ProcessNodeImpl::Create(id, this, std::move(service_ref));
}
SystemNodeImpl* Graph::FindOrCreateSystemNode(
std::unique_ptr<service_manager::ServiceKeepaliveRef> service_ref) {
NodeBase* system_cu = GetNodeByID(system_coordination_unit_id_);
if (system_cu)
return SystemNodeImpl::FromNodeBase(system_cu);
// Create the singleton SystemCU instance. Ownership is taken by the graph.
return SystemNodeImpl::Create(system_coordination_unit_id_, this,
std::move(service_ref));
}
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.get();
}
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>();
}
NodeBase* Graph::AddNewNode(std::unique_ptr<NodeBase> new_cu) {
auto it = coordination_units_.emplace(new_cu->id(), std::move(new_cu));
DCHECK(it.second); // Inserted successfully
NodeBase* added_cu = it.first->second.get();
OnNodeCreated(added_cu);
return added_cu;
}
void Graph::DestroyNode(NodeBase* cu) {
OnBeforeNodeDestroyed(cu);
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.get()));
}
return ret;
}
} // namespace performance_manager