blob: b181b5fbb9d88b0b1dfb325ad7bb0466fc6992e4 [file] [log] [blame]
// Copyright 2018 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/performance_manager.h"
#include <memory>
#include <utility>
#include "base/containers/flat_set.h"
#include "base/feature_list.h"
#include "base/logging.h"
#include "base/macros.h"
#include "base/memory/ptr_util.h"
#include "base/task/post_task.h"
#include "base/task/task_traits.h"
#include "build/build_config.h"
#include "chrome/browser/performance_manager/decorators/frozen_frame_aggregator.h"
#include "chrome/browser/performance_manager/decorators/page_almost_idle_decorator.h"
#include "chrome/browser/performance_manager/graph/frame_node_impl.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/isolation_context_metrics.h"
#include "chrome/browser/performance_manager/observers/metrics_collector.h"
#include "chrome/browser/performance_manager/observers/working_set_trimmer_observer_win.h"
#include "content/public/browser/system_connector.h"
#include "services/resource_coordinator/public/cpp/resource_coordinator_features.h"
namespace performance_manager {
namespace {
PerformanceManager* g_performance_manager = nullptr;
scoped_refptr<base::SequencedTaskRunner> CreateTaskRunner() {
return base::CreateSequencedTaskRunnerWithTraits(
{base::TaskPriority::USER_VISIBLE,
base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN, base::MayBlock()});
}
} // namespace
PerformanceManager* PerformanceManager::GetInstance() {
return g_performance_manager;
}
PerformanceManager::PerformanceManager() : task_runner_(CreateTaskRunner()) {
DETACH_FROM_SEQUENCE(sequence_checker_);
}
PerformanceManager::~PerformanceManager() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
for (auto& observer : observers_)
graph_.UnregisterObserver(observer.get());
}
// static
std::unique_ptr<PerformanceManager> PerformanceManager::Create() {
DCHECK_EQ(nullptr, g_performance_manager);
std::unique_ptr<PerformanceManager> instance =
base::WrapUnique(new PerformanceManager());
instance->OnStart();
g_performance_manager = instance.get();
return instance;
}
// static
void PerformanceManager::Destroy(std::unique_ptr<PerformanceManager> instance) {
DCHECK_EQ(instance.get(), g_performance_manager);
g_performance_manager = nullptr;
instance->task_runner_->DeleteSoon(FROM_HERE, instance.release());
}
void PerformanceManager::CallOnGraph(const base::Location& from_here,
GraphCallback callback) {
DCHECK(!callback.is_null());
task_runner_->PostTask(
FROM_HERE, base::BindOnce(&PerformanceManager::CallOnGraphImpl,
base::Unretained(this), std::move(callback)));
}
std::unique_ptr<FrameNodeImpl> PerformanceManager::CreateFrameNode(
ProcessNodeImpl* process_node,
PageNodeImpl* page_node,
FrameNodeImpl* parent_frame_node,
int frame_tree_node_id,
const base::UnguessableToken& dev_tools_token,
int32_t browsing_instance_id,
int32_t site_instance_id) {
return CreateNodeImpl<FrameNodeImpl>(
FrameNodeCreationCallback(), process_node, page_node, parent_frame_node,
frame_tree_node_id, dev_tools_token, browsing_instance_id,
site_instance_id);
}
std::unique_ptr<FrameNodeImpl> PerformanceManager::CreateFrameNode(
ProcessNodeImpl* process_node,
PageNodeImpl* page_node,
FrameNodeImpl* parent_frame_node,
int frame_tree_node_id,
const base::UnguessableToken& dev_tools_token,
int32_t browsing_instance_id,
int32_t site_instance_id,
FrameNodeCreationCallback creation_callback) {
return CreateNodeImpl<FrameNodeImpl>(
std::move(creation_callback), process_node, page_node, parent_frame_node,
frame_tree_node_id, dev_tools_token, browsing_instance_id,
site_instance_id);
}
std::unique_ptr<PageNodeImpl> PerformanceManager::CreatePageNode(
const WebContentsProxy& contents_proxy,
bool is_visible,
bool is_audible) {
return CreateNodeImpl<PageNodeImpl>(base::OnceCallback<void(PageNodeImpl*)>(),
contents_proxy, is_visible, is_audible);
}
std::unique_ptr<ProcessNodeImpl> PerformanceManager::CreateProcessNode() {
return CreateNodeImpl<ProcessNodeImpl>(
base::OnceCallback<void(ProcessNodeImpl*)>());
}
void PerformanceManager::BatchDeleteNodes(
std::vector<std::unique_ptr<NodeBase>> nodes) {
task_runner_->PostTask(
FROM_HERE, base::BindOnce(&PerformanceManager::BatchDeleteNodesImpl,
base::Unretained(this), std::move(nodes)));
}
void PerformanceManager::RegisterObserver(
std::unique_ptr<GraphImplObserver> observer) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
graph_.RegisterObserver(observer.get());
observers_.push_back(std::move(observer));
}
void PerformanceManager::PostBindInterface(
const std::string& interface_name,
mojo::ScopedMessagePipeHandle message_pipe) {
task_runner_->PostTask(FROM_HERE,
base::BindOnce(&PerformanceManager::BindInterfaceImpl,
base::Unretained(this), interface_name,
std::move(message_pipe)));
}
namespace {
// Helper function for adding a node to a graph, and invoking a post-creation
// callback immediately afterwards.
template <typename NodeType>
void AddNodeAndInvokeCreationCallback(
base::OnceCallback<void(NodeType*)> callback,
NodeType* node,
GraphImpl* graph) {
graph->AddNewNode(node);
if (!callback.is_null())
std::move(callback).Run(node);
}
} // namespace
template <typename NodeType, typename... Args>
std::unique_ptr<NodeType> PerformanceManager::CreateNodeImpl(
base::OnceCallback<void(NodeType*)> creation_callback,
Args&&... constructor_args) {
std::unique_ptr<NodeType> new_node = std::make_unique<NodeType>(
&graph_, std::forward<Args>(constructor_args)...);
task_runner_->PostTask(
FROM_HERE, base::BindOnce(&AddNodeAndInvokeCreationCallback<NodeType>,
std::move(creation_callback),
base::Unretained(new_node.get()),
base::Unretained(&graph_)));
return new_node;
}
void PerformanceManager::PostDeleteNode(std::unique_ptr<NodeBase> node) {
task_runner_->PostTask(
FROM_HERE, base::BindOnce(&PerformanceManager::DeleteNodeImpl,
base::Unretained(this), std::move(node)));
}
void PerformanceManager::DeleteNodeImpl(std::unique_ptr<NodeBase> node) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
graph_.RemoveNode(node.get());
}
namespace {
void RemoveFrameAndChildrenFromGraph(FrameNodeImpl* frame_node) {
// Recurse on the first child while there is one.
while (!frame_node->child_frame_nodes().empty())
RemoveFrameAndChildrenFromGraph(*(frame_node->child_frame_nodes().begin()));
// Now that all children are deleted, delete this frame.
frame_node->graph()->RemoveNode(frame_node);
}
} // namespace
void PerformanceManager::BatchDeleteNodesImpl(
std::vector<std::unique_ptr<NodeBase>> nodes) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
base::flat_set<ProcessNodeImpl*> process_nodes;
for (auto it = nodes.begin(); it != nodes.end(); ++it) {
switch ((*it)->type()) {
case PageNodeImpl::Type(): {
auto* page_node = PageNodeImpl::FromNodeBase(it->get());
// Delete the main frame nodes until no more exist.
while (!page_node->main_frame_nodes().empty())
RemoveFrameAndChildrenFromGraph(
*(page_node->main_frame_nodes().begin()));
graph_.RemoveNode(page_node);
break;
}
case ProcessNodeImpl::Type(): {
// Keep track of the process nodes for removing once all frames nodes
// are removed.
auto* process_node = ProcessNodeImpl::FromNodeBase(it->get());
process_nodes.insert(process_node);
break;
}
case FrameNodeImpl::Type():
break;
case SystemNodeImpl::Type():
case NodeTypeEnum::kInvalidType:
default: {
NOTREACHED();
break;
}
}
}
// Remove the process nodes from the graph.
for (auto* process_node : process_nodes)
graph_.RemoveNode(process_node);
// When |nodes| goes out of scope, all nodes are deleted.
}
void PerformanceManager::OnStart() {
// Some tests don't initialize the service manager connection, so this class
// tolerates its absence for tests.
auto* connector = content::GetSystemConnector();
task_runner_->PostTask(
FROM_HERE,
base::BindOnce(&PerformanceManager::OnStartImpl, base::Unretained(this),
connector ? connector->Clone() : nullptr));
}
void PerformanceManager::CallOnGraphImpl(GraphCallback graph_callback) {
std::move(graph_callback).Run(&graph_);
}
void PerformanceManager::OnStartImpl(
std::unique_ptr<service_manager::Connector> connector) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
graph_.PassToGraph(std::make_unique<FrozenFrameAggregator>());
graph_.PassToGraph(std::make_unique<PageAlmostIdleDecorator>());
graph_.PassToGraph(std::make_unique<IsolationContextMetrics>());
graph_.PassToGraph(std::make_unique<MetricsCollector>());
#if defined(OS_WIN)
if (base::FeatureList::IsEnabled(features::kEmptyWorkingSet))
graph_.PassToGraph(std::make_unique<WorkingSetTrimmer>());
#endif
interface_registry_.AddInterface(base::BindRepeating(
&PerformanceManager::BindWebUIGraphDump, base::Unretained(this)));
if (connector) {
ukm_recorder_ = ukm::MojoUkmRecorder::Create(connector.get());
graph_.set_ukm_recorder(ukm_recorder_.get());
}
}
void PerformanceManager::BindInterfaceImpl(
const std::string& interface_name,
mojo::ScopedMessagePipeHandle message_pipe) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
interface_registry_.BindInterface(interface_name, std::move(message_pipe),
service_manager::BindSourceInfo());
}
void PerformanceManager::BindWebUIGraphDump(
mojom::WebUIGraphDumpRequest request,
const service_manager::BindSourceInfo& source_info) {
std::unique_ptr<WebUIGraphDumpImpl> graph_dump =
std::make_unique<WebUIGraphDumpImpl>(&graph_);
auto error_callback =
base::BindOnce(&PerformanceManager::OnGraphDumpConnectionError,
base::Unretained(this), graph_dump.get());
graph_dump->Bind(std::move(request), std::move(error_callback));
graph_dumps_.push_back(std::move(graph_dump));
}
void PerformanceManager::OnGraphDumpConnectionError(
WebUIGraphDumpImpl* graph_dump) {
const auto it = std::find_if(
graph_dumps_.begin(), graph_dumps_.end(),
[graph_dump](const std::unique_ptr<WebUIGraphDumpImpl>& graph_dump_ptr) {
return graph_dump_ptr.get() == graph_dump;
});
DCHECK(it != graph_dumps_.end());
graph_dumps_.erase(it);
}
} // namespace performance_manager