Commit 6c6de388 authored by Patrick Monette's avatar Patrick Monette Committed by Commit Bot

Reland "[PM] Add SharedWorkerWatcher"

This is a reland of 7c390807

The fix for the issue that caused the revert is here:
https://chromium-review.googlesource.com/c/chromium/src/+/1835164

Original change's description:
> [PM] Add SharedWorkerWatcher
>
> This class observes running shared worker instances, keeps track of
> the connection to their client frames and manages the lifetime of
> worker nodes in the Performance Manager graph.
>
> Bug: 993029
> Change-Id: I2e0c7cd9f194a06405293c22fd543a14ff3c6cbc
> Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1757283
> Commit-Queue: Patrick Monette <pmonette@chromium.org>
> Reviewed-by: Matt Falkenhagen <falken@chromium.org>
> Reviewed-by: Sigurður Ásgeirsson <siggi@chromium.org>
> Cr-Commit-Position: refs/heads/master@{#701445}

Bug: 993029
Change-Id: Ib77ea36b9cc71ec83ccbe28173fc6053e4a497f8
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1829796
Commit-Queue: Patrick Monette <pmonette@chromium.org>
Reviewed-by: default avatarSigurður Ásgeirsson <siggi@chromium.org>
Reviewed-by: default avatarMatt Falkenhagen <falken@chromium.org>
Cr-Commit-Position: refs/heads/master@{#704055}
parent de1f81b5
...@@ -9,6 +9,8 @@ ...@@ -9,6 +9,8 @@
#include "base/bind.h" #include "base/bind.h"
#include "base/feature_list.h" #include "base/feature_list.h"
#include "build/build_config.h" #include "build/build_config.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/chrome_notification_types.h"
#include "chrome/browser/performance_manager/browser_child_process_watcher.h" #include "chrome/browser/performance_manager/browser_child_process_watcher.h"
#include "chrome/browser/performance_manager/decorators/frozen_frame_aggregator.h" #include "chrome/browser/performance_manager/decorators/frozen_frame_aggregator.h"
#include "chrome/browser/performance_manager/decorators/page_aggregator.h" #include "chrome/browser/performance_manager/decorators/page_aggregator.h"
...@@ -18,10 +20,14 @@ ...@@ -18,10 +20,14 @@
#include "chrome/browser/performance_manager/graph/policies/working_set_trimmer_policy.h" #include "chrome/browser/performance_manager/graph/policies/working_set_trimmer_policy.h"
#include "chrome/browser/performance_manager/observers/isolation_context_metrics.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/metrics_collector.h"
#include "chrome/browser/profiles/profile_manager.h"
#include "components/performance_manager/graph/graph_impl.h" #include "components/performance_manager/graph/graph_impl.h"
#include "components/performance_manager/performance_manager_impl.h" #include "components/performance_manager/performance_manager_impl.h"
#include "components/performance_manager/performance_manager_tab_helper.h" #include "components/performance_manager/performance_manager_tab_helper.h"
#include "components/performance_manager/render_process_user_data.h" #include "components/performance_manager/render_process_user_data.h"
#include "components/performance_manager/shared_worker_watcher.h"
#include "content/public/browser/notification_service.h"
#include "content/public/browser/storage_partition.h"
#include "content/public/common/content_features.h" #include "content/public/common/content_features.h"
#if defined(OS_LINUX) #if defined(OS_LINUX)
...@@ -37,6 +43,37 @@ ChromeBrowserMainExtraPartsPerformanceManager:: ...@@ -37,6 +43,37 @@ ChromeBrowserMainExtraPartsPerformanceManager::
ChromeBrowserMainExtraPartsPerformanceManager:: ChromeBrowserMainExtraPartsPerformanceManager::
~ChromeBrowserMainExtraPartsPerformanceManager() = default; ~ChromeBrowserMainExtraPartsPerformanceManager() = default;
// static
void ChromeBrowserMainExtraPartsPerformanceManager::
CreateDefaultPoliciesAndDecorators(performance_manager::GraphImpl* graph) {
graph->PassToGraph(std::make_unique<performance_manager::PageAggregator>());
graph->PassToGraph(
std::make_unique<performance_manager::FrozenFrameAggregator>());
graph->PassToGraph(
std::make_unique<performance_manager::PageAlmostIdleDecorator>());
graph->PassToGraph(
std::make_unique<performance_manager::IsolationContextMetrics>());
graph->PassToGraph(std::make_unique<performance_manager::MetricsCollector>());
graph->PassToGraph(
std::make_unique<performance_manager::ProcessMetricsDecorator>());
if (performance_manager::policies::WorkingSetTrimmerPolicy::
PlatformSupportsWorkingSetTrim()) {
graph->PassToGraph(performance_manager::policies::WorkingSetTrimmerPolicy::
CreatePolicyForPlatform());
}
#if defined(OS_LINUX)
#if BUILDFLAG(USE_TCMALLOC)
if (base::FeatureList::IsEnabled(
performance_manager::features::kDynamicTcmallocTuning)) {
graph->PassToGraph(std::make_unique<
performance_manager::policies::DynamicTcmallocPolicy>());
}
#endif // BUILDFLAG(USE_TCMALLOC)
#endif // defined(OS_LINUX)
}
void ChromeBrowserMainExtraPartsPerformanceManager::PostCreateThreads() { void ChromeBrowserMainExtraPartsPerformanceManager::PostCreateThreads() {
performance_manager_ = performance_manager::PerformanceManagerImpl::Create( performance_manager_ = performance_manager::PerformanceManagerImpl::Create(
base::BindOnce(&ChromeBrowserMainExtraPartsPerformanceManager:: base::BindOnce(&ChromeBrowserMainExtraPartsPerformanceManager::
...@@ -44,6 +81,15 @@ void ChromeBrowserMainExtraPartsPerformanceManager::PostCreateThreads() { ...@@ -44,6 +81,15 @@ void ChromeBrowserMainExtraPartsPerformanceManager::PostCreateThreads() {
browser_child_process_watcher_ = browser_child_process_watcher_ =
std::make_unique<performance_manager::BrowserChildProcessWatcher>(); std::make_unique<performance_manager::BrowserChildProcessWatcher>();
browser_child_process_watcher_->Initialize(); browser_child_process_watcher_->Initialize();
// There are no existing loaded profiles.
DCHECK(g_browser_process->profile_manager()->GetLoadedProfiles().empty());
// Register for profile events to observe them as they come and go.
notification_registrar_.Add(this, chrome::NOTIFICATION_PROFILE_CREATED,
content::NotificationService::AllSources());
notification_registrar_.Add(this, chrome::NOTIFICATION_PROFILE_DESTROYED,
content::NotificationService::AllSources());
} }
void ChromeBrowserMainExtraPartsPerformanceManager::PostMainMessageLoopRun() { void ChromeBrowserMainExtraPartsPerformanceManager::PostMainMessageLoopRun() {
...@@ -52,6 +98,11 @@ void ChromeBrowserMainExtraPartsPerformanceManager::PostMainMessageLoopRun() { ...@@ -52,6 +98,11 @@ void ChromeBrowserMainExtraPartsPerformanceManager::PostMainMessageLoopRun() {
browser_child_process_watcher_->TearDown(); browser_child_process_watcher_->TearDown();
browser_child_process_watcher_.reset(); browser_child_process_watcher_.reset();
// Clear up the worker nodes.
for (auto& shared_worker_watcher : shared_worker_watchers_)
shared_worker_watcher.second->TearDown();
shared_worker_watchers_.clear();
// There may still be WebContents with attached tab helpers at this point in // There may still be WebContents with attached tab helpers at this point in
// time, and there's no convenient later call-out to destroy the performance // time, and there's no convenient later call-out to destroy the performance
// manager. To release the page and frame nodes, detach the tab helpers // manager. To release the page and frame nodes, detach the tab helpers
...@@ -64,35 +115,52 @@ void ChromeBrowserMainExtraPartsPerformanceManager::PostMainMessageLoopRun() { ...@@ -64,35 +115,52 @@ void ChromeBrowserMainExtraPartsPerformanceManager::PostMainMessageLoopRun() {
performance_manager::PerformanceManagerImpl::Destroy( performance_manager::PerformanceManagerImpl::Destroy(
std::move(performance_manager_)); std::move(performance_manager_));
}
// static // Unregister to profile notifications so as to not create more worker
void ChromeBrowserMainExtraPartsPerformanceManager:: // watchers after this point.
CreateDefaultPoliciesAndDecorators(performance_manager::GraphImpl* graph) { notification_registrar_.RemoveAll();
graph->PassToGraph(std::make_unique<performance_manager::PageAggregator>()); }
graph->PassToGraph(
std::make_unique<performance_manager::FrozenFrameAggregator>());
graph->PassToGraph(
std::make_unique<performance_manager::PageAlmostIdleDecorator>());
graph->PassToGraph(
std::make_unique<performance_manager::IsolationContextMetrics>());
graph->PassToGraph(std::make_unique<performance_manager::MetricsCollector>());
graph->PassToGraph(
std::make_unique<performance_manager::ProcessMetricsDecorator>());
if (performance_manager::policies::WorkingSetTrimmerPolicy:: void ChromeBrowserMainExtraPartsPerformanceManager::Observe(
PlatformSupportsWorkingSetTrim()) { int type,
graph->PassToGraph(performance_manager::policies::WorkingSetTrimmerPolicy:: const content::NotificationSource& source,
CreatePolicyForPlatform()); const content::NotificationDetails& details) {
switch (type) {
case chrome::NOTIFICATION_PROFILE_CREATED: {
Profile* profile = content::Source<Profile>(source).ptr();
CreateSharedWorkerWatcher(profile);
break;
} }
case chrome::NOTIFICATION_PROFILE_DESTROYED: {
#if defined(OS_LINUX) Profile* profile = content::Source<Profile>(source).ptr();
#if BUILDFLAG(USE_TCMALLOC) DeleteSharedWorkerWatcher(profile);
if (base::FeatureList::IsEnabled( break;
performance_manager::features::kDynamicTcmallocTuning)) {
graph->PassToGraph(std::make_unique<
performance_manager::policies::DynamicTcmallocPolicy>());
} }
#endif // BUILDFLAG(USE_TCMALLOC) default:
#endif // defined(OS_LINUX) NOTREACHED();
break;
}
}
void ChromeBrowserMainExtraPartsPerformanceManager::CreateSharedWorkerWatcher(
Profile* profile) {
auto shared_worker_watcher =
std::make_unique<performance_manager::SharedWorkerWatcher>(
profile->UniqueId(),
content::BrowserContext::GetDefaultStoragePartition(profile)
->GetSharedWorkerService(),
&process_node_source_, &frame_node_source_);
bool inserted = shared_worker_watchers_
.insert({profile, std::move(shared_worker_watcher)})
.second;
DCHECK(inserted);
}
void ChromeBrowserMainExtraPartsPerformanceManager::DeleteSharedWorkerWatcher(
Profile* profile) {
auto it = shared_worker_watchers_.find(profile);
DCHECK(it != shared_worker_watchers_.end());
it->second->TearDown();
shared_worker_watchers_.erase(it);
} }
...@@ -8,17 +8,28 @@ ...@@ -8,17 +8,28 @@
#include <memory> #include <memory>
#include "base/compiler_specific.h" #include "base/compiler_specific.h"
#include "base/containers/flat_map.h"
#include "base/macros.h" #include "base/macros.h"
#include "chrome/browser/chrome_browser_main_extra_parts.h" #include "chrome/browser/chrome_browser_main_extra_parts.h"
#include "components/performance_manager/process_node_source.h"
#include "components/performance_manager/tab_helper_frame_node_source.h"
#include "content/public/browser/notification_observer.h"
#include "content/public/browser/notification_registrar.h"
class Profile;
namespace performance_manager { namespace performance_manager {
class BrowserChildProcessWatcher; class BrowserChildProcessWatcher;
class GraphImpl; class GraphImpl;
class PerformanceManagerImpl; class PerformanceManagerImpl;
class SharedWorkerWatcher;
} // namespace performance_manager } // namespace performance_manager
// Handles the initialization of the performance manager and a few dependent
// classes that create/manage graph nodes.
class ChromeBrowserMainExtraPartsPerformanceManager class ChromeBrowserMainExtraPartsPerformanceManager
: public ChromeBrowserMainExtraParts { : public ChromeBrowserMainExtraParts,
public content::NotificationObserver {
public: public:
ChromeBrowserMainExtraPartsPerformanceManager(); ChromeBrowserMainExtraPartsPerformanceManager();
~ChromeBrowserMainExtraPartsPerformanceManager() override; ~ChromeBrowserMainExtraPartsPerformanceManager() override;
...@@ -31,12 +42,33 @@ class ChromeBrowserMainExtraPartsPerformanceManager ...@@ -31,12 +42,33 @@ class ChromeBrowserMainExtraPartsPerformanceManager
void PostCreateThreads() override; void PostCreateThreads() override;
void PostMainMessageLoopRun() override; void PostMainMessageLoopRun() override;
// content::NotificationObserver:
void Observe(int type,
const content::NotificationSource& source,
const content::NotificationDetails& details) override;
// Handlers for profile creation and destruction notifications.
void CreateSharedWorkerWatcher(Profile* profile);
void DeleteSharedWorkerWatcher(Profile* profile);
std::unique_ptr<performance_manager::PerformanceManagerImpl> std::unique_ptr<performance_manager::PerformanceManagerImpl>
performance_manager_; performance_manager_;
std::unique_ptr<performance_manager::BrowserChildProcessWatcher> std::unique_ptr<performance_manager::BrowserChildProcessWatcher>
browser_child_process_watcher_; browser_child_process_watcher_;
content::NotificationRegistrar notification_registrar_;
// Needed by the worker watchers to access existing process nodes and frame
// nodes.
performance_manager::ProcessNodeSource process_node_source_;
performance_manager::TabHelperFrameNodeSource frame_node_source_;
// Observes the lifetime of shared workers.
base::flat_map<Profile*,
std::unique_ptr<performance_manager::SharedWorkerWatcher>>
shared_worker_watchers_;
DISALLOW_COPY_AND_ASSIGN(ChromeBrowserMainExtraPartsPerformanceManager); DISALLOW_COPY_AND_ASSIGN(ChromeBrowserMainExtraPartsPerformanceManager);
}; };
......
...@@ -43,6 +43,8 @@ static_library("performance_manager") { ...@@ -43,6 +43,8 @@ static_library("performance_manager") {
"performance_manager_lock_observer.h", "performance_manager_lock_observer.h",
"performance_manager_tab_helper.cc", "performance_manager_tab_helper.cc",
"performance_manager_tab_helper.h", "performance_manager_tab_helper.h",
"process_node_source.cc",
"process_node_source.h",
"public/frame_priority/frame_priority.h", "public/frame_priority/frame_priority.h",
"public/frame_priority/max_vote_aggregator.h", "public/frame_priority/max_vote_aggregator.h",
"public/frame_priority/override_vote_aggregator.h", "public/frame_priority/override_vote_aggregator.h",
...@@ -61,6 +63,10 @@ static_library("performance_manager") { ...@@ -61,6 +63,10 @@ static_library("performance_manager") {
"render_process_host_proxy.cc", "render_process_host_proxy.cc",
"render_process_user_data.cc", "render_process_user_data.cc",
"render_process_user_data.h", "render_process_user_data.h",
"shared_worker_watcher.cc",
"shared_worker_watcher.h",
"tab_helper_frame_node_source.cc",
"tab_helper_frame_node_source.h",
"web_contents_proxy.cc", "web_contents_proxy.cc",
"web_contents_proxy_impl.cc", "web_contents_proxy_impl.cc",
"web_contents_proxy_impl.h", "web_contents_proxy_impl.h",
...@@ -99,6 +105,7 @@ source_set("unit_tests") { ...@@ -99,6 +105,7 @@ source_set("unit_tests") {
"performance_manager_tab_helper_unittest.cc", "performance_manager_tab_helper_unittest.cc",
"performance_manager_test_harness.cc", "performance_manager_test_harness.cc",
"performance_manager_test_harness.h", "performance_manager_test_harness.h",
"shared_worker_watcher_unittest.cc",
"web_contents_proxy_unittest.cc", "web_contents_proxy_unittest.cc",
] ]
......
// Copyright 2019 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.
#ifndef COMPONENTS_PERFORMANCE_MANAGER_FRAME_NODE_SOURCE_H_
#define COMPONENTS_PERFORMANCE_MANAGER_FRAME_NODE_SOURCE_H_
#include "base/callback.h"
#include "base/observer_list_types.h"
namespace performance_manager {
class FrameNodeImpl;
// Represents a source of existing frame nodes that lives on the main thread.
// In practice, this is used by the worker watchers as an abstraction over the
// PerformanceManagerTabHelper to make testing easier.
class FrameNodeSource {
public:
virtual ~FrameNodeSource() = default;
using OnbeforeFrameNodeRemovedCallback =
base::OnceCallback<void(FrameNodeImpl*)>;
// Returns the frame node associated with |render_process_id| and |frame_id|.
// Returns null if no such node exists.
virtual FrameNodeImpl* GetFrameNode(int render_process_id, int frame_id) = 0;
// Subscribes to receive removal notification for a frame node.
virtual void SubscribeToFrameNode(
int render_process_id,
int frame_id,
OnbeforeFrameNodeRemovedCallback
on_before_frame_node_removed_callback) = 0;
// Unsubscribes to a frame node
virtual void UnsubscribeFromFrameNode(int render_process_id,
int frame_id) = 0;
};
} // namespace performance_manager
#endif // COMPONENTS_PERFORMANCE_MANAGER_FRAME_NODE_SOURCE_H_
...@@ -248,6 +248,11 @@ void PerformanceManagerImpl::BatchDeleteNodesImpl( ...@@ -248,6 +248,11 @@ void PerformanceManagerImpl::BatchDeleteNodesImpl(
} }
case FrameNodeImpl::Type(): case FrameNodeImpl::Type():
break; break;
case WorkerNodeImpl::Type(): {
auto* worker_node = WorkerNodeImpl::FromNodeBase(it->get());
graph_.RemoveNode(worker_node);
break;
}
case SystemNodeImpl::Type(): case SystemNodeImpl::Type():
case NodeTypeEnum::kInvalidType: case NodeTypeEnum::kInvalidType:
default: { default: {
......
...@@ -5,6 +5,7 @@ ...@@ -5,6 +5,7 @@
#include "components/performance_manager/performance_manager_tab_helper.h" #include "components/performance_manager/performance_manager_tab_helper.h"
#include <type_traits> #include <type_traits>
#include <utility>
#include <vector> #include <vector>
#include "base/bind.h" #include "base/bind.h"
...@@ -86,8 +87,16 @@ void PerformanceManagerTabHelper::TearDown() { ...@@ -86,8 +87,16 @@ void PerformanceManagerTabHelper::TearDown() {
// incineration. // incineration.
std::vector<std::unique_ptr<NodeBase>> nodes; std::vector<std::unique_ptr<NodeBase>> nodes;
nodes.push_back(std::move(page_node_)); nodes.push_back(std::move(page_node_));
for (auto& kv : frames_) for (auto& kv : frames_) {
nodes.push_back(std::move(kv.second)); std::unique_ptr<FrameNodeImpl> frame_node = std::move(kv.second);
// Notify observers.
for (Observer& observer : observers_)
observer.OnBeforeFrameNodeRemoved(this, frame_node.get());
// Ensure the node will be deleted on the graph sequence.
nodes.push_back(std::move(frame_node));
}
frames_.clear(); frames_.clear();
...@@ -169,7 +178,14 @@ void PerformanceManagerTabHelper::RenderFrameDeleted( ...@@ -169,7 +178,14 @@ void PerformanceManagerTabHelper::RenderFrameDeleted(
} }
DCHECK(it != frames_.end()); DCHECK(it != frames_.end());
performance_manager_->DeleteNode(std::move(it->second)); std::unique_ptr<FrameNodeImpl> frame_node = std::move(it->second);
// Notify observers.
for (Observer& observer : observers_)
observer.OnBeforeFrameNodeRemoved(this, frame_node.get());
// Then delete the node.
performance_manager_->DeleteNode(std::move(frame_node));
frames_.erase(it); frames_.erase(it);
} }
...@@ -228,7 +244,7 @@ void PerformanceManagerTabHelper::RenderFrameHostChanged( ...@@ -228,7 +244,7 @@ void PerformanceManagerTabHelper::RenderFrameHostChanged(
new_frame->SetIsCurrent(true); new_frame->SetIsCurrent(true);
} }
}, },
base::Unretained(old_frame), base::Unretained(new_frame))); old_frame, new_frame));
} }
void PerformanceManagerTabHelper::DidStartLoading() { void PerformanceManagerTabHelper::DidStartLoading() {
...@@ -352,6 +368,26 @@ int64_t PerformanceManagerTabHelper::LastNavigationId() const { ...@@ -352,6 +368,26 @@ int64_t PerformanceManagerTabHelper::LastNavigationId() const {
return last_navigation_id_; return last_navigation_id_;
} }
FrameNodeImpl* PerformanceManagerTabHelper::GetFrameNode(
content::RenderFrameHost* render_frame_host) {
auto it = frames_.find(render_frame_host);
if (it == frames_.end()) {
// Avoid dereferencing an invalid iterator because it produces hard to debug
// crashes.
NOTREACHED();
return nullptr;
}
return it->second.get();
}
void PerformanceManagerTabHelper::AddObserver(Observer* observer) {
observers_.AddObserver(observer);
}
void PerformanceManagerTabHelper::RemoveObserver(Observer* observer) {
observers_.RemoveObserver(observer);
}
template <typename Functor, typename NodeType, typename... Args> template <typename Functor, typename NodeType, typename... Args>
void PerformanceManagerTabHelper::PostToGraph(const base::Location& from_here, void PerformanceManagerTabHelper::PostToGraph(const base::Location& from_here,
Functor&& functor, Functor&& functor,
......
...@@ -12,6 +12,7 @@ ...@@ -12,6 +12,7 @@
#include "base/macros.h" #include "base/macros.h"
#include "base/memory/weak_ptr.h" #include "base/memory/weak_ptr.h"
#include "base/observer_list.h"
#include "components/performance_manager/web_contents_proxy_impl.h" #include "components/performance_manager/web_contents_proxy_impl.h"
#include "content/public/browser/web_contents_observer.h" #include "content/public/browser/web_contents_observer.h"
#include "content/public/browser/web_contents_user_data.h" #include "content/public/browser/web_contents_user_data.h"
...@@ -66,6 +67,21 @@ class PerformanceManagerTabHelper ...@@ -66,6 +67,21 @@ class PerformanceManagerTabHelper
void SetUkmSourceIdForTesting(ukm::SourceId id) { ukm_source_id_ = id; } void SetUkmSourceIdForTesting(ukm::SourceId id) { ukm_source_id_ = id; }
// Retrieves the frame node associated with |render_frame_host|.
FrameNodeImpl* GetFrameNode(content::RenderFrameHost* render_frame_host);
class Observer : public base::CheckedObserver {
public:
// Invoked when a frame node is about to be removed from the graph.
virtual void OnBeforeFrameNodeRemoved(
PerformanceManagerTabHelper* performance_manager,
FrameNodeImpl* frame_node) = 0;
};
// Adds/removes an observer.
void AddObserver(Observer* observer);
void RemoveObserver(Observer* observer);
private: private:
friend class content::WebContentsUserData<PerformanceManagerTabHelper>; friend class content::WebContentsUserData<PerformanceManagerTabHelper>;
friend class WebContentsProxyImpl; friend class WebContentsProxyImpl;
...@@ -102,6 +118,8 @@ class PerformanceManagerTabHelper ...@@ -102,6 +118,8 @@ class PerformanceManagerTabHelper
// Maps from RenderFrameHost to the associated PM node. // Maps from RenderFrameHost to the associated PM node.
std::map<content::RenderFrameHost*, std::unique_ptr<FrameNodeImpl>> frames_; std::map<content::RenderFrameHost*, std::unique_ptr<FrameNodeImpl>> frames_;
base::ObserverList<Observer, true, false> observers_;
// All instances are linked together in a doubly linked list to allow orderly // All instances are linked together in a doubly linked list to allow orderly
// destruction at browser shutdown time. // destruction at browser shutdown time.
static PerformanceManagerTabHelper* first_; static PerformanceManagerTabHelper* first_;
......
// Copyright 2019 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 "components/performance_manager/process_node_source.h"
#include "components/performance_manager/render_process_user_data.h"
#include "content/public/browser/render_process_host.h"
namespace performance_manager {
ProcessNodeImpl* ProcessNodeSource::GetProcessNode(int render_process_id) {
auto* render_process_host =
content::RenderProcessHost::FromID(render_process_id);
DCHECK(render_process_host);
auto* render_process_user_data =
RenderProcessUserData::GetForRenderProcessHost(render_process_host);
DCHECK(render_process_user_data);
return render_process_user_data->process_node();
}
} // namespace performance_manager
// Copyright 2019 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.
#ifndef COMPONENTS_PERFORMANCE_MANAGER_PROCESS_NODE_SOURCE_H_
#define COMPONENTS_PERFORMANCE_MANAGER_PROCESS_NODE_SOURCE_H_
namespace performance_manager {
class ProcessNodeImpl;
// Represents a source of existing process nodes that lives on the main thread.
// In practice, this is used by the worker watchers as an abstraction over the
// peformance_manager::RenderProcessUserData to make testing easier.
class ProcessNodeSource {
public:
virtual ~ProcessNodeSource() = default;
// Retrieves the process node associated with the |render_process_id|.
virtual ProcessNodeImpl* GetProcessNode(int render_process_id);
};
} // namespace performance_manager
#endif // COMPONENTS_PERFORMANCE_MANAGER_PROCESS_NODE_SOURCE_H_
// Copyright 2019 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 "components/performance_manager/shared_worker_watcher.h"
#include <utility>
#include <vector>
#include "components/performance_manager/frame_node_source.h"
#include "components/performance_manager/graph/frame_node_impl.h"
#include "components/performance_manager/graph/worker_node_impl.h"
#include "components/performance_manager/performance_manager_impl.h"
#include "components/performance_manager/process_node_source.h"
#include "content/public/browser/shared_worker_instance.h"
namespace performance_manager {
namespace {
// Helper function to add |worker_node| as a child to |frame_node| on the PM
// sequence.
void AddWorkerToFrameNode(FrameNodeImpl* frame_node,
WorkerNodeImpl* worker_node,
GraphImpl* graph) {
worker_node->AddClientFrame(frame_node);
}
// Helper function to remove |worker_node| from |frame_node| on the PM sequence.
void RemoveWorkerFromFrameNode(FrameNodeImpl* frame_node,
WorkerNodeImpl* worker_node,
GraphImpl* graph) {
worker_node->RemoveClientFrame(frame_node);
}
// Helper function to remove all |worker_nodes| from |frame_node| on the PM
// sequence.
void RemoveWorkersFromFrameNode(
FrameNodeImpl* frame_node,
const base::flat_set<WorkerNodeImpl*>& worker_nodes,
GraphImpl* graph) {
for (auto* worker_node : worker_nodes)
worker_node->RemoveClientFrame(frame_node);
}
} // namespace
SharedWorkerWatcher::SharedWorkerWatcher(
const std::string& browser_context_id,
content::SharedWorkerService* shared_worker_service,
ProcessNodeSource* process_node_source,
FrameNodeSource* frame_node_source)
: browser_context_id_(browser_context_id),
shared_worker_service_observer_(this),
process_node_source_(process_node_source),
frame_node_source_(frame_node_source) {
DCHECK(shared_worker_service);
DCHECK(process_node_source_);
DCHECK(frame_node_source_);
shared_worker_service_observer_.Add(shared_worker_service);
}
SharedWorkerWatcher::~SharedWorkerWatcher() {
DCHECK(frame_node_child_workers_.empty());
DCHECK(worker_nodes_.empty());
DCHECK(!shared_worker_service_observer_.IsObservingSources());
}
void SharedWorkerWatcher::TearDown() {
// First clear client-child relations between frames and workers.
for (auto& kv : frame_node_child_workers_) {
const FrameInfo& frame_info = kv.first;
base::flat_set<WorkerNodeImpl*>& child_workers = kv.second;
frame_node_source_->UnsubscribeFromFrameNode(frame_info.render_process_id,
frame_info.frame_id);
// Disconnect all child workers from |frame_node|.
FrameNodeImpl* frame_node = frame_node_source_->GetFrameNode(
frame_info.render_process_id, frame_info.frame_id);
DCHECK(frame_node);
DCHECK(!child_workers.empty());
PerformanceManagerImpl::CallOnGraphImpl(
FROM_HERE,
base::BindOnce(&RemoveWorkersFromFrameNode, frame_node, child_workers));
}
frame_node_child_workers_.clear();
// Then clean all the worker nodes.
std::vector<std::unique_ptr<NodeBase>> nodes;
nodes.reserve(worker_nodes_.size());
for (auto& node : worker_nodes_)
nodes.push_back(std::move(node.second));
worker_nodes_.clear();
PerformanceManagerImpl::GetInstance()->BatchDeleteNodes(std::move(nodes));
shared_worker_service_observer_.RemoveAll();
}
void SharedWorkerWatcher::OnWorkerStarted(
const content::SharedWorkerInstance& instance,
int worker_process_id,
const base::UnguessableToken& dev_tools_token) {
auto worker_node = PerformanceManagerImpl::GetInstance()->CreateWorkerNode(
browser_context_id_, WorkerNode::WorkerType::kShared,
process_node_source_->GetProcessNode(worker_process_id), instance.url(),
dev_tools_token);
bool inserted =
worker_nodes_.emplace(instance, std::move(worker_node)).second;
DCHECK(inserted);
}
void SharedWorkerWatcher::OnBeforeWorkerTerminated(
const content::SharedWorkerInstance& instance) {
auto it = worker_nodes_.find(instance);
DCHECK(it != worker_nodes_.end());
auto worker_node = std::move(it->second);
#if DCHECK_IS_ON()
DCHECK(!base::Contains(clients_to_remove_, worker_node.get()));
#endif // DCHECK_IS_ON()
PerformanceManagerImpl::GetInstance()->DeleteNode(std::move(worker_node));
worker_nodes_.erase(it);
}
void SharedWorkerWatcher::OnClientAdded(
const content::SharedWorkerInstance& instance,
int client_process_id,
int frame_id) {
FrameNodeImpl* frame_node =
frame_node_source_->GetFrameNode(client_process_id, frame_id);
DCHECK(frame_node);
// Connect the nodes in the PM graph.
WorkerNodeImpl* worker_node = GetWorkerNode(instance);
PerformanceManagerImpl::CallOnGraphImpl(
FROM_HERE,
base::BindOnce(&AddWorkerToFrameNode, frame_node, worker_node));
// Keep track of the shared workers that this frame is a client to.
if (AddChildWorker(client_process_id, frame_id, worker_node)) {
frame_node_source_->SubscribeToFrameNode(
client_process_id, frame_id,
base::BindOnce(&SharedWorkerWatcher::OnBeforeFrameNodeRemoved,
base::Unretained(this), client_process_id, frame_id));
}
}
void SharedWorkerWatcher::OnClientRemoved(
const content::SharedWorkerInstance& instance,
int client_process_id,
int frame_id) {
WorkerNodeImpl* worker_node = GetWorkerNode(instance);
FrameNodeImpl* frame_node =
frame_node_source_->GetFrameNode(client_process_id, frame_id);
// It's possible that the frame was destroyed before receiving the
// OnClientRemoved() for all of its child shared worker. Nothing to do in
// that case because OnBeforeFrameNodeRemoved() took care of removing this
// client from its child worker nodes.
if (!frame_node) {
#if DCHECK_IS_ON()
// These debug only checks ensure that this code path is only taken if
// OnBeforeFrameNodeRemoved() was already called for that frame.
auto it = clients_to_remove_.find(worker_node);
DCHECK(it != clients_to_remove_.end());
int& count = it->second;
DCHECK_GT(count, 0);
--count;
if (count == 0)
clients_to_remove_.erase(it);
#endif // DCHECK_IS_ON()
return;
}
// Disconnect the node.
PerformanceManagerImpl::CallOnGraphImpl(
FROM_HERE,
base::BindOnce(&RemoveWorkerFromFrameNode, frame_node, worker_node));
// Remove |worker_node| from the set of workers that this frame is a client
// of.
if (RemoveChildWorker(client_process_id, frame_id, worker_node))
frame_node_source_->UnsubscribeFromFrameNode(client_process_id, frame_id);
}
void SharedWorkerWatcher::OnBeforeFrameNodeRemoved(int render_process_id,
int frame_id,
FrameNodeImpl* frame_node) {
auto it =
frame_node_child_workers_.find(FrameInfo{render_process_id, frame_id});
DCHECK(it != frame_node_child_workers_.end());
// Clean up all child workers of this frame node.
base::flat_set<WorkerNodeImpl*> child_workers = std::move(it->second);
frame_node_child_workers_.erase(it);
// Disconnect all child workers from |frame_node|.
DCHECK(!child_workers.empty());
PerformanceManagerImpl::CallOnGraphImpl(
FROM_HERE,
base::BindOnce(&RemoveWorkersFromFrameNode, frame_node, child_workers));
#if DCHECK_IS_ON()
for (WorkerNodeImpl* worker_node : child_workers) {
// Now expect that this frame will be removed as a client for each worker
// in |child_workers|.
// Note: the [] operator is intentionally used to default initialize the
// count to zero if needed.
clients_to_remove_[worker_node]++;
}
#endif // DCHECK_IS_ON()
}
bool SharedWorkerWatcher::AddChildWorker(int render_process_id,
int frame_id,
WorkerNodeImpl* child_worker_node) {
auto insertion_result =
frame_node_child_workers_.insert({{render_process_id, frame_id}, {}});
auto& child_workers = insertion_result.first->second;
bool inserted = child_workers.insert(child_worker_node).second;
DCHECK(inserted);
return insertion_result.second;
}
bool SharedWorkerWatcher::RemoveChildWorker(int render_process_id,
int frame_id,
WorkerNodeImpl* child_worker_node) {
auto it =
frame_node_child_workers_.find(FrameInfo{render_process_id, frame_id});
DCHECK(it != frame_node_child_workers_.end());
auto& child_workers = it->second;
size_t removed = child_workers.erase(child_worker_node);
DCHECK_EQ(removed, 1u);
if (child_workers.empty()) {
frame_node_child_workers_.erase(it);
return true;
}
return false;
}
WorkerNodeImpl* SharedWorkerWatcher::GetWorkerNode(
const content::SharedWorkerInstance& instance) {
auto it = worker_nodes_.find(instance);
if (it == worker_nodes_.end()) {
NOTREACHED();
return nullptr;
}
return it->second.get();
}
bool operator<(const SharedWorkerWatcher::FrameInfo& lhs,
const SharedWorkerWatcher::FrameInfo& rhs) {
return std::tie(lhs.render_process_id, lhs.frame_id) <
std::tie(rhs.render_process_id, rhs.frame_id);
}
} // namespace performance_manager
// Copyright 2019 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.
#ifndef COMPONENTS_PERFORMANCE_MANAGER_SHARED_WORKER_WATCHER_H_
#define COMPONENTS_PERFORMANCE_MANAGER_SHARED_WORKER_WATCHER_H_
#include <memory>
#include <string>
#include "base/containers/flat_map.h"
#include "base/containers/flat_set.h"
#include "base/logging.h"
#include "base/macros.h"
#include "base/scoped_observer.h"
#include "content/public/browser/shared_worker_service.h"
namespace content {
class SharedWorkerInstance;
}
namespace performance_manager {
class FrameNodeImpl;
class FrameNodeSource;
class ProcessNodeSource;
class WorkerNodeImpl;
// This class keeps track of running shared workers for a single browser context
// and handles the ownership of the worker nodes.
class SharedWorkerWatcher : public content::SharedWorkerService::Observer {
public:
SharedWorkerWatcher(const std::string& browser_context_id,
content::SharedWorkerService* shared_worker_service,
ProcessNodeSource* process_node_source,
FrameNodeSource* frame_node_source);
~SharedWorkerWatcher() override;
// Cleans up this instance and ensures shared worker nodes are correctly
// destroyed on the PM graph.
void TearDown();
// content::SharedWorkerService::Observer:
void OnWorkerStarted(const content::SharedWorkerInstance& instance,
int worker_process_id,
const base::UnguessableToken& dev_tools_token) override;
void OnBeforeWorkerTerminated(
const content::SharedWorkerInstance& instance) override;
void OnClientAdded(const content::SharedWorkerInstance& instance,
int client_process_id,
int frame_id) override;
void OnClientRemoved(const content::SharedWorkerInstance& instance,
int client_process_id,
int frame_id) override;
private:
friend class SharedWorkerWatcherTest;
void OnBeforeFrameNodeRemoved(int render_process_id,
int frame_id,
FrameNodeImpl* frame_node);
bool AddChildWorker(int render_process_id,
int frame_id,
WorkerNodeImpl* child_worker_node);
bool RemoveChildWorker(int render_process_id,
int frame_id,
WorkerNodeImpl* child_worker_node);
// Helper function to retrieve an existing worker node.
WorkerNodeImpl* GetWorkerNode(const content::SharedWorkerInstance& instance);
// The ID of the BrowserContext who owns the shared worker service.
const std::string browser_context_id_;
// Observes the SharedWorkerService for this browser context.
ScopedObserver<content::SharedWorkerService,
content::SharedWorkerService::Observer>
shared_worker_service_observer_;
// Used to retrieve an existing process node from its render process ID.
ProcessNodeSource* const process_node_source_;
// Used to retrieve an existing frame node from its render process ID and
// frame ID. Also allows to subscribe to a frame's deletion notification.
FrameNodeSource* const frame_node_source_;
// Maps each SharedWorkerInstance to its worker node.
base::flat_map<content::SharedWorkerInstance, std::unique_ptr<WorkerNodeImpl>>
worker_nodes_;
// Maps each frame to the shared workers that this frame is a client of. This
// is used when a frame is torn down before the OnBeforeWorkerTerminated() is
// received, to ensure the deletion of the worker nodes in the right order
// (workers before frames).
struct FrameInfo {
int render_process_id;
int frame_id;
};
// Comparison operator to allow using FrameInfo as a key.
friend bool operator<(const FrameInfo& lhs, const FrameInfo& rhs);
base::flat_map<FrameInfo, base::flat_set<WorkerNodeImpl*>>
frame_node_child_workers_;
#if DCHECK_IS_ON()
// Keeps track of how many OnClientRemoved() calls are expected for an
// existing worker. This happens when OnBeforeFrameNodeRemoved() is invoked
// before OnClientRemoved().
base::flat_map<WorkerNodeImpl*, int> clients_to_remove_;
#endif // DCHECK_IS_ON()
DISALLOW_COPY_AND_ASSIGN(SharedWorkerWatcher);
};
} // namespace performance_manager
#endif // COMPONENTS_PERFORMANCE_MANAGER_SHARED_WORKER_WATCHER_H_
// Copyright 2019 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 "components/performance_manager/shared_worker_watcher.h"
#include <memory>
#include <utility>
#include <vector>
#include "base/bind_helpers.h"
#include "base/macros.h"
#include "base/observer_list.h"
#include "base/run_loop.h"
#include "base/stl_util.h"
#include "base/strings/stringprintf.h"
#include "base/test/bind_test_util.h"
#include "base/test/task_environment.h"
#include "components/performance_manager/frame_node_source.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/worker_node_impl.h"
#include "components/performance_manager/performance_manager_impl.h"
#include "components/performance_manager/process_node_source.h"
#include "content/public/browser/shared_worker_instance.h"
#include "content/public/browser/shared_worker_service.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace performance_manager {
namespace {
// Generates a new sequential int ID. Used for things that need a unique ID.
int GenerateNextId() {
static int next_id = 0;
return next_id++;
}
// Generates a unique URL for a fake worker node.
GURL GenerateWorkerUrl() {
return GURL(base::StringPrintf("https://www.foo.org/worker_script_%d.js",
GenerateNextId()));
}
// Helper function to check that |worker_node| and |client_frame_node| are
// correctly hooked up together.
bool IsWorkerClient(WorkerNodeImpl* worker_node,
FrameNodeImpl* client_frame_node) {
return base::Contains(worker_node->client_frames(), client_frame_node) &&
base::Contains(client_frame_node->child_worker_nodes(), worker_node);
}
// TestSharedWorkerService -----------------------------------------------------
// A test SharedWorkerService that allows to simulate a worker starting and
// stopping and adding clients to running workers.
class TestSharedWorkerService : public content::SharedWorkerService {
public:
TestSharedWorkerService();
~TestSharedWorkerService() override;
// content::SharedWorkerService
void AddObserver(Observer* observer) override;
void RemoveObserver(Observer* observer) override;
bool TerminateWorker(const GURL& url,
const std::string& name,
const url::Origin& constructor_origin) override;
// Starts a new shared worker and returns its instance.
content::SharedWorkerInstance StartSharedWorker(int worker_process_id);
// Stops a running shared worker.
void StopSharedWorker(const content::SharedWorkerInstance& instance);
// Adds a new frame client to an existing worker.
void AddWorkerClient(const content::SharedWorkerInstance& instance,
int client_process_id,
int frame_id);
// Removes an existing frame client from a worker.
void RemoveWorkerClient(const content::SharedWorkerInstance& instance,
int client_process_id,
int frame_id);
private:
base::ObserverList<Observer> observer_list_;
// Contains the set of clients for each running workers.
base::flat_map<content::SharedWorkerInstance,
base::flat_set<std::pair<int, int>>>
shared_worker_client_frames_;
DISALLOW_COPY_AND_ASSIGN(TestSharedWorkerService);
};
TestSharedWorkerService::TestSharedWorkerService() = default;
TestSharedWorkerService::~TestSharedWorkerService() = default;
void TestSharedWorkerService::AddObserver(Observer* observer) {
observer_list_.AddObserver(observer);
}
void TestSharedWorkerService::RemoveObserver(Observer* observer) {
observer_list_.RemoveObserver(observer);
}
bool TestSharedWorkerService::TerminateWorker(
const GURL& url,
const std::string& name,
const url::Origin& constructor_origin) {
return true;
}
content::SharedWorkerInstance TestSharedWorkerService::StartSharedWorker(
int worker_process_id) {
// Create a new SharedWorkerInstance and add it to the map.
GURL worker_url = GenerateWorkerUrl();
content::SharedWorkerInstance instance(
worker_url, "SharedWorker", url::Origin::Create(worker_url), "",
blink::mojom::ContentSecurityPolicyType::kReport,
network::mojom::IPAddressSpace::kPublic,
blink::mojom::SharedWorkerCreationContextType::kSecure);
bool inserted = shared_worker_client_frames_.insert({instance, {}}).second;
DCHECK(inserted);
// Notify observers.
for (auto& observer : observer_list_) {
observer.OnWorkerStarted(instance, worker_process_id,
base::UnguessableToken::Create());
}
return instance;
}
void TestSharedWorkerService::StopSharedWorker(
const content::SharedWorkerInstance& instance) {
auto it = shared_worker_client_frames_.find(instance);
DCHECK(it != shared_worker_client_frames_.end());
// A stopping worker should have no clients.
DCHECK(it->second.empty());
// Notify observers that the worker is terminating.
for (auto& observer : observer_list_)
observer.OnBeforeWorkerTerminated(instance);
// Remove the worker instance from the map.
shared_worker_client_frames_.erase(it);
}
void TestSharedWorkerService::AddWorkerClient(
const content::SharedWorkerInstance& instance,
int client_process_id,
int frame_id) {
// Add the frame to the set of clients for this worker.
auto it = shared_worker_client_frames_.find(instance);
DCHECK(it != shared_worker_client_frames_.end());
base::flat_set<std::pair<int, int>>& client_frames = it->second;
bool inserted = client_frames.insert({client_process_id, frame_id}).second;
DCHECK(inserted);
// Then notify observers.
for (auto& observer : observer_list_)
observer.OnClientAdded(instance, client_process_id, frame_id);
}
void TestSharedWorkerService::RemoveWorkerClient(
const content::SharedWorkerInstance& instance,
int client_process_id,
int frame_id) {
// Notify observers.
for (auto& observer : observer_list_)
observer.OnClientRemoved(instance, client_process_id, frame_id);
// Then remove the frame from the set of clients of this worker.
auto it = shared_worker_client_frames_.find(instance);
DCHECK(it != shared_worker_client_frames_.end());
base::flat_set<std::pair<int, int>>& client_frames = it->second;
size_t removed =
client_frames.erase(std::make_pair(client_process_id, frame_id));
DCHECK_EQ(removed, 1u);
}
// TestProcessNodeSource -------------------------------------------------------
// A test ProcessNodeSource that allows creating process nodes on demand to
// "host" frames and workers.
class TestProcessNodeSource : public ProcessNodeSource {
public:
TestProcessNodeSource();
~TestProcessNodeSource() override;
// ProcessNodeSource:
ProcessNodeImpl* GetProcessNode(int render_process_id) override;
// Creates a process node and returns its generated render process ID.
int CreateProcessNode();
private:
// Maps render process IDs with their associated process node.
base::flat_map<int, std::unique_ptr<ProcessNodeImpl>> process_node_map_;
DISALLOW_COPY_AND_ASSIGN(TestProcessNodeSource);
};
TestProcessNodeSource::TestProcessNodeSource() = default;
TestProcessNodeSource::~TestProcessNodeSource() {
std::vector<std::unique_ptr<NodeBase>> nodes;
nodes.reserve(process_node_map_.size());
for (auto& kv : process_node_map_) {
std::unique_ptr<ProcessNodeImpl> process_node = std::move(kv.second);
nodes.push_back(std::move(process_node));
}
PerformanceManagerImpl::GetInstance()->BatchDeleteNodes(std::move(nodes));
process_node_map_.clear();
}
ProcessNodeImpl* TestProcessNodeSource::GetProcessNode(int render_process_id) {
auto it = process_node_map_.find(render_process_id);
DCHECK(it != process_node_map_.end());
return it->second.get();
}
int TestProcessNodeSource::CreateProcessNode() {
// Generate a render process ID for this process node.
int render_process_id = GenerateNextId();
// Create the process node and insert it into the map.
auto process_node = PerformanceManagerImpl::GetInstance()->CreateProcessNode(
RenderProcessHostProxy());
bool inserted =
process_node_map_.insert({render_process_id, std::move(process_node)})
.second;
DCHECK(inserted);
return render_process_id;
}
// TestFrameNodeSource ---------------------------------------------------------
class TestFrameNodeSource : public FrameNodeSource {
public:
TestFrameNodeSource();
~TestFrameNodeSource() override;
// FrameNodeSource:
FrameNodeImpl* GetFrameNode(int render_process_id, int frame_id) override;
void SubscribeToFrameNode(int render_process_id,
int frame_id,
OnbeforeFrameNodeRemovedCallback
on_before_frame_node_removed_callback) override;
void UnsubscribeFromFrameNode(int render_process_id, int frame_id) override;
// Creates a frame node and returns its generated frame id.
int CreateFrameNode(int render_process_id, ProcessNodeImpl* process_node);
// Deletes an existing frame node and notify subscribers.
void DeleteFrameNode(int render_process_id, int frame_id);
private:
// Helper function that invokes the OnBeforeFrameNodeRemovedCallback
// associated with |frame_node| and removes it from the map.
void InvokeAndRemoveCallback(FrameNodeImpl* frame_node);
// The page node that hosts all frames.
std::unique_ptr<PageNodeImpl> page_node_;
// Maps each frame's render process id and frame id with their associated
// frame node.
base::flat_map<std::pair<int, int>, std::unique_ptr<FrameNodeImpl>>
frame_node_map_;
// Maps each observed frame node to their callback.
base::flat_map<FrameNodeImpl*, OnbeforeFrameNodeRemovedCallback>
frame_node_callbacks_;
DISALLOW_COPY_AND_ASSIGN(TestFrameNodeSource);
};
TestFrameNodeSource::TestFrameNodeSource()
: page_node_(PerformanceManagerImpl::GetInstance()->CreatePageNode(
WebContentsProxy(),
"page_node_context_id",
GURL(),
false,
false)) {}
TestFrameNodeSource::~TestFrameNodeSource() {
std::vector<std::unique_ptr<NodeBase>> nodes;
nodes.push_back(std::move(page_node_));
nodes.reserve(frame_node_map_.size());
for (auto& kv : frame_node_map_)
nodes.push_back(std::move(kv.second));
PerformanceManagerImpl::GetInstance()->BatchDeleteNodes(std::move(nodes));
frame_node_map_.clear();
}
FrameNodeImpl* TestFrameNodeSource::GetFrameNode(int render_process_id,
int frame_id) {
auto it = frame_node_map_.find(std::make_pair(render_process_id, frame_id));
return it != frame_node_map_.end() ? it->second.get() : nullptr;
}
void TestFrameNodeSource::SubscribeToFrameNode(
int render_process_id,
int frame_id,
OnbeforeFrameNodeRemovedCallback on_before_frame_node_removed_callback) {
FrameNodeImpl* frame_node = GetFrameNode(render_process_id, frame_id);
DCHECK(frame_node);
bool inserted =
frame_node_callbacks_
.insert(std::make_pair(
frame_node, std::move(on_before_frame_node_removed_callback)))
.second;
DCHECK(inserted);
}
void TestFrameNodeSource::UnsubscribeFromFrameNode(int render_process_id,
int frame_id) {
FrameNodeImpl* frame_node = GetFrameNode(render_process_id, frame_id);
DCHECK(frame_node);
size_t removed = frame_node_callbacks_.erase(frame_node);
DCHECK_EQ(removed, 1u);
}
// Creates a frame node and returns its frame id.
int TestFrameNodeSource::CreateFrameNode(int render_process_id,
ProcessNodeImpl* process_node) {
int frame_id = GenerateNextId();
auto frame_node = PerformanceManagerImpl::GetInstance()->CreateFrameNode(
process_node, page_node_.get(), nullptr, 0, frame_id,
base::UnguessableToken::Null(), 0, 0);
std::pair<int, int> frame_info(render_process_id, frame_id);
bool inserted = frame_node_map_
.insert({std::make_pair(render_process_id, frame_id),
std::move(frame_node)})
.second;
DCHECK(inserted);
return frame_id;
}
void TestFrameNodeSource::DeleteFrameNode(int render_process_id, int frame_id) {
auto it = frame_node_map_.find(std::make_pair(render_process_id, frame_id));
DCHECK(it != frame_node_map_.end());
FrameNodeImpl* frame_node = it->second.get();
// Notify the subscriber then delete the node.
InvokeAndRemoveCallback(frame_node);
PerformanceManagerImpl::GetInstance()->DeleteNode(std::move(it->second));
frame_node_map_.erase(it);
}
void TestFrameNodeSource::InvokeAndRemoveCallback(FrameNodeImpl* frame_node) {
auto it = frame_node_callbacks_.find(frame_node);
DCHECK(it != frame_node_callbacks_.end());
std::move(it->second).Run(frame_node);
frame_node_callbacks_.erase(it);
}
} // namespace
class SharedWorkerWatcherTest : public testing::Test {
public:
SharedWorkerWatcherTest();
~SharedWorkerWatcherTest() override;
// testing::Test:
void SetUp() override;
void TearDown() override;
// Wraps a |graph_callback| and ensures the task completes before returning.
void CallOnGraphAndWait(
PerformanceManagerImpl::GraphImplCallback graph_callback);
// Retrieves the worker node associated with |instance|.
WorkerNodeImpl* GetWorkerNode(const content::SharedWorkerInstance& instance);
PerformanceManagerImpl* performance_manager() {
return performance_manager_.get();
}
TestSharedWorkerService* shared_worker_service() {
return &shared_worker_service_;
}
TestProcessNodeSource* process_node_source() {
return process_node_source_.get();
}
TestFrameNodeSource* frame_node_source() { return frame_node_source_.get(); }
private:
base::test::TaskEnvironment task_environment_;
TestSharedWorkerService shared_worker_service_;
std::unique_ptr<PerformanceManagerImpl> performance_manager_;
std::unique_ptr<TestProcessNodeSource> process_node_source_;
std::unique_ptr<TestFrameNodeSource> frame_node_source_;
// The SharedWorkerWatcher that's being tested.
std::unique_ptr<SharedWorkerWatcher> shared_worker_watcher_;
DISALLOW_COPY_AND_ASSIGN(SharedWorkerWatcherTest);
};
SharedWorkerWatcherTest::SharedWorkerWatcherTest() = default;
SharedWorkerWatcherTest::~SharedWorkerWatcherTest() = default;
void SharedWorkerWatcherTest::SetUp() {
performance_manager_ = PerformanceManagerImpl::Create(base::DoNothing());
process_node_source_ = std::make_unique<TestProcessNodeSource>();
frame_node_source_ = std::make_unique<TestFrameNodeSource>();
shared_worker_watcher_ = std::make_unique<SharedWorkerWatcher>(
"browser_context_id", &shared_worker_service_, process_node_source_.get(),
frame_node_source_.get());
}
void SharedWorkerWatcherTest::TearDown() {
// Clean up the performance manager correctly.
shared_worker_watcher_->TearDown();
shared_worker_watcher_ = nullptr;
// Delete the TestFrameNodeSource and the TestProcessNodeSource in
// that order since they own graph nodes.
frame_node_source_ = nullptr;
process_node_source_ = nullptr;
PerformanceManagerImpl::Destroy(std::move(performance_manager_));
}
void SharedWorkerWatcherTest::CallOnGraphAndWait(
PerformanceManagerImpl::GraphImplCallback graph_callback) {
base::RunLoop run_loop;
performance_manager_->CallOnGraphImpl(
FROM_HERE,
base::BindLambdaForTesting(
[graph_callback = std::move(graph_callback),
quit_closure = run_loop.QuitClosure()](GraphImpl* graph) mutable {
std::move(graph_callback).Run(graph);
quit_closure.Run();
}));
}
WorkerNodeImpl* SharedWorkerWatcherTest::GetWorkerNode(
const content::SharedWorkerInstance& instance) {
return shared_worker_watcher_->GetWorkerNode(instance);
}
// This test creates one worker with one client frame.
TEST_F(SharedWorkerWatcherTest, SimpleWorker) {
int render_process_id = process_node_source()->CreateProcessNode();
// Create the frame node.
int frame_id = frame_node_source()->CreateFrameNode(
render_process_id,
process_node_source()->GetProcessNode(render_process_id));
// Create the worker.
content::SharedWorkerInstance shared_worker_instance =
shared_worker_service()->StartSharedWorker(render_process_id);
// Connect the frame to the worker.
shared_worker_service()->AddWorkerClient(shared_worker_instance,
render_process_id, frame_id);
// Check expectations on the graph.
CallOnGraphAndWait(base::BindLambdaForTesting(
[process_node = process_node_source()->GetProcessNode(render_process_id),
worker_node = GetWorkerNode(shared_worker_instance),
client_frame_node = frame_node_source()->GetFrameNode(
render_process_id, frame_id)](GraphImpl* graph) {
EXPECT_TRUE(graph->NodeInGraph(worker_node));
EXPECT_EQ(worker_node->worker_type(), WorkerNode::WorkerType::kShared);
EXPECT_EQ(worker_node->process_node(), process_node);
EXPECT_TRUE(IsWorkerClient(worker_node, client_frame_node));
}));
// Disconnect and clean up the worker.
shared_worker_service()->RemoveWorkerClient(shared_worker_instance,
render_process_id, frame_id);
shared_worker_service()->StopSharedWorker(shared_worker_instance);
}
TEST_F(SharedWorkerWatcherTest, CrossProcess) {
// Create the frame node.
int frame_process_id = process_node_source()->CreateProcessNode();
int frame_id = frame_node_source()->CreateFrameNode(
frame_process_id,
process_node_source()->GetProcessNode(frame_process_id));
// Create the worker in a different process.
int worker_process_id = process_node_source()->CreateProcessNode();
content::SharedWorkerInstance shared_worker_instance =
shared_worker_service()->StartSharedWorker(worker_process_id);
// Connect the frame to the worker.
shared_worker_service()->AddWorkerClient(shared_worker_instance,
frame_process_id, frame_id);
// Check expectations on the graph.
CallOnGraphAndWait(base::BindLambdaForTesting(
[worker_process_node =
process_node_source()->GetProcessNode(worker_process_id),
worker_node = GetWorkerNode(shared_worker_instance),
client_process_node =
process_node_source()->GetProcessNode(frame_process_id),
client_frame_node = frame_node_source()->GetFrameNode(
frame_process_id, frame_id)](GraphImpl* graph) {
EXPECT_TRUE(graph->NodeInGraph(worker_node));
EXPECT_EQ(worker_node->worker_type(), WorkerNode::WorkerType::kShared);
EXPECT_EQ(worker_node->process_node(), worker_process_node);
EXPECT_TRUE(IsWorkerClient(worker_node, client_frame_node));
}));
// Disconnect and clean up the worker.
shared_worker_service()->RemoveWorkerClient(shared_worker_instance,
frame_process_id, frame_id);
shared_worker_service()->StopSharedWorker(shared_worker_instance);
}
TEST_F(SharedWorkerWatcherTest, OneWorkerTwoClients) {
int render_process_id = process_node_source()->CreateProcessNode();
// Create the worker.
content::SharedWorkerInstance shared_worker_instance =
shared_worker_service()->StartSharedWorker(render_process_id);
// Create 2 client frame nodes and connect them to the worker.
int frame_id_1 = frame_node_source()->CreateFrameNode(
render_process_id,
process_node_source()->GetProcessNode(render_process_id));
shared_worker_service()->AddWorkerClient(shared_worker_instance,
render_process_id, frame_id_1);
int frame_id_2 = frame_node_source()->CreateFrameNode(
render_process_id,
process_node_source()->GetProcessNode(render_process_id));
shared_worker_service()->AddWorkerClient(shared_worker_instance,
render_process_id, frame_id_2);
// Check expectations on the graph.
CallOnGraphAndWait(base::BindLambdaForTesting(
[worker_node = GetWorkerNode(shared_worker_instance),
client_frame_node_1 =
frame_node_source()->GetFrameNode(render_process_id, frame_id_1),
client_frame_node_2 = frame_node_source()->GetFrameNode(
render_process_id, frame_id_2)](GraphImpl* graph) {
EXPECT_TRUE(graph->NodeInGraph(worker_node));
EXPECT_EQ(worker_node->worker_type(), WorkerNode::WorkerType::kShared);
// Check frame 1.
EXPECT_TRUE(IsWorkerClient(worker_node, client_frame_node_1));
// Check frame 2.
EXPECT_TRUE(IsWorkerClient(worker_node, client_frame_node_2));
}));
// Disconnect and clean up the worker.
shared_worker_service()->RemoveWorkerClient(shared_worker_instance,
render_process_id, frame_id_1);
shared_worker_service()->RemoveWorkerClient(shared_worker_instance,
render_process_id, frame_id_2);
shared_worker_service()->StopSharedWorker(shared_worker_instance);
}
TEST_F(SharedWorkerWatcherTest, OneClientTwoWorkers) {
int render_process_id = process_node_source()->CreateProcessNode();
// Create the frame node.
int frame_id = frame_node_source()->CreateFrameNode(
render_process_id,
process_node_source()->GetProcessNode(render_process_id));
// Create the 2 workers and connect them to the frame.
content::SharedWorkerInstance shared_worker_instance_1 =
shared_worker_service()->StartSharedWorker(render_process_id);
shared_worker_service()->AddWorkerClient(shared_worker_instance_1,
render_process_id, frame_id);
content::SharedWorkerInstance shared_worker_instance_2 =
shared_worker_service()->StartSharedWorker(render_process_id);
shared_worker_service()->AddWorkerClient(shared_worker_instance_2,
render_process_id, frame_id);
// Check expectations on the graph.
CallOnGraphAndWait(base::BindLambdaForTesting(
[worker_node_1 = GetWorkerNode(shared_worker_instance_1),
worker_node_2 = GetWorkerNode(shared_worker_instance_2),
client_frame_node = frame_node_source()->GetFrameNode(
render_process_id, frame_id)](GraphImpl* graph) {
// Check worker 1.
EXPECT_TRUE(graph->NodeInGraph(worker_node_1));
EXPECT_EQ(worker_node_1->worker_type(),
WorkerNode::WorkerType::kShared);
EXPECT_TRUE(IsWorkerClient(worker_node_1, client_frame_node));
// Check worker 2.
EXPECT_TRUE(graph->NodeInGraph(worker_node_2));
EXPECT_EQ(worker_node_2->worker_type(),
WorkerNode::WorkerType::kShared);
EXPECT_TRUE(IsWorkerClient(worker_node_2, client_frame_node));
}));
// Disconnect and clean up the workers.
shared_worker_service()->RemoveWorkerClient(shared_worker_instance_1,
render_process_id, frame_id);
shared_worker_service()->StopSharedWorker(shared_worker_instance_1);
shared_worker_service()->RemoveWorkerClient(shared_worker_instance_2,
render_process_id, frame_id);
shared_worker_service()->StopSharedWorker(shared_worker_instance_2);
}
TEST_F(SharedWorkerWatcherTest, FrameDestroyed) {
int render_process_id = process_node_source()->CreateProcessNode();
// Create the frame node.
int frame_id = frame_node_source()->CreateFrameNode(
render_process_id,
process_node_source()->GetProcessNode(render_process_id));
// Create the worker.
content::SharedWorkerInstance shared_worker_instance =
shared_worker_service()->StartSharedWorker(render_process_id);
// Connect the frame to the worker.
shared_worker_service()->AddWorkerClient(shared_worker_instance,
render_process_id, frame_id);
// Check that everything is wired up correctly.
CallOnGraphAndWait(base::BindLambdaForTesting(
[worker_node = GetWorkerNode(shared_worker_instance),
client_frame_node = frame_node_source()->GetFrameNode(
render_process_id, frame_id)](GraphImpl* graph) {
EXPECT_TRUE(graph->NodeInGraph(worker_node));
EXPECT_TRUE(IsWorkerClient(worker_node, client_frame_node));
}));
frame_node_source()->DeleteFrameNode(render_process_id, frame_id);
// Check that the worker is no longer connected to the deleted frame.
CallOnGraphAndWait(base::BindLambdaForTesting(
[worker_node = GetWorkerNode(shared_worker_instance)](GraphImpl* graph) {
EXPECT_TRUE(graph->NodeInGraph(worker_node));
EXPECT_TRUE(worker_node->client_frames().empty());
}));
// The watcher is still expecting a worker removed notification.
shared_worker_service()->RemoveWorkerClient(shared_worker_instance,
render_process_id, frame_id);
shared_worker_service()->StopSharedWorker(shared_worker_instance);
}
} // namespace performance_manager
// Copyright 2019 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 "components/performance_manager/tab_helper_frame_node_source.h"
#include <utility>
#include "content/public/browser/render_frame_host.h"
#include "content/public/browser/web_contents.h"
namespace performance_manager {
TabHelperFrameNodeSource::TabHelperFrameNodeSource()
: performance_manager_tab_helper_observers_(this) {}
TabHelperFrameNodeSource::~TabHelperFrameNodeSource() {
DCHECK(observed_frame_nodes_.empty());
DCHECK(!performance_manager_tab_helper_observers_.IsObservingSources());
}
FrameNodeImpl* TabHelperFrameNodeSource::GetFrameNode(int render_process_id,
int frame_id) {
// Retrieve the client's RenderFrameHost and its associated
// PerformanceManagerTabHelper.
auto* render_frame_host =
content::RenderFrameHost::FromID(render_process_id, frame_id);
if (!render_frame_host)
return nullptr;
PerformanceManagerTabHelper* performance_manager_tab_helper =
PerformanceManagerTabHelper::FromWebContents(
content::WebContents::FromRenderFrameHost(render_frame_host));
DCHECK(performance_manager_tab_helper);
return performance_manager_tab_helper->GetFrameNode(render_frame_host);
}
void TabHelperFrameNodeSource::SubscribeToFrameNode(
int render_process_id,
int frame_id,
OnbeforeFrameNodeRemovedCallback on_before_frame_node_removed_callback) {
auto* render_frame_host =
content::RenderFrameHost::FromID(render_process_id, frame_id);
DCHECK(render_frame_host);
PerformanceManagerTabHelper* performance_manager_tab_helper =
PerformanceManagerTabHelper::FromWebContents(
content::WebContents::FromRenderFrameHost(render_frame_host));
DCHECK(performance_manager_tab_helper);
FrameNodeImpl* frame_node =
performance_manager_tab_helper->GetFrameNode(render_frame_host);
// Add the frame to the set of observed frames that belongs to
// |performance_manager_tab_helper|.
if (AddObservedFrameNode(performance_manager_tab_helper, frame_node)) {
// Start observing the tab helper only if this is the first observed frame
// that is associated with it.
performance_manager_tab_helper_observers_.Add(
performance_manager_tab_helper);
}
// Then remember the frame's callback.
bool inserted =
frame_node_callbacks_
.insert(std::make_pair(
frame_node, std::move(on_before_frame_node_removed_callback)))
.second;
DCHECK(inserted);
}
void TabHelperFrameNodeSource::UnsubscribeFromFrameNode(int render_process_id,
int frame_id) {
auto* render_frame_host =
content::RenderFrameHost::FromID(render_process_id, frame_id);
DCHECK(render_frame_host);
PerformanceManagerTabHelper* performance_manager_tab_helper =
PerformanceManagerTabHelper::FromWebContents(
content::WebContents::FromRenderFrameHost(render_frame_host));
DCHECK(performance_manager_tab_helper);
FrameNodeImpl* frame_node =
performance_manager_tab_helper->GetFrameNode(render_frame_host);
// Remove the frame's callback without invoking it.
size_t removed = frame_node_callbacks_.erase(frame_node);
DCHECK_EQ(removed, 1u);
// And also remove the frame from the set of observed frames that belongs to
// |performance_manager_tab_helper|.
if (RemoveObservedFrameNode(performance_manager_tab_helper, frame_node)) {
// Stop observing that tab helper if there no longer are any observed
// frames that are associated with it.
performance_manager_tab_helper_observers_.Remove(
performance_manager_tab_helper);
}
}
void TabHelperFrameNodeSource::OnBeforeFrameNodeRemoved(
PerformanceManagerTabHelper* performance_manager_tab_helper,
FrameNodeImpl* frame_node) {
// The tab helper owns many other frames than the ones this instance cares
// about. Ignore irrelevant notifications.
auto it = frame_node_callbacks_.find(frame_node);
if (it == frame_node_callbacks_.end())
return;
// Invoke the frame's callback and remove it.
std::move(it->second).Run(frame_node);
frame_node_callbacks_.erase(it);
// And also remove the frame from the set of observed frames that belong to
// |performance_manager_tab_helper|.
if (RemoveObservedFrameNode(performance_manager_tab_helper, frame_node)) {
// Stop observing that tab helper if there no longer are any observed
// frames that are associated with it.
performance_manager_tab_helper_observers_.Remove(
performance_manager_tab_helper);
}
}
bool TabHelperFrameNodeSource::AddObservedFrameNode(
PerformanceManagerTabHelper* performance_manager_tab_helper,
FrameNodeImpl* frame_node) {
auto insertion_result =
observed_frame_nodes_.insert({performance_manager_tab_helper, {}});
base::flat_set<FrameNodeImpl*>& frame_nodes = insertion_result.first->second;
bool inserted = frame_nodes.insert(frame_node).second;
DCHECK(inserted);
return insertion_result.second;
}
bool TabHelperFrameNodeSource::RemoveObservedFrameNode(
PerformanceManagerTabHelper* performance_manager_tab_helper,
FrameNodeImpl* frame_node) {
auto it = observed_frame_nodes_.find(performance_manager_tab_helper);
DCHECK(it != observed_frame_nodes_.end());
base::flat_set<FrameNodeImpl*>& frame_nodes = it->second;
size_t removed = frame_nodes.erase(frame_node);
DCHECK_EQ(removed, 1u);
if (frame_nodes.empty()) {
observed_frame_nodes_.erase(it);
return true;
}
return false;
}
} // namespace performance_manager
// Copyright 2019 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.
#ifndef COMPONENTS_PERFORMANCE_MANAGER_TAB_HELPER_FRAME_NODE_SOURCE_H_
#define COMPONENTS_PERFORMANCE_MANAGER_TAB_HELPER_FRAME_NODE_SOURCE_H_
#include "components/performance_manager/frame_node_source.h"
#include "base/macros.h"
#include "base/observer_list.h"
#include "base/observer_list_types.h"
#include "base/scoped_observer.h"
#include "components/performance_manager/performance_manager_tab_helper.h"
namespace performance_manager {
// An implementation of FrameNodeSource that uses PerformanceManagerTabHelper to
// get frame node information.
class TabHelperFrameNodeSource : public FrameNodeSource,
public PerformanceManagerTabHelper::Observer {
public:
TabHelperFrameNodeSource();
~TabHelperFrameNodeSource() override;
// FrameNodeSource:
FrameNodeImpl* GetFrameNode(int render_process_id, int frame_id) override;
void SubscribeToFrameNode(int render_process_id,
int frame_id,
OnbeforeFrameNodeRemovedCallback
on_before_frame_node_removed_callback) override;
void UnsubscribeFromFrameNode(int render_process_id, int frame_id) override;
// PerformanceManagerTabHelper::Observer:
void OnBeforeFrameNodeRemoved(
PerformanceManagerTabHelper* performance_manager_tab_helper,
FrameNodeImpl* frame_node) override;
private:
// Adds |frame_node| to the set of observed frame nodes associated with
// |performance_manager_tab_helper|. Returns true if |frame_node| was the
// first frame added to that set.
bool AddObservedFrameNode(
PerformanceManagerTabHelper* performance_manager_tab_helper,
FrameNodeImpl* frame_node);
// Removes |frame_node| to the set of observed frame nodes associated with
// |performance_manager_tab_helper|. Returns true if |frame_node| was the
// last frame removed from that set.
bool RemoveObservedFrameNode(
PerformanceManagerTabHelper* performance_manager_tab_helper,
FrameNodeImpl* frame_node);
// Maps each observed frame node to their callback.
base::flat_map<FrameNodeImpl*, OnbeforeFrameNodeRemovedCallback>
frame_node_callbacks_;
// Maps each tab helper to the set of observed frame nodes that belongs to
// that tab helper.
base::flat_map<PerformanceManagerTabHelper*, base::flat_set<FrameNodeImpl*>>
observed_frame_nodes_;
// Observes frame node deletions.
ScopedObserver<PerformanceManagerTabHelper,
PerformanceManagerTabHelper::Observer>
performance_manager_tab_helper_observers_;
DISALLOW_COPY_AND_ASSIGN(TabHelperFrameNodeSource);
};
} // namespace performance_manager
#endif // COMPONENTS_PERFORMANCE_MANAGER_TAB_HELPER_FRAME_NODE_SOURCE_H_
...@@ -12,9 +12,13 @@ ...@@ -12,9 +12,13 @@
class GURL; class GURL;
namespace base {
class UnguessableToken;
}
namespace url { namespace url {
class Origin; class Origin;
} // namespace url }
namespace content { namespace content {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment