blob: 6ad3a017cf78b1d307ba5e2c8208d4328f33b72f [file] [log] [blame]
// Copyright 2018 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "components/performance_manager/performance_manager_impl.h"
#include <atomic>
#include <memory>
#include <utility>
#include "base/check_op.h"
#include "base/containers/flat_set.h"
#include "base/feature_list.h"
#include "base/functional/bind.h"
#include "base/functional/callback.h"
#include "base/location.h"
#include "base/memory/ptr_util.h"
#include "base/memory/scoped_refptr.h"
#include "base/no_destructor.h"
#include "base/notreached.h"
#include "base/task/delayed_task_handle.h"
#include "base/task/lazy_thread_pool_task_runner.h"
#include "base/task/sequenced_task_runner.h"
#include "base/task/single_thread_task_runner_thread_mode.h"
#include "base/task/task_traits.h"
#include "base/time/time.h"
#include "components/performance_manager/graph/frame_node_impl.h"
#include "components/performance_manager/graph/page_node_impl.h"
#include "components/performance_manager/graph/process_node_impl.h"
#include "components/performance_manager/graph/system_node_impl.h"
#include "components/performance_manager/graph/worker_node_impl.h"
#include "components/performance_manager/public/features.h"
#include "content/public/browser/browser_task_traits.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/web_contents.h"
#include "url/origin.h"
namespace performance_manager {
namespace {
// Singleton instance of PerformanceManagerImpl. Set from
// PerformanceManagerImpl::StartImpl() and reset from the destructor of
// PerformanceManagerImpl (PM sequence). Should only be accessed on the PM
// sequence.
PerformanceManagerImpl* g_performance_manager = nullptr;
// Indicates if a task posted to `GetTaskRunner()` will have
// access to a valid PerformanceManagerImpl instance via
// |g_performance_manager|. Should only be accessed on the main thread.
bool g_pm_is_available = false;
constexpr base::TaskPriority kPmTaskPriority = base::TaskPriority::USER_VISIBLE;
// Task traits appropriate for the PM task runner.
// NOTE: The PM task runner has to block shutdown as some of the tasks posted to
// it should be guaranteed to run before shutdown (e.g. removing some entries
// from the site data store).
constexpr base::TaskTraits kPMTaskTraits = {
kPmTaskPriority, base::TaskShutdownBehavior::BLOCK_SHUTDOWN,
base::MayBlock()};
// Builds a UI task runner with the appropriate traits for the PM.
// TODO(crbug.com/40755583): The PM task runner has to block shutdown as some of
// the tasks posted to it should be guaranteed to run before shutdown (e.g.
// removing some entries from the site data store). The UI thread ignores
// MayBlock and TaskShutdownBehavior, so these tasks and any blocking tasks must
// be found and migrated to a worker thread.
scoped_refptr<base::SequencedTaskRunner> GetUITaskRunner() {
return content::GetUIThreadTaskRunner({kPmTaskPriority});
}
// A `TaskRunner` which runs callbacks synchronously when they're posted with no
// delay from the UI thread, or posts to the UI thread otherwise.
//
// Note: The UI thread `TaskRunner` is obtained from `GetUITaskRunner()` in each
// method called, rather than being cached in a member, to ensure that the
// correct `TaskRunner` is used across tests that run in the same process.
// `GetUIThreadTaskRunner()` is not known to be costly.
class TaskRunnerWithSynchronousRunOnUIThread
: public base::SequencedTaskRunner {
public:
static scoped_refptr<base::SequencedTaskRunner> GetInstance() {
static base::NoDestructor<scoped_refptr<base::SequencedTaskRunner>>
instance(
base::MakeRefCounted<TaskRunnerWithSynchronousRunOnUIThread>());
return *instance;
}
TaskRunnerWithSynchronousRunOnUIThread() = default;
bool PostDelayedTask(const base::Location& from_here,
base::OnceClosure task,
base::TimeDelta delay) override {
auto task_runner = GetUITaskRunner();
if (task_runner->RunsTasksInCurrentSequence() && delay.is_zero()) {
std::move(task).Run();
return true;
}
return task_runner->PostDelayedTask(from_here, std::move(task), delay);
}
bool PostNonNestableDelayedTask(const base::Location& from_here,
base::OnceClosure task,
base::TimeDelta delay) override {
auto task_runner = GetUITaskRunner();
if (task_runner->RunsTasksInCurrentSequence() && delay.is_zero()) {
std::move(task).Run();
return true;
}
return task_runner->PostNonNestableDelayedTask(from_here, std::move(task),
delay);
}
base::DelayedTaskHandle PostCancelableDelayedTask(
base::subtle::PostDelayedTaskPassKey pass_key,
const base::Location& from_here,
base::OnceClosure task,
base::TimeDelta delay) override {
// There is no call to this method on the Performance Manager `TaskRunner`.
//
// All callers are annotated with `base::subtle::PostDelayedTaskPassKey`. In
// most cases, it's trivial to verify that they don't target this
// `TaskRunner`. To confirm that no calls are made via timers defined in
// base/timer/timer.cc, we manually verified that no `TaskRunner` obtained
// from `PerformanceManager(Impl)::GetTaskRunner()` is passed to
// `base::TimerBase::SetTaskRunner()`.
NOTREACHED_NORETURN();
}
base::DelayedTaskHandle PostCancelableDelayedTaskAt(
base::subtle::PostDelayedTaskPassKey pass_key,
const base::Location& from_here,
base::OnceClosure task,
base::TimeTicks delayed_run_time,
base::subtle::DelayPolicy delay_policy) override {
// There is no call to this method on the Performance Manager `TaskRunner`.
//
// See notes in `PostCancelableDelayedTask`.
NOTREACHED_NORETURN();
}
bool PostDelayedTaskAt(base::subtle::PostDelayedTaskPassKey pass_key,
const base::Location& from_here,
base::OnceClosure task,
base::TimeTicks delayed_run_time,
base::subtle::DelayPolicy delay_policy) override {
// There is no call to this method on the Performance Manager `TaskRunner`.
//
// See notes in `PostCancelableDelayedTask`.
NOTREACHED_NORETURN();
}
bool RunOrPostTask(base::subtle::RunOrPostTaskPassKey,
const base::Location& from_here,
base::OnceClosure task) override {
// There is no call to this method on the Performance Manager `TaskRunner`.
// The only call is in ipc/ipc_mojo_bootstrap.cc and it's trivial to verify
// that it doesn't target this `TaskRunner`.
NOTREACHED_NORETURN();
}
bool RunsTasksInCurrentSequence() const override {
return GetUITaskRunner()->RunsTasksInCurrentSequence();
}
private:
~TaskRunnerWithSynchronousRunOnUIThread() override = default;
};
} // namespace
// static
bool PerformanceManager::IsAvailable() {
return g_pm_is_available;
}
PerformanceManagerImpl::~PerformanceManagerImpl() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK_EQ(g_performance_manager, this);
// TODO(crbug.com/40629049): Move this to a TearDown function.
graph_.TearDown();
g_performance_manager = nullptr;
if (on_destroyed_callback_)
std::move(on_destroyed_callback_).Run();
}
// static
void PerformanceManagerImpl::CallOnGraphImpl(const base::Location& from_here,
base::OnceClosure callback) {
DCHECK(callback);
GetTaskRunner()->PostTask(from_here, std::move(callback));
}
// static
void PerformanceManagerImpl::CallOnGraphImpl(const base::Location& from_here,
GraphImplCallback callback) {
DCHECK(callback);
GetTaskRunner()->PostTask(
from_here,
base::BindOnce(&PerformanceManagerImpl::RunCallbackWithGraphImpl,
std::move(callback)));
}
// static
std::unique_ptr<PerformanceManagerImpl> PerformanceManagerImpl::Create(
GraphImplCallback on_start) {
DCHECK(!g_pm_is_available);
g_pm_is_available = true;
std::unique_ptr<PerformanceManagerImpl> instance =
base::WrapUnique(new PerformanceManagerImpl());
GetTaskRunner()->PostTask(
FROM_HERE,
base::BindOnce(&PerformanceManagerImpl::OnStartImpl,
base::Unretained(instance.get()), std::move(on_start)));
return instance;
}
// static
void PerformanceManagerImpl::Destroy(
std::unique_ptr<PerformanceManager> instance) {
DCHECK(g_pm_is_available);
g_pm_is_available = false;
GetTaskRunner()->DeleteSoon(FROM_HERE, instance.release());
}
// static
std::unique_ptr<FrameNodeImpl> PerformanceManagerImpl::CreateFrameNode(
ProcessNodeImpl* process_node,
PageNodeImpl* page_node,
FrameNodeImpl* parent_frame_node,
FrameNodeImpl* outer_document_for_fenced_frame,
int render_frame_id,
const blink::LocalFrameToken& frame_token,
content::BrowsingInstanceId browsing_instance_id,
content::SiteInstanceGroupId site_instance_group_id,
bool is_current,
FrameNodeCreationCallback creation_callback) {
return CreateNodeImpl<FrameNodeImpl>(
std::move(creation_callback), process_node, page_node, parent_frame_node,
outer_document_for_fenced_frame, render_frame_id, frame_token,
browsing_instance_id, site_instance_group_id, is_current);
}
// static
std::unique_ptr<PageNodeImpl> PerformanceManagerImpl::CreatePageNode(
base::WeakPtr<content::WebContents> web_contents,
const std::string& browser_context_id,
const GURL& visible_url,
PagePropertyFlags initial_property_flags,
base::TimeTicks visibility_change_time) {
return CreateNodeImpl<PageNodeImpl>(
base::OnceCallback<void(PageNodeImpl*)>(), std::move(web_contents),
browser_context_id, visible_url, initial_property_flags,
visibility_change_time);
}
// static
std::unique_ptr<ProcessNodeImpl> PerformanceManagerImpl::CreateProcessNode(
BrowserProcessNodeTag tag) {
return CreateNodeImpl<ProcessNodeImpl>(
base::OnceCallback<void(ProcessNodeImpl*)>(), tag);
}
// static
std::unique_ptr<ProcessNodeImpl> PerformanceManagerImpl::CreateProcessNode(
RenderProcessHostProxy render_process_host_proxy,
base::TaskPriority priority) {
return CreateNodeImpl<ProcessNodeImpl>(
base::OnceCallback<void(ProcessNodeImpl*)>(),
std::move(render_process_host_proxy), priority);
}
// static
std::unique_ptr<ProcessNodeImpl> PerformanceManagerImpl::CreateProcessNode(
content::ProcessType process_type,
BrowserChildProcessHostProxy browser_child_process_host_proxy) {
return CreateNodeImpl<ProcessNodeImpl>(
base::OnceCallback<void(ProcessNodeImpl*)>(), process_type,
std::move(browser_child_process_host_proxy));
}
// static
std::unique_ptr<WorkerNodeImpl> PerformanceManagerImpl::CreateWorkerNode(
const std::string& browser_context_id,
WorkerNode::WorkerType worker_type,
ProcessNodeImpl* process_node,
const blink::WorkerToken& worker_token,
const url::Origin& origin) {
return CreateNodeImpl<WorkerNodeImpl>(
base::OnceCallback<void(WorkerNodeImpl*)>(), browser_context_id,
worker_type, process_node, worker_token, origin);
}
// static
void PerformanceManagerImpl::DeleteNode(std::unique_ptr<NodeBase> node) {
CallOnGraphImpl(
FROM_HERE,
base::BindOnce(&PerformanceManagerImpl::DeleteNodeImpl, node.release()));
}
// static
void PerformanceManagerImpl::BatchDeleteNodes(
std::vector<std::unique_ptr<NodeBase>> nodes) {
// Move the nodes vector to the heap.
auto nodes_ptr = std::make_unique<std::vector<std::unique_ptr<NodeBase>>>(
std::move(nodes));
CallOnGraphImpl(FROM_HERE,
base::BindOnce(&PerformanceManagerImpl::BatchDeleteNodesImpl,
nodes_ptr.release()));
}
// static
bool PerformanceManagerImpl::OnPMTaskRunnerForTesting() {
return GetTaskRunner()->RunsTasksInCurrentSequence();
}
// static
void PerformanceManagerImpl::SetOnDestroyedCallbackForTesting(
base::OnceClosure callback) {
// Bind the callback in one that can be called on the PM sequence (it also
// binds the main thread, and bounces a task back to that thread).
scoped_refptr<base::SequencedTaskRunner> main_thread =
base::SequencedTaskRunner::GetCurrentDefault();
base::OnceClosure pm_callback = base::BindOnce(
[](scoped_refptr<base::SequencedTaskRunner> main_thread,
base::OnceClosure callback) {
main_thread->PostTask(FROM_HERE, std::move(callback));
},
main_thread, std::move(callback));
// Pass the callback to the PM.
GetTaskRunner()->PostTask(
FROM_HERE,
base::BindOnce(&PerformanceManagerImpl::SetOnDestroyedCallbackImpl,
std::move(pm_callback)));
}
PerformanceManagerImpl::PerformanceManagerImpl() {
DETACH_FROM_SEQUENCE(sequence_checker_);
}
// static
scoped_refptr<base::SequencedTaskRunner>
PerformanceManagerImpl::GetTaskRunner() {
if (base::FeatureList::IsEnabled(features::kRunOnMainThreadSync)) {
return TaskRunnerWithSynchronousRunOnUIThread::GetInstance();
}
static base::LazyThreadPoolSequencedTaskRunner
performance_manager_task_runner =
LAZY_THREAD_POOL_SEQUENCED_TASK_RUNNER_INITIALIZER(kPMTaskTraits);
return performance_manager_task_runner.Get();
}
PerformanceManagerImpl* PerformanceManagerImpl::GetInstance() {
DCHECK(GetTaskRunner()->RunsTasksInCurrentSequence());
return g_performance_manager;
}
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)
std::move(callback).Run(node);
}
} // namespace
// static
template <typename NodeType, typename... Args>
std::unique_ptr<NodeType> PerformanceManagerImpl::CreateNodeImpl(
base::OnceCallback<void(NodeType*)> creation_callback,
Args&&... constructor_args) {
std::unique_ptr<NodeType> new_node =
std::make_unique<NodeType>(std::forward<Args>(constructor_args)...);
CallOnGraphImpl(FROM_HERE,
base::BindOnce(&AddNodeAndInvokeCreationCallback<NodeType>,
std::move(creation_callback),
base::Unretained(new_node.get())));
return new_node;
}
// static
void PerformanceManagerImpl::DeleteNodeImpl(NodeBase* node_ptr,
GraphImpl* graph) {
// Must be done first to avoid leaking |node_ptr|.
std::unique_ptr<NodeBase> node(node_ptr);
graph->RemoveNode(node.get());
}
namespace {
void RemoveFrameAndChildrenFromGraph(FrameNodeImpl* frame_node,
GraphImpl* graph) {
// Recurse on the first child while there is one.
while (!frame_node->child_frame_nodes().empty()) {
RemoveFrameAndChildrenFromGraph(*(frame_node->child_frame_nodes().begin()),
graph);
}
// Now that all children are deleted, delete this frame.
graph->RemoveNode(frame_node);
}
} // namespace
// static
void PerformanceManagerImpl::BatchDeleteNodesImpl(
std::vector<std::unique_ptr<NodeBase>>* nodes_ptr,
GraphImpl* graph) {
// Must be done first to avoid leaking |nodes_ptr|.
std::unique_ptr<std::vector<std::unique_ptr<NodeBase>>> nodes(nodes_ptr);
base::flat_set<ProcessNodeImpl*> process_nodes;
for (const auto& node : *nodes) {
switch (node->GetNodeType()) {
case PageNodeImpl::Type(): {
auto* page_node = PageNodeImpl::FromNodeBase(node.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);
}
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(node.get());
process_nodes.insert(process_node);
break;
}
case FrameNodeImpl::Type():
break;
case WorkerNodeImpl::Type(): {
auto* worker_node = WorkerNodeImpl::FromNodeBase(node.get());
graph->RemoveNode(worker_node);
break;
}
case SystemNodeImpl::Type(): {
NOTREACHED_IN_MIGRATION();
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 PerformanceManagerImpl::OnStartImpl(GraphImplCallback on_start) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK(!g_performance_manager);
g_performance_manager = this;
graph_.SetUp();
graph_.set_ukm_recorder(ukm::UkmRecorder::Get());
std::move(on_start).Run(&graph_);
}
// static
void PerformanceManagerImpl::RunCallbackWithGraphImpl(
GraphImplCallback graph_callback) {
DCHECK(GetTaskRunner()->RunsTasksInCurrentSequence());
if (g_performance_manager)
std::move(graph_callback).Run(&g_performance_manager->graph_);
}
// static
void PerformanceManagerImpl::RunCallbackWithGraph(
GraphCallback graph_callback) {
DCHECK(GetTaskRunner()->RunsTasksInCurrentSequence());
if (g_performance_manager)
std::move(graph_callback).Run(&g_performance_manager->graph_);
}
// static
void PerformanceManagerImpl::SetOnDestroyedCallbackImpl(
base::OnceClosure callback) {
DCHECK(GetTaskRunner()->RunsTasksInCurrentSequence());
if (g_performance_manager) {
DCHECK_CALLED_ON_VALID_SEQUENCE(g_performance_manager->sequence_checker_);
g_performance_manager->on_destroyed_callback_ = std::move(callback);
}
}
} // namespace performance_manager