Reland "PM: Elminate CuID in favor of Node* in private IPCs to graph nodes."
This is a reland of f5b288f95873153befd9410d0fb9d73561084771
Original change's description:
> PM: Elminate CuID in favor of Node* in private IPCs to graph nodes.
>
> Separate node teardown from destruction by making BeforeDestroyed
> virtual and overriding it in concrete node classes.
> Tighten up the contracts and enforce a stricter data invariant.
> Fix tests and test fixtures to destroy frames before pages.
> Add a SequenceChecker to the graph.
>
> Bug: 910288
> Change-Id: I2e6bd857462b8576212001df8128f273ba4f4d74
> Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1532342
> Commit-Queue: Sigurður Ásgeirsson <siggi@chromium.org>
> Reviewed-by: Chris Hamilton <chrisha@chromium.org>
> Cr-Commit-Position: refs/heads/master@{#642613}
Bug: 910288
Change-Id: Ib2f465d2f5e743a7525867bb2e4d78bfe6a19fef
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1531034
Commit-Queue: Chris Hamilton <chrisha@chromium.org>
Reviewed-by: Chris Hamilton <chrisha@chromium.org>
Cr-Commit-Position: refs/heads/master@{#642949}
diff --git a/chrome/browser/performance_manager/graph/frame_node_impl.cc b/chrome/browser/performance_manager/graph/frame_node_impl.cc
index 90c7eec..9822d54 100644
--- a/chrome/browser/performance_manager/graph/frame_node_impl.cc
+++ b/chrome/browser/performance_manager/graph/frame_node_impl.cc
@@ -26,54 +26,38 @@
FrameNodeImpl::~FrameNodeImpl() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- if (parent_frame_node_)
- parent_frame_node_->RemoveChildFrame(this);
- if (page_node_)
- page_node_->RemoveFrameImpl(this);
- if (process_node_)
- process_node_->RemoveFrame(this);
- for (auto* child_frame : child_frame_nodes_)
- child_frame->RemoveParentFrame(this);
}
-void FrameNodeImpl::SetProcess(
- const resource_coordinator::CoordinationUnitID& cu_id) {
+void FrameNodeImpl::SetProcess(ProcessNodeImpl* process_node) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- ProcessNodeImpl* process_node = ProcessNodeImpl::GetNodeByID(graph_, cu_id);
- if (!process_node)
- return;
+ DCHECK(NodeInGraph(process_node));
DCHECK(!process_node_);
process_node_ = process_node;
process_node->AddFrame(this);
}
-void FrameNodeImpl::AddChildFrame(
- const resource_coordinator::CoordinationUnitID& cu_id) {
+void FrameNodeImpl::AddChildFrame(FrameNodeImpl* child_frame_node) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- DCHECK(cu_id != id());
- FrameNodeImpl* frame_node = FrameNodeImpl::GetNodeByID(graph_, cu_id);
- if (!frame_node)
- return;
- if (HasFrameNodeInAncestors(frame_node) ||
- frame_node->HasFrameNodeInDescendants(this)) {
- DCHECK(false) << "Cyclic reference in frame coordination units detected!";
- return;
- }
- if (AddChildFrameImpl(frame_node)) {
- frame_node->AddParentFrame(this);
- }
+ DCHECK(child_frame_node);
+ DCHECK_NE(this, child_frame_node);
+ DCHECK(NodeInGraph(child_frame_node));
+ DCHECK(!HasFrameNodeInAncestors(child_frame_node) &&
+ !child_frame_node->HasFrameNodeInDescendants(this));
+
+ bool inserted = child_frame_nodes_.insert(child_frame_node).second;
+ DCHECK(inserted);
+ child_frame_node->AddParentFrame(this);
}
-void FrameNodeImpl::RemoveChildFrame(
- const resource_coordinator::CoordinationUnitID& cu_id) {
+void FrameNodeImpl::RemoveChildFrame(FrameNodeImpl* child_frame_node) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- DCHECK(cu_id != id());
- FrameNodeImpl* frame_node = FrameNodeImpl::GetNodeByID(graph_, cu_id);
- if (!frame_node)
- return;
- if (RemoveChildFrame(frame_node)) {
- frame_node->RemoveParentFrame(this);
- }
+ DCHECK(child_frame_node);
+ DCHECK_NE(this, child_frame_node);
+ DCHECK(NodeInGraph(child_frame_node));
+
+ size_t removed = child_frame_nodes_.erase(child_frame_node);
+ DCHECK_EQ(1u, removed);
+ child_frame_node->RemoveParentFrame(this);
}
void FrameNodeImpl::SetNetworkAlmostIdle(bool network_almost_idle) {
@@ -193,6 +177,20 @@
}
}
+void FrameNodeImpl::BeforeDestroyed() {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+ NodeBase::BeforeDestroyed();
+
+ if (parent_frame_node_)
+ parent_frame_node_->RemoveChildFrame(this);
+ if (page_node_)
+ page_node_->RemoveFrame(this);
+ if (process_node_)
+ process_node_->RemoveFrame(this);
+ for (auto* child_frame : child_frame_nodes_)
+ child_frame->RemoveParentFrame(this);
+}
+
void FrameNodeImpl::OnEventReceived(resource_coordinator::mojom::Event event) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
for (auto& observer : observers())
@@ -229,27 +227,16 @@
void FrameNodeImpl::AddParentFrame(FrameNodeImpl* parent_frame_node) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+ DCHECK_EQ(nullptr, parent_frame_node_);
parent_frame_node_ = parent_frame_node;
}
-bool FrameNodeImpl::AddChildFrameImpl(FrameNodeImpl* child_frame_node) {
- DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- return child_frame_nodes_.count(child_frame_node)
- ? false
- : child_frame_nodes_.insert(child_frame_node).second;
-}
-
void FrameNodeImpl::RemoveParentFrame(FrameNodeImpl* parent_frame_node) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK(parent_frame_node_ == parent_frame_node);
parent_frame_node_ = nullptr;
}
-bool FrameNodeImpl::RemoveChildFrame(FrameNodeImpl* child_frame_node) {
- DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- return child_frame_nodes_.erase(child_frame_node) > 0;
-}
-
void FrameNodeImpl::AddPageNode(PageNodeImpl* page_node) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK(!page_node_);
diff --git a/chrome/browser/performance_manager/graph/frame_node_impl.h b/chrome/browser/performance_manager/graph/frame_node_impl.h
index 9fd8b90..48182c7 100644
--- a/chrome/browser/performance_manager/graph/frame_node_impl.h
+++ b/chrome/browser/performance_manager/graph/frame_node_impl.h
@@ -43,9 +43,9 @@
resource_coordinator::mojom::InterventionPolicy policy) override;
void OnNonPersistentNotificationCreated() override;
- void SetProcess(const resource_coordinator::CoordinationUnitID& cu_id);
- void AddChildFrame(const resource_coordinator::CoordinationUnitID& cu_id);
- void RemoveChildFrame(const resource_coordinator::CoordinationUnitID& cu_id);
+ void SetProcess(ProcessNodeImpl* process_node);
+ void AddChildFrame(FrameNodeImpl* frame_node);
+ void RemoveChildFrame(FrameNodeImpl* frame_node);
FrameNodeImpl* GetParentFrameNode() const;
PageNodeImpl* GetPageNode() const;
@@ -74,6 +74,8 @@
friend class PageNodeImpl;
friend class ProcessNodeImpl;
+ void BeforeDestroyed() override;
+
// CoordinationUnitInterface implementation.
void OnEventReceived(resource_coordinator::mojom::Event event) override;
void OnPropertyChanged(
@@ -89,7 +91,6 @@
void AddParentFrame(FrameNodeImpl* parent_frame_node);
bool AddChildFrameImpl(FrameNodeImpl* child_frame_node);
void RemoveParentFrame(FrameNodeImpl* parent_frame_node);
- bool RemoveChildFrame(FrameNodeImpl* child_frame_node);
void AddPageNode(PageNodeImpl* page_node);
void AddProcessNode(ProcessNodeImpl* process_node);
void RemovePageNode(PageNodeImpl* page_node);
diff --git a/chrome/browser/performance_manager/graph/frame_node_impl_unittest.cc b/chrome/browser/performance_manager/graph/frame_node_impl_unittest.cc
index 9eecff3..6ebab27 100644
--- a/chrome/browser/performance_manager/graph/frame_node_impl_unittest.cc
+++ b/chrome/browser/performance_manager/graph/frame_node_impl_unittest.cc
@@ -34,8 +34,6 @@
base::SimpleTestTickClock clock_;
};
-using FrameNodeImplDeathTest = FrameNodeImplTest;
-
} // namespace
TEST_F(FrameNodeImplTest, AddChildFrameBasic) {
@@ -43,41 +41,14 @@
auto frame2_node = CreateNode<FrameNodeImpl>();
auto frame3_node = CreateNode<FrameNodeImpl>();
- frame1_node->AddChildFrame(frame2_node->id());
- frame1_node->AddChildFrame(frame3_node->id());
+ frame1_node->AddChildFrame(frame2_node.get());
+ frame1_node->AddChildFrame(frame3_node.get());
EXPECT_EQ(nullptr, frame1_node->GetParentFrameNode());
EXPECT_EQ(2u, frame1_node->child_frame_nodes_for_testing().size());
EXPECT_EQ(frame1_node.get(), frame2_node->GetParentFrameNode());
EXPECT_EQ(frame1_node.get(), frame3_node->GetParentFrameNode());
}
-TEST_F(FrameNodeImplDeathTest, AddChildFrameOnCyclicReference) {
- ::testing::FLAGS_gtest_death_test_style = "threadsafe";
-
- auto frame1_node = CreateNode<FrameNodeImpl>();
- auto frame2_node = CreateNode<FrameNodeImpl>();
- auto frame3_node = CreateNode<FrameNodeImpl>();
-
- frame1_node->AddChildFrame(frame2_node->id());
- frame2_node->AddChildFrame(frame3_node->id());
-// |frame3_node| can't add |frame1_node| because |frame1_node| is an ancestor of
-// |frame3_node|, and this will hit a DCHECK because of cyclic reference.
-#if !defined(NDEBUG) || defined(DCHECK_ALWAYS_ON)
- EXPECT_DEATH_IF_SUPPORTED(frame3_node->AddChildFrame(frame1_node->id()), "");
-#else
- frame3_node->AddChildFrame(frame1_node->id());
-#endif // !defined(NDEBUG) || defined(DCHECK_ALWAYS_ON)
-
- EXPECT_EQ(1u, frame1_node->child_frame_nodes_for_testing().count(
- frame2_node.get()));
- EXPECT_EQ(1u, frame2_node->child_frame_nodes_for_testing().count(
- frame3_node.get()));
- // |frame1_node| was not added successfully because |frame1_node| is one of
- // the ancestors of |frame3_node|.
- EXPECT_EQ(0u, frame3_node->child_frame_nodes_for_testing().count(
- frame1_node.get()));
-}
-
TEST_F(FrameNodeImplTest, RemoveChildFrame) {
auto parent_frame_node = CreateNode<FrameNodeImpl>();
auto child_frame_node = CreateNode<FrameNodeImpl>();
@@ -88,7 +59,7 @@
EXPECT_EQ(0u, child_frame_node->child_frame_nodes_for_testing().size());
EXPECT_TRUE(!child_frame_node->GetParentFrameNode());
- parent_frame_node->AddChildFrame(child_frame_node->id());
+ parent_frame_node->AddChildFrame(child_frame_node.get());
// Ensure correct Parent-child relationships have been established.
EXPECT_EQ(1u, parent_frame_node->child_frame_nodes_for_testing().size());
@@ -96,7 +67,7 @@
EXPECT_EQ(0u, child_frame_node->child_frame_nodes_for_testing().size());
EXPECT_EQ(parent_frame_node.get(), child_frame_node->GetParentFrameNode());
- parent_frame_node->RemoveChildFrame(child_frame_node->id());
+ parent_frame_node->RemoveChildFrame(child_frame_node.get());
// Parent-child relationships should no longer exist.
EXPECT_EQ(0u, parent_frame_node->child_frame_nodes_for_testing().size());
diff --git a/chrome/browser/performance_manager/graph/graph.cc b/chrome/browser/performance_manager/graph/graph.cc
index dc7efa5..7a9a8879 100644
--- a/chrome/browser/performance_manager/graph/graph.cc
+++ b/chrome/browser/performance_manager/graph/graph.cc
@@ -23,9 +23,13 @@
namespace performance_manager {
-Graph::Graph() = default;
+Graph::Graph() {
+ DETACH_FROM_SEQUENCE(sequence_checker_);
+}
Graph::~Graph() {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
// Because the graph has ownership of the CUs, and because the process CUs
// unregister on destruction, there is reentrancy to this class on
// destruction. The order of operations here is optimized to minimize the work
@@ -42,11 +46,13 @@
}
void Graph::RegisterObserver(std::unique_ptr<GraphObserver> observer) {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
observer->set_node_graph(this);
observers_.push_back(std::move(observer));
}
void Graph::OnNodeAdded(NodeBase* node) {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
for (auto& observer : observers_) {
if (observer->ShouldObserve(node)) {
node->AddObserver(observer.get());
@@ -56,10 +62,12 @@
}
void Graph::OnBeforeNodeRemoved(NodeBase* node) {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
node->BeforeDestroyed();
}
SystemNodeImpl* Graph::FindOrCreateSystemNode() {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (!system_node_) {
// Create the singleton SystemCU instance. Ownership is taken by the graph.
resource_coordinator::CoordinationUnitID id(
@@ -74,6 +82,7 @@
NodeBase* Graph::GetNodeByID(
const resource_coordinator::CoordinationUnitID cu_id) {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
const auto& it = nodes_.find(cu_id);
if (it == nodes_.end())
return nullptr;
@@ -81,6 +90,7 @@
}
ProcessNodeImpl* Graph::GetProcessNodeByPid(base::ProcessId pid) {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
auto it = processes_by_pid_.find(pid);
if (it == processes_by_pid_.end())
return nullptr;
@@ -102,6 +112,7 @@
size_t Graph::GetNodeAttachedDataCountForTesting(NodeBase* node,
const void* key) const {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (!node && !key)
return node_attached_data_map_.size();
@@ -118,12 +129,14 @@
}
void Graph::AddNewNode(NodeBase* new_node) {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
auto it = nodes_.emplace(new_node->id(), new_node);
DCHECK(it.second); // Inserted successfully
OnNodeAdded(new_node);
}
void Graph::RemoveNode(NodeBase* node) {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
OnBeforeNodeRemoved(node);
// Remove any node attached data affiliated with this node.
@@ -140,6 +153,7 @@
void Graph::BeforeProcessPidChange(ProcessNodeImpl* process,
base::ProcessId new_pid) {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
// On Windows, PIDs are aggressively reused, and because not all process
// creation/death notifications are synchronized, it's possible for more than
// one CU to have the same PID. To handle this, the second and subsequent
@@ -156,6 +170,7 @@
template <typename CUType>
std::vector<CUType*> Graph::GetAllNodesOfType() {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
const auto type = CUType::Type();
std::vector<CUType*> ret;
for (const auto& el : nodes_) {
diff --git a/chrome/browser/performance_manager/graph/graph.h b/chrome/browser/performance_manager/graph/graph.h
index c960747..ac68786 100644
--- a/chrome/browser/performance_manager/graph/graph.h
+++ b/chrome/browser/performance_manager/graph/graph.h
@@ -15,6 +15,7 @@
#include "base/macros.h"
#include "base/process/process_handle.h"
+#include "base/sequence_checker.h"
#include "chrome/browser/performance_manager/graph/node_attached_data.h"
#include "services/metrics/public/cpp/mojo_ukm_recorder.h"
#include "services/metrics/public/cpp/ukm_recorder.h"
@@ -97,8 +98,7 @@
std::map<NodeAttachedDataKey, std::unique_ptr<NodeAttachedData>>;
NodeAttachedDataMap node_attached_data_map_;
- static void Create();
-
+ SEQUENCE_CHECKER(sequence_checker_);
DISALLOW_COPY_AND_ASSIGN(Graph);
};
diff --git a/chrome/browser/performance_manager/graph/mock_graphs.cc b/chrome/browser/performance_manager/graph/mock_graphs.cc
index 751a36a..e19ab94 100644
--- a/chrome/browser/performance_manager/graph/mock_graphs.cc
+++ b/chrome/browser/performance_manager/graph/mock_graphs.cc
@@ -19,32 +19,38 @@
MockSinglePageInSingleProcessGraph::MockSinglePageInSingleProcessGraph(
Graph* graph)
: system(TestNodeWrapper<SystemNodeImpl>::Create(graph)),
- frame(TestNodeWrapper<FrameNodeImpl>::Create(graph)),
process(TestNodeWrapper<ProcessNodeImpl>::Create(graph)),
- page(TestNodeWrapper<PageNodeImpl>::Create(graph)) {
+ page(TestNodeWrapper<PageNodeImpl>::Create(graph)),
+ frame(TestNodeWrapper<FrameNodeImpl>::Create(graph)) {
frame->SetAllInterventionPoliciesForTesting(
resource_coordinator::mojom::InterventionPolicy::kDefault);
- page->AddFrame(frame->id());
- frame->SetProcess(process->id());
+ page->AddFrame(frame.get());
+ frame->SetProcess(process.get());
process->SetPID(1);
}
-MockSinglePageInSingleProcessGraph::~MockSinglePageInSingleProcessGraph() =
- default;
+MockSinglePageInSingleProcessGraph::~MockSinglePageInSingleProcessGraph() {
+ // Make sure frame nodes are torn down before pages.
+ frame.reset();
+ page.reset();
+}
MockMultiplePagesInSingleProcessGraph::MockMultiplePagesInSingleProcessGraph(
Graph* graph)
: MockSinglePageInSingleProcessGraph(graph),
- other_frame(TestNodeWrapper<FrameNodeImpl>::Create(graph)),
- other_page(TestNodeWrapper<PageNodeImpl>::Create(graph)) {
+ other_page(TestNodeWrapper<PageNodeImpl>::Create(graph)),
+ other_frame(TestNodeWrapper<FrameNodeImpl>::Create(graph)) {
other_frame->SetAllInterventionPoliciesForTesting(
resource_coordinator::mojom::InterventionPolicy::kDefault);
- other_page->AddFrame(other_frame->id());
- other_frame->SetProcess(process->id());
+ other_page->AddFrame(other_frame.get());
+ other_frame->SetProcess(process.get());
}
MockMultiplePagesInSingleProcessGraph::
- ~MockMultiplePagesInSingleProcessGraph() = default;
+ ~MockMultiplePagesInSingleProcessGraph() {
+ other_frame.reset();
+ other_page.reset();
+}
MockSinglePageWithMultipleProcessesGraph::
MockSinglePageWithMultipleProcessesGraph(Graph* graph)
@@ -53,9 +59,9 @@
other_process(TestNodeWrapper<ProcessNodeImpl>::Create(graph)) {
child_frame->SetAllInterventionPoliciesForTesting(
resource_coordinator::mojom::InterventionPolicy::kDefault);
- frame->AddChildFrame(child_frame->id());
- page->AddFrame(child_frame->id());
- child_frame->SetProcess(other_process->id());
+ frame->AddChildFrame(child_frame.get());
+ page->AddFrame(child_frame.get());
+ child_frame->SetProcess(other_process.get());
other_process->SetPID(2);
}
@@ -69,9 +75,9 @@
other_process(TestNodeWrapper<ProcessNodeImpl>::Create(graph)) {
child_frame->SetAllInterventionPoliciesForTesting(
resource_coordinator::mojom::InterventionPolicy::kDefault);
- other_frame->AddChildFrame(child_frame->id());
- other_page->AddFrame(child_frame->id());
- child_frame->SetProcess(other_process->id());
+ other_frame->AddChildFrame(child_frame.get());
+ other_page->AddFrame(child_frame.get());
+ child_frame->SetProcess(other_process.get());
other_process->SetPID(2);
}
diff --git a/chrome/browser/performance_manager/graph/mock_graphs.h b/chrome/browser/performance_manager/graph/mock_graphs.h
index 2a7e0fa..9937128 100644
--- a/chrome/browser/performance_manager/graph/mock_graphs.h
+++ b/chrome/browser/performance_manager/graph/mock_graphs.h
@@ -30,9 +30,9 @@
explicit MockSinglePageInSingleProcessGraph(Graph* graph);
~MockSinglePageInSingleProcessGraph();
TestNodeWrapper<SystemNodeImpl> system;
- TestNodeWrapper<FrameNodeImpl> frame;
TestNodeWrapper<ProcessNodeImpl> process;
TestNodeWrapper<PageNodeImpl> page;
+ TestNodeWrapper<FrameNodeImpl> frame;
};
// The following coordination unit graph topology is created to emulate a
@@ -52,8 +52,8 @@
: public MockSinglePageInSingleProcessGraph {
explicit MockMultiplePagesInSingleProcessGraph(Graph* graph);
~MockMultiplePagesInSingleProcessGraph();
- TestNodeWrapper<FrameNodeImpl> other_frame;
TestNodeWrapper<PageNodeImpl> other_page;
+ TestNodeWrapper<FrameNodeImpl> other_frame;
};
// The following coordination unit graph topology is created to emulate a
diff --git a/chrome/browser/performance_manager/graph/node_attached_data_unittest.cc b/chrome/browser/performance_manager/graph/node_attached_data_unittest.cc
index 6c2925a..5b0b3de 100644
--- a/chrome/browser/performance_manager/graph/node_attached_data_unittest.cc
+++ b/chrome/browser/performance_manager/graph/node_attached_data_unittest.cc
@@ -234,6 +234,7 @@
// Release the page node and expect the node attached data to have been
// cleaned up.
+ mock_graph.frame.reset();
mock_graph.page.reset();
EXPECT_EQ(1u, graph()->GetNodeAttachedDataCountForTesting(nullptr, nullptr));
EXPECT_EQ(0u,
diff --git a/chrome/browser/performance_manager/graph/node_base.cc b/chrome/browser/performance_manager/graph/node_base.cc
index 62d650dd..c952e038 100644
--- a/chrome/browser/performance_manager/graph/node_base.cc
+++ b/chrome/browser/performance_manager/graph/node_base.cc
@@ -59,6 +59,10 @@
return default_value;
}
+bool NodeBase::NodeInGraph(const NodeBase* other_node) const {
+ return graph_->GetNodeByID(other_node->id()) == other_node;
+}
+
void NodeBase::OnEventReceived(resource_coordinator::mojom::Event event) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
for (auto& observer : observers())
diff --git a/chrome/browser/performance_manager/graph/node_base.h b/chrome/browser/performance_manager/graph/node_base.h
index bcd9a9d..df44b0b0 100644
--- a/chrome/browser/performance_manager/graph/node_base.h
+++ b/chrome/browser/performance_manager/graph/node_base.h
@@ -33,7 +33,7 @@
NodeBase(const resource_coordinator::CoordinationUnitID& id, Graph* graph);
virtual ~NodeBase();
- void BeforeDestroyed();
+ virtual void BeforeDestroyed();
void AddObserver(GraphObserver* observer);
void RemoveObserver(GraphObserver* observer);
bool GetProperty(
@@ -61,6 +61,9 @@
}
protected:
+ // Returns true if |other_node| is in the same graph.
+ bool NodeInGraph(const NodeBase* other_node) const;
+
// Helper function for setting a property, and notifying observers if the
// value has changed.
template <typename NodeType,
diff --git a/chrome/browser/performance_manager/graph/page_node_impl.cc b/chrome/browser/performance_manager/graph/page_node_impl.cc
index 6a8f725..50230e1 100644
--- a/chrome/browser/performance_manager/graph/page_node_impl.cc
+++ b/chrome/browser/performance_manager/graph/page_node_impl.cc
@@ -41,30 +41,45 @@
PageNodeImpl::~PageNodeImpl() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- for (auto* child_frame : frame_nodes_)
- child_frame->RemovePageNode(this);
}
-void PageNodeImpl::AddFrame(
- const resource_coordinator::CoordinationUnitID& cu_id) {
+void PageNodeImpl::AddFrame(FrameNodeImpl* frame_node) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- DCHECK(cu_id.type == resource_coordinator::CoordinationUnitType::kFrame);
- FrameNodeImpl* frame_node = FrameNodeImpl::GetNodeByID(graph_, cu_id);
- if (!frame_node)
- return;
- if (AddFrameImpl(frame_node))
+ DCHECK(frame_node);
+ DCHECK(NodeInGraph(frame_node));
+
+ // TODO(https://crbug.com/944150): This method is called on navigation
+ // complete, and as such can fire more than once for a given frame in
+ // its lifetime. The |frame_nodes_| set is redundant to the frame tree and
+ // should be removed.
+ const bool inserted = frame_nodes_.insert(frame_node).second;
+ if (inserted) {
frame_node->AddPageNode(this);
+
+ OnNumFrozenFramesStateChange(
+ frame_node->lifecycle_state() ==
+ resource_coordinator::mojom::LifecycleState::kFrozen
+ ? 1
+ : 0);
+ MaybeInvalidateInterventionPolicies(frame_node, true /* adding_frame */);
+ }
}
-void PageNodeImpl::RemoveFrame(
- const resource_coordinator::CoordinationUnitID& cu_id) {
+void PageNodeImpl::RemoveFrame(FrameNodeImpl* frame_node) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- DCHECK(cu_id != id());
- FrameNodeImpl* frame_node = FrameNodeImpl::GetNodeByID(graph_, cu_id);
- if (!frame_node)
- return;
- if (RemoveFrameImpl(frame_node))
- frame_node->RemovePageNode(this);
+ DCHECK(frame_node);
+ DCHECK(NodeInGraph(frame_node));
+
+ size_t removed = frame_nodes_.erase(frame_node);
+ DCHECK_EQ(1u, removed);
+ frame_node->RemovePageNode(this);
+
+ OnNumFrozenFramesStateChange(
+ frame_node->lifecycle_state() ==
+ resource_coordinator::mojom::LifecycleState::kFrozen
+ ? -1
+ : 0);
+ MaybeInvalidateInterventionPolicies(frame_node, false /* adding_frame */);
}
void PageNodeImpl::SetIsLoading(bool is_loading) {
@@ -240,6 +255,19 @@
return intervention_policy_[kIndex];
}
+void PageNodeImpl::BeforeDestroyed() {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+ // TODO(siggi): This fails browser_tests for some reason. Would be nice to
+ // assert this.
+ // DCHECK(frame_nodes_.empty());
+
+ NodeBase::BeforeDestroyed();
+
+ for (auto* child_frame : frame_nodes_)
+ child_frame->RemovePageNode(this);
+}
+
void PageNodeImpl::set_page_almost_idle(bool page_almost_idle) {
if (page_almost_idle_ == page_almost_idle)
return;
@@ -262,35 +290,6 @@
observer.OnPagePropertyChanged(this, property_type, value);
}
-bool PageNodeImpl::AddFrameImpl(FrameNodeImpl* frame_node) {
- DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- const bool inserted = frame_nodes_.insert(frame_node).second;
- if (inserted) {
- OnNumFrozenFramesStateChange(
- frame_node->lifecycle_state() ==
- resource_coordinator::mojom::LifecycleState::kFrozen
- ? 1
- : 0);
- MaybeInvalidateInterventionPolicies(frame_node, true /* adding_frame */);
- }
- return inserted;
-}
-
-bool PageNodeImpl::RemoveFrameImpl(FrameNodeImpl* frame_node) {
- DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- bool removed = frame_nodes_.erase(frame_node) > 0;
- if (removed) {
- OnNumFrozenFramesStateChange(
- frame_node->lifecycle_state() ==
- resource_coordinator::mojom::LifecycleState::kFrozen
- ? -1
- : 0);
- MaybeInvalidateInterventionPolicies(frame_node, false /* adding_frame */);
- }
-
- return removed;
-}
-
void PageNodeImpl::OnNumFrozenFramesStateChange(int num_frozen_frames_delta) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
num_frozen_frames_ += num_frozen_frames_delta;
diff --git a/chrome/browser/performance_manager/graph/page_node_impl.h b/chrome/browser/performance_manager/graph/page_node_impl.h
index 1713b31..89624e6 100644
--- a/chrome/browser/performance_manager/graph/page_node_impl.h
+++ b/chrome/browser/performance_manager/graph/page_node_impl.h
@@ -29,8 +29,8 @@
Graph* graph);
~PageNodeImpl() override;
- void AddFrame(const resource_coordinator::CoordinationUnitID& cu_id);
- void RemoveFrame(const resource_coordinator::CoordinationUnitID& cu_id);
+ void AddFrame(FrameNodeImpl* frame_node);
+ void RemoveFrame(FrameNodeImpl* frame_node);
void SetIsLoading(bool is_loading);
void SetIsVisible(bool is_visible);
void SetUKMSourceId(int64_t ukm_source_id);
@@ -125,6 +125,8 @@
private:
friend class FrameNodeImpl;
+ void BeforeDestroyed() override;
+
void set_page_almost_idle(bool page_almost_idle);
// CoordinationUnitInterface implementation.
@@ -133,9 +135,6 @@
resource_coordinator::mojom::PropertyType property_type,
int64_t value) override;
- bool AddFrameImpl(FrameNodeImpl* frame_node);
- bool RemoveFrameImpl(FrameNodeImpl* frame_node);
-
// This is called whenever |num_frozen_frames_| changes, or whenever
// |frame_nodes_.size()| changes. It is used to synthesize the
// value of |has_nonempty_beforeunload| and to update the LifecycleState of
diff --git a/chrome/browser/performance_manager/graph/page_node_impl_unittest.cc b/chrome/browser/performance_manager/graph/page_node_impl_unittest.cc
index 43d74a0..bea8d36 100644
--- a/chrome/browser/performance_manager/graph/page_node_impl_unittest.cc
+++ b/chrome/browser/performance_manager/graph/page_node_impl_unittest.cc
@@ -43,9 +43,9 @@
auto frame2_node = CreateNode<FrameNodeImpl>();
auto frame3_node = CreateNode<FrameNodeImpl>();
- page_node->AddFrame(frame1_node->id());
- page_node->AddFrame(frame2_node->id());
- page_node->AddFrame(frame3_node->id());
+ page_node->AddFrame(frame1_node.get());
+ page_node->AddFrame(frame2_node.get());
+ page_node->AddFrame(frame3_node.get());
EXPECT_EQ(3u, page_node->GetFrameNodes().size());
}
@@ -54,9 +54,9 @@
auto frame1_node = CreateNode<FrameNodeImpl>();
auto frame2_node = CreateNode<FrameNodeImpl>();
- page_node->AddFrame(frame1_node->id());
- page_node->AddFrame(frame2_node->id());
- page_node->AddFrame(frame1_node->id());
+ page_node->AddFrame(frame1_node.get());
+ page_node->AddFrame(frame2_node.get());
+ page_node->AddFrame(frame1_node.get());
EXPECT_EQ(2u, page_node->GetFrameNodes().size());
}
@@ -68,14 +68,14 @@
EXPECT_EQ(0u, page_node->GetFrameNodes().size());
EXPECT_FALSE(frame_node->GetPageNode());
- page_node->AddFrame(frame_node->id());
+ page_node->AddFrame(frame_node.get());
// Ensure correct Parent-child relationships have been established.
EXPECT_EQ(1u, page_node->GetFrameNodes().size());
EXPECT_EQ(1u, page_node->GetFrameNodes().count(frame_node.get()));
EXPECT_EQ(page_node.get(), frame_node->GetPageNode());
- page_node->RemoveFrame(frame_node->id());
+ page_node->RemoveFrame(frame_node.get());
// Parent-child relationships should no longer exist.
EXPECT_EQ(0u, page_node->GetFrameNodes().size());
@@ -302,7 +302,7 @@
// Add a frame and expect the values to be invalidated. Reaggregate and
// ensure the appropriate value results.
- page->AddFrame(f0->id());
+ page->AddFrame(f0.get());
EXPECT_EQ(1u, page->GetInterventionPolicyFramesReportedForTesting());
ExpectRawInterventionPolicy(
resource_coordinator::mojom::InterventionPolicy::kUnknown, page.get());
@@ -310,7 +310,7 @@
// Do it again. This time the raw values should be the same as the
// aggregated values above.
- page->AddFrame(f1->id());
+ page->AddFrame(f1.get());
EXPECT_EQ(2u, page->GetInterventionPolicyFramesReportedForTesting());
ExpectRawInterventionPolicy(
resource_coordinator::mojom::InterventionPolicy::kUnknown, page.get());
@@ -322,6 +322,9 @@
ExpectRawInterventionPolicy(
resource_coordinator::mojom::InterventionPolicy::kUnknown, page.get());
ExpectInterventionPolicy(f0_policy_aggregated, page.get());
+
+ f0.reset();
+ page.reset();
}
} // namespace
@@ -433,9 +436,9 @@
TestNodeWrapper<FrameNodeImpl> f1 =
TestNodeWrapper<FrameNodeImpl>::Create(mock_graph);
EXPECT_EQ(0u, page->GetInterventionPolicyFramesReportedForTesting());
- page->AddFrame(f0->id());
+ page->AddFrame(f0.get());
EXPECT_EQ(0u, page->GetInterventionPolicyFramesReportedForTesting());
- page->AddFrame(f1->id());
+ page->AddFrame(f1.get());
EXPECT_EQ(0u, page->GetInterventionPolicyFramesReportedForTesting());
// Set the policies on the first frame. This should be observed by the page
diff --git a/chrome/browser/performance_manager/graph/process_node_impl.cc b/chrome/browser/performance_manager/graph/process_node_impl.cc
index 221d0875..5c60dc26 100644
--- a/chrome/browser/performance_manager/graph/process_node_impl.cc
+++ b/chrome/browser/performance_manager/graph/process_node_impl.cc
@@ -19,13 +19,6 @@
ProcessNodeImpl::~ProcessNodeImpl() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- // Make as if we're transitioning to the null PID before we die to clear this
- // instance from the PID map.
- if (process_id_ != base::kNullProcessId)
- graph()->BeforeProcessPidChange(this, base::kNullProcessId);
-
- for (auto* child_frame : frame_nodes_)
- child_frame->RemoveProcessNode(this);
}
void ProcessNodeImpl::AddFrame(FrameNodeImpl* frame_node) {
@@ -128,6 +121,19 @@
IncrementNumFrozenFrames();
}
+void ProcessNodeImpl::BeforeDestroyed() {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+ NodeBase::BeforeDestroyed();
+
+ // Make as if we're transitioning to the null PID before we die to clear this
+ // instance from the PID map.
+ if (process_id_ != base::kNullProcessId)
+ graph()->BeforeProcessPidChange(this, base::kNullProcessId);
+
+ for (auto* child_frame : frame_nodes_)
+ child_frame->RemoveProcessNode(this);
+}
+
void ProcessNodeImpl::OnEventReceived(
resource_coordinator::mojom::Event event) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
diff --git a/chrome/browser/performance_manager/graph/process_node_impl.h b/chrome/browser/performance_manager/graph/process_node_impl.h
index b05e9c9..eeb2bae 100644
--- a/chrome/browser/performance_manager/graph/process_node_impl.h
+++ b/chrome/browser/performance_manager/graph/process_node_impl.h
@@ -80,6 +80,8 @@
resource_coordinator::mojom::LifecycleState old_state);
private:
+ void BeforeDestroyed() override;
+
// CoordinationUnitInterface implementation.
void OnEventReceived(resource_coordinator::mojom::Event event) override;
void OnPropertyChanged(
diff --git a/chrome/browser/performance_manager/observers/metrics_collector_unittest.cc b/chrome/browser/performance_manager/observers/metrics_collector_unittest.cc
index 71ba823a..10e694e 100644
--- a/chrome/browser/performance_manager/observers/metrics_collector_unittest.cc
+++ b/chrome/browser/performance_manager/observers/metrics_collector_unittest.cc
@@ -111,7 +111,7 @@
FromBackgroundedToFirstNonPersistentNotificationCreatedUMA) {
auto page_node = CreateNode<PageNodeImpl>();
auto frame_node = CreateNode<FrameNodeImpl>();
- page_node->AddFrame(frame_node->id());
+ page_node->AddFrame(frame_node.get());
page_node->OnMainFrameNavigationCommitted(
ResourceCoordinatorClock::NowTicks(), kDummyID, kDummyUrl);
@@ -147,7 +147,7 @@
FromBackgroundedToFirstNonPersistentNotificationCreatedUMA5MinutesTimeout) {
auto page_node = CreateNode<PageNodeImpl>();
auto frame_node = CreateNode<FrameNodeImpl>();
- page_node->AddFrame(frame_node->id());
+ page_node->AddFrame(frame_node.get());
page_node->OnMainFrameNavigationCommitted(
ResourceCoordinatorClock::NowTicks(), kDummyID, kDummyUrl);
@@ -219,8 +219,8 @@
auto process_node = CreateNode<ProcessNodeImpl>();
auto frame_node = CreateNode<FrameNodeImpl>();
- page_node->AddFrame(frame_node->id());
- frame_node->SetProcess(process_node->id());
+ page_node->AddFrame(frame_node.get());
+ frame_node->SetProcess(process_node.get());
ukm::TestUkmRecorder ukm_recorder;
graph()->set_ukm_recorder(&ukm_recorder);
diff --git a/chrome/browser/performance_manager/performance_manager_tab_helper.cc b/chrome/browser/performance_manager/performance_manager_tab_helper.cc
index bd0455e..1194f4ce 100644
--- a/chrome/browser/performance_manager/performance_manager_tab_helper.cc
+++ b/chrome/browser/performance_manager/performance_manager_tab_helper.cc
@@ -103,7 +103,7 @@
performance_manager_->task_runner()->PostTask(
FROM_HERE,
base::BindOnce(&FrameNodeImpl::AddChildFrame,
- base::Unretained(parent_frame_node.get()), frame->id()));
+ base::Unretained(parent_frame_node.get()), frame.get()));
}
RenderProcessUserData* user_data =
@@ -117,7 +117,7 @@
performance_manager_->task_runner()->PostTask(
FROM_HERE, base::BindOnce(&FrameNodeImpl::SetProcess,
base::Unretained(frame.get()),
- user_data->process_node()->id()));
+ user_data->process_node()));
}
frames_[render_frame_host] = std::move(frame);
@@ -163,7 +163,7 @@
content::RenderFrameHost* render_frame_host =
navigation_handle->GetRenderFrameHost();
// Make sure the hierarchical structure is constructed before sending signal
- // to Resource Coordinator.
+ // to the performance manager.
// TODO(siggi): Ideally this would be a DCHECK, but it seems it's possible
// to get a DidFinishNavigation notification for a deleted frame with
// the network service.
@@ -173,7 +173,7 @@
performance_manager_->task_runner()->PostTask(
FROM_HERE,
base::BindOnce(&PageNodeImpl::AddFrame,
- base::Unretained(page_node_.get()), it->second->id()));
+ base::Unretained(page_node_.get()), it->second.get()));
if (navigation_handle->IsInMainFrame()) {
OnMainFrameNavigation(navigation_handle->GetNavigationId());