blob: d105ffee524bb62aff62e59390cb883a1389c710 [file] [log] [blame]
Patrick Monette6c6de3882019-10-09 02:59:321// Copyright 2019 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
Patrick Monette9a905082020-01-07 18:37:355#include "components/performance_manager/worker_watcher.h"
Patrick Monette6c6de3882019-10-09 02:59:326
7#include <utility>
8#include <vector>
9
10#include "components/performance_manager/frame_node_source.h"
11#include "components/performance_manager/graph/frame_node_impl.h"
12#include "components/performance_manager/graph/worker_node_impl.h"
13#include "components/performance_manager/performance_manager_impl.h"
14#include "components/performance_manager/process_node_source.h"
15#include "content/public/browser/shared_worker_instance.h"
16
17namespace performance_manager {
18
19namespace {
20
21// Helper function to add |worker_node| as a child to |frame_node| on the PM
22// sequence.
23void AddWorkerToFrameNode(FrameNodeImpl* frame_node,
24 WorkerNodeImpl* worker_node,
25 GraphImpl* graph) {
26 worker_node->AddClientFrame(frame_node);
27}
28
29// Helper function to remove |worker_node| from |frame_node| on the PM sequence.
30void RemoveWorkerFromFrameNode(FrameNodeImpl* frame_node,
31 WorkerNodeImpl* worker_node,
32 GraphImpl* graph) {
33 worker_node->RemoveClientFrame(frame_node);
34}
35
36// Helper function to remove all |worker_nodes| from |frame_node| on the PM
37// sequence.
38void RemoveWorkersFromFrameNode(
39 FrameNodeImpl* frame_node,
40 const base::flat_set<WorkerNodeImpl*>& worker_nodes,
41 GraphImpl* graph) {
42 for (auto* worker_node : worker_nodes)
43 worker_node->RemoveClientFrame(frame_node);
44}
45
46} // namespace
47
Patrick Monette9a905082020-01-07 18:37:3548WorkerWatcher::WorkerWatcher(
Patrick Monette6c6de3882019-10-09 02:59:3249 const std::string& browser_context_id,
Patrick Monette3117283f2020-02-07 15:49:1550 content::DedicatedWorkerService* dedicated_worker_service,
Patrick Monette6c6de3882019-10-09 02:59:3251 content::SharedWorkerService* shared_worker_service,
52 ProcessNodeSource* process_node_source,
53 FrameNodeSource* frame_node_source)
54 : browser_context_id_(browser_context_id),
Patrick Monette3117283f2020-02-07 15:49:1555 dedicated_worker_service_observer_(this),
Patrick Monette6c6de3882019-10-09 02:59:3256 shared_worker_service_observer_(this),
57 process_node_source_(process_node_source),
58 frame_node_source_(frame_node_source) {
Patrick Monette3117283f2020-02-07 15:49:1559 DCHECK(dedicated_worker_service);
Patrick Monette6c6de3882019-10-09 02:59:3260 DCHECK(shared_worker_service);
61 DCHECK(process_node_source_);
62 DCHECK(frame_node_source_);
Patrick Monette3117283f2020-02-07 15:49:1563 dedicated_worker_service_observer_.Add(dedicated_worker_service);
Patrick Monette6c6de3882019-10-09 02:59:3264 shared_worker_service_observer_.Add(shared_worker_service);
65}
66
Patrick Monette9a905082020-01-07 18:37:3567WorkerWatcher::~WorkerWatcher() {
Patrick Monette6c6de3882019-10-09 02:59:3268 DCHECK(frame_node_child_workers_.empty());
Patrick Monette3117283f2020-02-07 15:49:1569 DCHECK(dedicated_worker_nodes_.empty());
70 DCHECK(!dedicated_worker_service_observer_.IsObservingSources());
Patrick Monette9a905082020-01-07 18:37:3571 DCHECK(shared_worker_nodes_.empty());
Patrick Monette6c6de3882019-10-09 02:59:3272 DCHECK(!shared_worker_service_observer_.IsObservingSources());
73}
74
Patrick Monette9a905082020-01-07 18:37:3575void WorkerWatcher::TearDown() {
Patrick Monette6c6de3882019-10-09 02:59:3276 // First clear client-child relations between frames and workers.
77 for (auto& kv : frame_node_child_workers_) {
Patrick Monette7364e6972020-01-09 22:56:0278 const content::GlobalFrameRoutingId& render_frame_host_id = kv.first;
Patrick Monette6c6de3882019-10-09 02:59:3279 base::flat_set<WorkerNodeImpl*>& child_workers = kv.second;
80
Patrick Monette7364e6972020-01-09 22:56:0281 frame_node_source_->UnsubscribeFromFrameNode(render_frame_host_id);
Patrick Monette6c6de3882019-10-09 02:59:3282
83 // Disconnect all child workers from |frame_node|.
Patrick Monette7364e6972020-01-09 22:56:0284 FrameNodeImpl* frame_node =
85 frame_node_source_->GetFrameNode(render_frame_host_id);
Patrick Monette6c6de3882019-10-09 02:59:3286 DCHECK(frame_node);
87 DCHECK(!child_workers.empty());
88 PerformanceManagerImpl::CallOnGraphImpl(
89 FROM_HERE,
90 base::BindOnce(&RemoveWorkersFromFrameNode, frame_node, child_workers));
91 }
92 frame_node_child_workers_.clear();
93
94 // Then clean all the worker nodes.
95 std::vector<std::unique_ptr<NodeBase>> nodes;
Patrick Monette3117283f2020-02-07 15:49:1596 nodes.reserve(dedicated_worker_nodes_.size() + shared_worker_nodes_.size());
97 for (auto& node : dedicated_worker_nodes_)
98 nodes.push_back(std::move(node.second));
99 dedicated_worker_nodes_.clear();
Patrick Monette9a905082020-01-07 18:37:35100 for (auto& node : shared_worker_nodes_)
Patrick Monette6c6de3882019-10-09 02:59:32101 nodes.push_back(std::move(node.second));
Patrick Monette9a905082020-01-07 18:37:35102 shared_worker_nodes_.clear();
Patrick Monette6c6de3882019-10-09 02:59:32103
104 PerformanceManagerImpl::GetInstance()->BatchDeleteNodes(std::move(nodes));
105
Patrick Monette3117283f2020-02-07 15:49:15106 dedicated_worker_service_observer_.RemoveAll();
Patrick Monette6c6de3882019-10-09 02:59:32107 shared_worker_service_observer_.RemoveAll();
108}
109
Patrick Monette9a905082020-01-07 18:37:35110void WorkerWatcher::OnWorkerStarted(
Patrick Monette3117283f2020-02-07 15:49:15111 content::DedicatedWorkerId dedicated_worker_id,
112 int worker_process_id,
113 content::GlobalFrameRoutingId ancestor_render_frame_host_id) {
114 // TODO(https://crbug.com/993029): Plumb through the URL and the DevTools
115 // token.
116 auto worker_node = PerformanceManagerImpl::GetInstance()->CreateWorkerNode(
117 browser_context_id_, WorkerNode::WorkerType::kDedicated,
118 process_node_source_->GetProcessNode(worker_process_id),
119 base::UnguessableToken::Create());
120 bool inserted = dedicated_worker_nodes_
121 .emplace(dedicated_worker_id, std::move(worker_node))
122 .second;
123 DCHECK(inserted);
124
125 // TODO(pmonette): Connect |worker_node| to its client frame.
126}
127
128void WorkerWatcher::OnBeforeWorkerTerminated(
129 content::DedicatedWorkerId dedicated_worker_id,
130 content::GlobalFrameRoutingId ancestor_render_frame_host_id) {
131 auto it = dedicated_worker_nodes_.find(dedicated_worker_id);
132 DCHECK(it != dedicated_worker_nodes_.end());
133
134 auto worker_node = std::move(it->second);
135 // TODO(pmonette): Disconnect |worker_node| from its client frame.
136#if DCHECK_IS_ON()
137 DCHECK(!base::Contains(clients_to_remove_, worker_node.get()));
138#endif // DCHECK_IS_ON()
139 PerformanceManagerImpl::GetInstance()->DeleteNode(std::move(worker_node));
140
141 dedicated_worker_nodes_.erase(it);
142}
143
144void WorkerWatcher::OnWorkerStarted(
Patrick Monette6c6de3882019-10-09 02:59:32145 const content::SharedWorkerInstance& instance,
146 int worker_process_id,
147 const base::UnguessableToken& dev_tools_token) {
148 auto worker_node = PerformanceManagerImpl::GetInstance()->CreateWorkerNode(
149 browser_context_id_, WorkerNode::WorkerType::kShared,
Patrick Monetteeb6c1eee2020-02-04 20:42:48150 process_node_source_->GetProcessNode(worker_process_id), dev_tools_token);
Patrick Monette6c6de3882019-10-09 02:59:32151 bool inserted =
Patrick Monette9a905082020-01-07 18:37:35152 shared_worker_nodes_.emplace(instance, std::move(worker_node)).second;
Patrick Monette6c6de3882019-10-09 02:59:32153 DCHECK(inserted);
154}
155
Patrick Monette9a905082020-01-07 18:37:35156void WorkerWatcher::OnBeforeWorkerTerminated(
Patrick Monette6c6de3882019-10-09 02:59:32157 const content::SharedWorkerInstance& instance) {
Patrick Monette9a905082020-01-07 18:37:35158 auto it = shared_worker_nodes_.find(instance);
159 DCHECK(it != shared_worker_nodes_.end());
Patrick Monette6c6de3882019-10-09 02:59:32160
161 auto worker_node = std::move(it->second);
162#if DCHECK_IS_ON()
163 DCHECK(!base::Contains(clients_to_remove_, worker_node.get()));
164#endif // DCHECK_IS_ON()
165 PerformanceManagerImpl::GetInstance()->DeleteNode(std::move(worker_node));
166
Patrick Monette9a905082020-01-07 18:37:35167 shared_worker_nodes_.erase(it);
Patrick Monette6c6de3882019-10-09 02:59:32168}
169
Patrick Monette7364e6972020-01-09 22:56:02170void WorkerWatcher::OnClientAdded(
171 const content::SharedWorkerInstance& instance,
172 content::GlobalFrameRoutingId render_frame_host_id) {
Patrick Monette3117283f2020-02-07 15:49:15173 AddClientFrame(GetSharedWorkerNode(instance), render_frame_host_id);
Patrick Monette897d08b2020-02-04 14:24:00174}
175
Marc Treib70854a72020-02-04 17:19:52176void WorkerWatcher::OnClientRemoved(
177 const content::SharedWorkerInstance& instance,
178 content::GlobalFrameRoutingId render_frame_host_id) {
Patrick Monette3117283f2020-02-07 15:49:15179 RemoveClientFrame(GetSharedWorkerNode(instance), render_frame_host_id);
180}
181
182void WorkerWatcher::AddClientFrame(
183 WorkerNodeImpl* worker_node,
184 content::GlobalFrameRoutingId client_render_frame_host_id) {
Patrick Monette897d08b2020-02-04 14:24:00185 FrameNodeImpl* frame_node =
Patrick Monette3117283f2020-02-07 15:49:15186 frame_node_source_->GetFrameNode(client_render_frame_host_id);
187 DCHECK(frame_node);
188
189 // Connect the nodes in the PM graph.
190 PerformanceManagerImpl::CallOnGraphImpl(
191 FROM_HERE,
192 base::BindOnce(&AddWorkerToFrameNode, frame_node, worker_node));
193
194 // Keep track of the shared workers that this frame is a client to.
195 if (AddChildWorker(client_render_frame_host_id, worker_node)) {
196 frame_node_source_->SubscribeToFrameNode(
197 client_render_frame_host_id,
198 base::BindOnce(&WorkerWatcher::OnBeforeFrameNodeRemoved,
199 base::Unretained(this), client_render_frame_host_id));
200 }
201}
202
203void WorkerWatcher::RemoveClientFrame(
204 WorkerNodeImpl* worker_node,
205 content::GlobalFrameRoutingId client_render_frame_host_id) {
206 FrameNodeImpl* frame_node =
207 frame_node_source_->GetFrameNode(client_render_frame_host_id);
Patrick Monette6c6de3882019-10-09 02:59:32208
209 // It's possible that the frame was destroyed before receiving the
210 // OnClientRemoved() for all of its child shared worker. Nothing to do in
211 // that case because OnBeforeFrameNodeRemoved() took care of removing this
212 // client from its child worker nodes.
213 if (!frame_node) {
214#if DCHECK_IS_ON()
215 // These debug only checks ensure that this code path is only taken if
216 // OnBeforeFrameNodeRemoved() was already called for that frame.
217 auto it = clients_to_remove_.find(worker_node);
218 DCHECK(it != clients_to_remove_.end());
219
220 int& count = it->second;
221 DCHECK_GT(count, 0);
222 --count;
223
224 if (count == 0)
225 clients_to_remove_.erase(it);
226#endif // DCHECK_IS_ON()
227 return;
228 }
229
230 // Disconnect the node.
231 PerformanceManagerImpl::CallOnGraphImpl(
232 FROM_HERE,
233 base::BindOnce(&RemoveWorkerFromFrameNode, frame_node, worker_node));
234
235 // Remove |worker_node| from the set of workers that this frame is a client
236 // of.
Patrick Monette3117283f2020-02-07 15:49:15237 if (RemoveChildWorker(client_render_frame_host_id, worker_node))
238 frame_node_source_->UnsubscribeFromFrameNode(client_render_frame_host_id);
Patrick Monette6c6de3882019-10-09 02:59:32239}
240
Patrick Monette7364e6972020-01-09 22:56:02241void WorkerWatcher::OnBeforeFrameNodeRemoved(
242 content::GlobalFrameRoutingId render_frame_host_id,
243 FrameNodeImpl* frame_node) {
244 auto it = frame_node_child_workers_.find(render_frame_host_id);
Patrick Monette6c6de3882019-10-09 02:59:32245 DCHECK(it != frame_node_child_workers_.end());
246
247 // Clean up all child workers of this frame node.
248 base::flat_set<WorkerNodeImpl*> child_workers = std::move(it->second);
249 frame_node_child_workers_.erase(it);
250
251 // Disconnect all child workers from |frame_node|.
252 DCHECK(!child_workers.empty());
253 PerformanceManagerImpl::CallOnGraphImpl(
254 FROM_HERE,
255 base::BindOnce(&RemoveWorkersFromFrameNode, frame_node, child_workers));
256
257#if DCHECK_IS_ON()
258 for (WorkerNodeImpl* worker_node : child_workers) {
259 // Now expect that this frame will be removed as a client for each worker
260 // in |child_workers|.
261 // Note: the [] operator is intentionally used to default initialize the
262 // count to zero if needed.
263 clients_to_remove_[worker_node]++;
264 }
265#endif // DCHECK_IS_ON()
266}
267
Patrick Monette7364e6972020-01-09 22:56:02268bool WorkerWatcher::AddChildWorker(
269 content::GlobalFrameRoutingId render_frame_host_id,
270 WorkerNodeImpl* child_worker_node) {
Patrick Monette6c6de3882019-10-09 02:59:32271 auto insertion_result =
Patrick Monette7364e6972020-01-09 22:56:02272 frame_node_child_workers_.insert({render_frame_host_id, {}});
Patrick Monette6c6de3882019-10-09 02:59:32273
274 auto& child_workers = insertion_result.first->second;
275 bool inserted = child_workers.insert(child_worker_node).second;
276 DCHECK(inserted);
277
278 return insertion_result.second;
279}
280
Patrick Monette7364e6972020-01-09 22:56:02281bool WorkerWatcher::RemoveChildWorker(
282 content::GlobalFrameRoutingId render_frame_host_id,
283 WorkerNodeImpl* child_worker_node) {
284 auto it = frame_node_child_workers_.find(render_frame_host_id);
Patrick Monette6c6de3882019-10-09 02:59:32285 DCHECK(it != frame_node_child_workers_.end());
286 auto& child_workers = it->second;
287
288 size_t removed = child_workers.erase(child_worker_node);
289 DCHECK_EQ(removed, 1u);
290
291 if (child_workers.empty()) {
292 frame_node_child_workers_.erase(it);
293 return true;
294 }
295 return false;
296}
297
Patrick Monette3117283f2020-02-07 15:49:15298WorkerNodeImpl* WorkerWatcher::GetDedicatedWorkerNode(
299 content::DedicatedWorkerId dedicated_worker_id) {
300 auto it = dedicated_worker_nodes_.find(dedicated_worker_id);
301 if (it == dedicated_worker_nodes_.end()) {
302 NOTREACHED();
303 return nullptr;
304 }
305 return it->second.get();
306}
307
Patrick Monette9a905082020-01-07 18:37:35308WorkerNodeImpl* WorkerWatcher::GetSharedWorkerNode(
Patrick Monette6c6de3882019-10-09 02:59:32309 const content::SharedWorkerInstance& instance) {
Patrick Monette9a905082020-01-07 18:37:35310 auto it = shared_worker_nodes_.find(instance);
311 if (it == shared_worker_nodes_.end()) {
Patrick Monette6c6de3882019-10-09 02:59:32312 NOTREACHED();
313 return nullptr;
314 }
315 return it->second.get();
316}
317
Patrick Monette6c6de3882019-10-09 02:59:32318} // namespace performance_manager