From bab17128283cf2efd312c95abcffe25d73f9cc3f Mon Sep 17 00:00:00 2001 From: Joe Mason Date: Wed, 10 Jun 2020 17:40:33 +0000 Subject: [PATCH] [PM] Provide public accessors for V8PerFrameMemoryDecorator data MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Rename FrameData, ProcessData to NodeAttachedFrameData, NodeAttachedProcessData and add public FrameData and ProcessData wrappers. * Remove the *ForTesting accessors since the unit test can now use the public accessors. R=siggi Bug: 1080672 Change-Id: Iad768a7fd77f30cfa6ee171c8fe75011a8037f61 Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2238271 Commit-Queue: Joe Mason Reviewed-by: Sigurður Ásgeirsson Cr-Commit-Position: refs/heads/master@{#777006} --- .../v8_per_frame_memory_decorator.cc | 163 ++++++++++-------- .../v8_per_frame_memory_decorator_unittest.cc | 79 ++++++--- .../v8_per_frame_memory_decorator.h | 68 +++++++- 3 files changed, 208 insertions(+), 102 deletions(-) diff --git a/components/performance_manager/decorators/v8_per_frame_memory_decorator.cc b/components/performance_manager/decorators/v8_per_frame_memory_decorator.cc index 454dbfc7defeeb..d2d606446b9c86 100644 --- a/components/performance_manager/decorators/v8_per_frame_memory_decorator.cc +++ b/components/performance_manager/decorators/v8_per_frame_memory_decorator.cc @@ -21,42 +21,64 @@ namespace performance_manager { -class V8PerFrameMemoryDecorator::FrameData - : public ExternalNodeAttachedDataImpl< - V8PerFrameMemoryDecorator::FrameData> { +namespace internal { + +// Provides access to V8PerFrameMemoryDecorator's private +// BindReceiverWithProxyHost method. +class ProxyHostReceiverBinder { + public: + static void Bind(const V8PerFrameMemoryDecorator& decorator, + mojo::PendingReceiver< + performance_manager::mojom::V8PerFrameMemoryReporter> + pending_receiver, + RenderProcessHostProxy proxy) { + decorator.BindReceiverWithProxyHost(std::move(pending_receiver), proxy); + } +}; + +} // namespace internal + +namespace { + +// Private implementations of the node attached data. This keeps the complexity +// out of the header file. + +class NodeAttachedFrameData + : public ExternalNodeAttachedDataImpl { public: - explicit FrameData(const FrameNode* frame_node) {} - ~FrameData() override = default; + explicit NodeAttachedFrameData(const FrameNode* frame_node) {} + ~NodeAttachedFrameData() override = default; - FrameData(const FrameData&) = delete; - FrameData& operator=(const FrameData&) = delete; + NodeAttachedFrameData(const NodeAttachedFrameData&) = delete; + NodeAttachedFrameData& operator=(const NodeAttachedFrameData&) = delete; - void set_v8_bytes_used(uint64_t v8_bytes_used) { - v8_bytes_used_ = v8_bytes_used; + const V8PerFrameMemoryDecorator::FrameData* data() const { + return data_available_ ? &data_ : nullptr; } - uint64_t v8_bytes_used() const { return v8_bytes_used_; } private: - uint64_t v8_bytes_used_ = 0; + friend class NodeAttachedProcessData; + + V8PerFrameMemoryDecorator::FrameData data_; + bool data_available_ = false; }; -class V8PerFrameMemoryDecorator::ProcessData - : public ExternalNodeAttachedDataImpl< - V8PerFrameMemoryDecorator::ProcessData> { +class NodeAttachedProcessData + : public ExternalNodeAttachedDataImpl { public: - explicit ProcessData(const ProcessNode* process_node) + explicit NodeAttachedProcessData(const ProcessNode* process_node) : process_node_(process_node) {} - ~ProcessData() override = default; - - ProcessData(const ProcessData&) = delete; - ProcessData& operator=(const ProcessData&) = delete; + ~NodeAttachedProcessData() override = default; - void Initialize(const V8PerFrameMemoryDecorator* decorator); + NodeAttachedProcessData(const NodeAttachedProcessData&) = delete; + NodeAttachedProcessData& operator=(const NodeAttachedProcessData&) = delete; - uint64_t unassociated_v8_bytes_used() const { - return unassociated_v8_bytes_used_; + const V8PerFrameMemoryDecorator::ProcessData* data() const { + return data_available_ ? &data_ : nullptr; } + void Initialize(const V8PerFrameMemoryDecorator* decorator); + private: void StartMeasurement(); void ScheduleNextMeasurement(); @@ -74,10 +96,11 @@ class V8PerFrameMemoryDecorator::ProcessData base::TimeTicks last_request_time_; base::OneShotTimer timer_; - uint64_t unassociated_v8_bytes_used_ = 0; + V8PerFrameMemoryDecorator::ProcessData data_; + bool data_available_ = false; }; -void V8PerFrameMemoryDecorator::ProcessData::Initialize( +void NodeAttachedProcessData::Initialize( const V8PerFrameMemoryDecorator* decorator) { DCHECK_EQ(nullptr, decorator_); decorator_ = decorator; @@ -85,32 +108,31 @@ void V8PerFrameMemoryDecorator::ProcessData::Initialize( StartMeasurement(); } -void V8PerFrameMemoryDecorator::ProcessData::StartMeasurement() { - DCHECK(process_node_); - +void NodeAttachedProcessData::StartMeasurement() { last_request_time_ = base::TimeTicks::Now(); EnsureRemote(); - resource_usage_reporter_->GetPerFrameV8MemoryUsageData(base::BindOnce( - &V8PerFrameMemoryDecorator::ProcessData::OnPerFrameV8MemoryUsageData, - base::Unretained(this))); + resource_usage_reporter_->GetPerFrameV8MemoryUsageData( + base::BindOnce(&NodeAttachedProcessData::OnPerFrameV8MemoryUsageData, + base::Unretained(this))); } -void V8PerFrameMemoryDecorator::ProcessData::ScheduleNextMeasurement() { +void NodeAttachedProcessData::ScheduleNextMeasurement() { + DCHECK_NE(nullptr, decorator_); base::TimeTicks next_request_time = last_request_time_ + decorator_->min_time_between_requests_per_process(); timer_.Start(FROM_HERE, next_request_time - base::TimeTicks::Now(), this, - &ProcessData::StartMeasurement); + &NodeAttachedProcessData::StartMeasurement); } -void V8PerFrameMemoryDecorator::ProcessData::OnPerFrameV8MemoryUsageData( +void NodeAttachedProcessData::OnPerFrameV8MemoryUsageData( performance_manager::mojom::PerProcessV8MemoryUsageDataPtr result) { // Distribute the data to the frames. // If a frame doesn't have corresponding data in the result, clear any data // it may have had. Any datum in the result that doesn't correspond to an // existing frame is likewise accured to unassociated usage. - unassociated_v8_bytes_used_ = result->unassociated_bytes_used; + uint64_t unassociated_v8_bytes_used = result->unassociated_bytes_used; base::flat_map associated_memory; @@ -121,15 +143,17 @@ void V8PerFrameMemoryDecorator::ProcessData::OnPerFrameV8MemoryUsageData( auto it = associated_memory.find(frame_node->GetDevToolsToken()); if (it == associated_memory.end()) { // No data for this node, clear any data associated with it. - FrameData::Destroy(frame_node); + NodeAttachedFrameData::Destroy(frame_node); } else { // There should always be data for the main isolated world for each frame. DCHECK(base::Contains(it->second->associated_bytes, 0)); - FrameData* frame_data = FrameData::GetOrCreate(frame_node); + NodeAttachedFrameData* frame_data = + NodeAttachedFrameData::GetOrCreate(frame_node); for (const auto& kv : it->second->associated_bytes) { if (kv.first == 0) { - frame_data->set_v8_bytes_used(kv.second->bytes_used); + frame_data->data_available_ = true; + frame_data->data_.set_v8_bytes_used(kv.second->bytes_used); } else { // TODO(siggi): Where to stash the rest of the data? } @@ -142,14 +166,17 @@ void V8PerFrameMemoryDecorator::ProcessData::OnPerFrameV8MemoryUsageData( for (const auto& it : associated_memory) { // Accrue the data for non-existent frames to unassociated bytes. - unassociated_v8_bytes_used_ += it.second->associated_bytes[0]->bytes_used; + unassociated_v8_bytes_used += it.second->associated_bytes[0]->bytes_used; } + data_available_ = true; + data_.set_unassociated_v8_bytes_used(unassociated_v8_bytes_used); + // Schedule another measurement for this process node. ScheduleNextMeasurement(); } -void V8PerFrameMemoryDecorator::ProcessData::EnsureRemote() { +void NodeAttachedProcessData::EnsureRemote() { if (resource_usage_reporter_.is_bound()) return; @@ -159,7 +186,24 @@ void V8PerFrameMemoryDecorator::ProcessData::EnsureRemote() { RenderProcessHostProxy proxy = process_node_->GetRenderProcessHostProxy(); - decorator_->BindReceiverWithProxyHost(std::move(pending_receiver), proxy); + DCHECK_NE(nullptr, decorator_); + internal::ProxyHostReceiverBinder::Bind(*decorator_, + std::move(pending_receiver), proxy); +} + +} // namespace + +const V8PerFrameMemoryDecorator::FrameData* +V8PerFrameMemoryDecorator::FrameData::ForFrameNode(const FrameNode* node) { + auto* node_data = NodeAttachedFrameData::Get(node); + return node_data ? node_data->data() : nullptr; +} + +const V8PerFrameMemoryDecorator::ProcessData* +V8PerFrameMemoryDecorator::ProcessData::ForProcessNode( + const ProcessNode* node) { + auto* node_data = NodeAttachedProcessData::Get(node); + return node_data ? node_data->data() : nullptr; } V8PerFrameMemoryDecorator::V8PerFrameMemoryDecorator( @@ -189,67 +233,44 @@ void V8PerFrameMemoryDecorator::OnTakenFromGraph(Graph* graph) { void V8PerFrameMemoryDecorator::OnProcessNodeAdded( const ProcessNode* process_node) { - DCHECK_EQ(nullptr, V8PerFrameMemoryDecorator::ProcessData::Get(process_node)); + DCHECK_EQ(nullptr, NodeAttachedProcessData::Get(process_node)); // Only renderer processes have frames. Don't attempt to connect to other // process types. if (process_node->GetProcessType() != content::PROCESS_TYPE_RENDERER) return; - V8PerFrameMemoryDecorator::ProcessData* process_data = - V8PerFrameMemoryDecorator::ProcessData::GetOrCreate(process_node); - DCHECK_NE(nullptr, process_data); + NodeAttachedProcessData* process_data = + NodeAttachedProcessData::GetOrCreate(process_node); process_data->Initialize(this); } base::Value V8PerFrameMemoryDecorator::DescribeFrameNodeData( const FrameNode* frame_node) const { - FrameData* frame_data = FrameData::Get(frame_node); + const FrameData* const frame_data = FrameData::ForFrameNode(frame_node); if (!frame_data) return base::Value(); base::Value dict(base::Value::Type::DICTIONARY); - dict.SetIntKey("v8_bytes_used_", frame_data->v8_bytes_used()); + dict.SetIntKey("v8_bytes_used", frame_data->v8_bytes_used()); return dict; } base::Value V8PerFrameMemoryDecorator::DescribeProcessNodeData( const ProcessNode* process_node) const { - ProcessData* process_data = ProcessData::Get(process_node); + const ProcessData* const process_data = + ProcessData::ForProcessNode(process_node); if (!process_data) return base::Value(); DCHECK_EQ(content::PROCESS_TYPE_RENDERER, process_node->GetProcessType()); base::Value dict(base::Value::Type::DICTIONARY); - dict.SetIntKey("unassociated_v8_bytes_used_", + dict.SetIntKey("unassociated_v8_bytes_used", process_data->unassociated_v8_bytes_used()); return dict; } -uint64_t V8PerFrameMemoryDecorator::GetUnassociatedBytesForTesting( - const ProcessNode* process_node) { - ProcessData* process_data = ProcessData::Get(process_node); - if (!process_data) - return 0u; - - return process_data->unassociated_v8_bytes_used(); -} - -uint64_t V8PerFrameMemoryDecorator::GetAssociatedBytesForTesting( - const FrameNode* frame_node) { - FrameData* frame_data = FrameData::Get(frame_node); - if (!frame_data) - return 0u; - - return frame_data->v8_bytes_used(); -} - -bool V8PerFrameMemoryDecorator::HasAssociatedBytesForTesting( - const FrameNode* frame_node) { - return FrameData::Get(frame_node); -} - void V8PerFrameMemoryDecorator::BindReceiverWithProxyHost( mojo::PendingReceiver pending_receiver, diff --git a/components/performance_manager/decorators/v8_per_frame_memory_decorator_unittest.cc b/components/performance_manager/decorators/v8_per_frame_memory_decorator_unittest.cc index fd9628fd685bc3..f8a0cb3b1bbec9 100644 --- a/components/performance_manager/decorators/v8_per_frame_memory_decorator_unittest.cc +++ b/components/performance_manager/decorators/v8_per_frame_memory_decorator_unittest.cc @@ -27,6 +27,9 @@ constexpr uint64_t kUnassociatedBytes = 0xABBA; namespace { +using FrameData = V8PerFrameMemoryDecorator::FrameData; +using ProcessData = V8PerFrameMemoryDecorator::ProcessData; + class TestV8PerFrameMemoryDecorator : public V8PerFrameMemoryDecorator { public: explicit TestV8PerFrameMemoryDecorator( @@ -97,13 +100,12 @@ class V8PerFrameMemoryDecoratorTest : public GraphTestHarness { } } - TestV8PerFrameMemoryDecorator* CreateDecorator() { + void CreateDecorator() { std::unique_ptr decorator = std::make_unique(kMinTimeBetweenRequests, this); test_decorator_raw_ = decorator.get(); graph()->PassToGraph(std::move(decorator)); - return test_decorator_raw_; } void ExpectQuery( @@ -168,7 +170,7 @@ void TestV8PerFrameMemoryDecorator::BindReceiverWithProxyHost( } TEST_F(V8PerFrameMemoryDecoratorTest, InstantiateOnEmptyGraph) { - auto* decorator = CreateDecorator(); + CreateDecorator(); MockV8PerFrameMemoryReporter mock_reporter; auto data = mojom::PerProcessV8MemoryUsageData::New(); @@ -180,11 +182,16 @@ TEST_F(V8PerFrameMemoryDecoratorTest, InstantiateOnEmptyGraph) { content::PROCESS_TYPE_RENDERER, RenderProcessHostProxy::CreateForTesting(kTestProcessID)); + // Data should not be available until the measurement is taken. + EXPECT_FALSE(ProcessData::ForProcessNode(process.get())); + // Run until idle to make sure the measurement isn't a hard loop. task_env().RunUntilIdle(); - EXPECT_EQ(kUnassociatedBytes, - decorator->GetUnassociatedBytesForTesting(process.get())); + EXPECT_TRUE(ProcessData::ForProcessNode(process.get())); + EXPECT_EQ( + kUnassociatedBytes, + ProcessData::ForProcessNode(process.get())->unassociated_v8_bytes_used()); } TEST_F(V8PerFrameMemoryDecoratorTest, InstantiateOnNonEmptyGraph) { @@ -199,13 +206,18 @@ TEST_F(V8PerFrameMemoryDecoratorTest, InstantiateOnNonEmptyGraph) { data->unassociated_bytes_used = kUnassociatedBytes; ExpectBindAndRespondToQuery(&mock_reporter, std::move(data)); - auto* decorator = CreateDecorator(); + CreateDecorator(); + + // Data should not be available until the measurement is taken. + EXPECT_FALSE(ProcessData::ForProcessNode(process.get())); // Run until idle to make sure the measurement isn't a hard loop. task_env().RunUntilIdle(); - EXPECT_EQ(kUnassociatedBytes, - decorator->GetUnassociatedBytesForTesting(process.get())); + EXPECT_TRUE(ProcessData::ForProcessNode(process.get())); + EXPECT_EQ( + kUnassociatedBytes, + ProcessData::ForProcessNode(process.get())->unassociated_v8_bytes_used()); } TEST_F(V8PerFrameMemoryDecoratorTest, OnlyMeasureRenderers) { @@ -240,12 +252,15 @@ TEST_F(V8PerFrameMemoryDecoratorTest, QueryRateIsLimited) { ExpectBindAndRespondToQuery(&mock_reporter, std::move(data)); } - auto* decorator = CreateDecorator(); + CreateDecorator(); // Run until idle to make sure the measurement isn't a hard loop. task_env().RunUntilIdle(); - EXPECT_EQ(1u, decorator->GetUnassociatedBytesForTesting(process.get())); + EXPECT_TRUE(ProcessData::ForProcessNode(process.get())); + EXPECT_EQ( + 1u, + ProcessData::ForProcessNode(process.get())->unassociated_v8_bytes_used()); // There shouldn't be an additional request this soon. task_env().FastForwardBy(kMinTimeBetweenRequests / 2); @@ -269,7 +284,10 @@ TEST_F(V8PerFrameMemoryDecoratorTest, QueryRateIsLimited) { task_env().FastForwardBy(10 * kMinTimeBetweenRequests); testing::Mock::VerifyAndClearExpectations(&mock_reporter); - EXPECT_EQ(1u, decorator->GetUnassociatedBytesForTesting(process.get())); + EXPECT_TRUE(ProcessData::ForProcessNode(process.get())); + EXPECT_EQ( + 1u, + ProcessData::ForProcessNode(process.get())->unassociated_v8_bytes_used()); // Expect another query once completing the query above. { @@ -290,7 +308,10 @@ TEST_F(V8PerFrameMemoryDecoratorTest, QueryRateIsLimited) { task_env().RunUntilIdle(); // This should have updated all the way to the third response. - EXPECT_EQ(3u, decorator->GetUnassociatedBytesForTesting(process.get())); + EXPECT_TRUE(ProcessData::ForProcessNode(process.get())); + EXPECT_EQ( + 3u, + ProcessData::ForProcessNode(process.get())->unassociated_v8_bytes_used()); // Despite the long delay to respond to request 2, there shouldn't be another // request until kMinTimeBetweenRequests has expired. @@ -299,7 +320,7 @@ TEST_F(V8PerFrameMemoryDecoratorTest, QueryRateIsLimited) { } TEST_F(V8PerFrameMemoryDecoratorTest, MultipleProcessesHaveDistinctSchedules) { - auto* decorator = CreateDecorator(); + CreateDecorator(); // Create a process node and validate that it gets a request. MockV8PerFrameMemoryReporter reporter1; @@ -331,8 +352,12 @@ TEST_F(V8PerFrameMemoryDecoratorTest, MultipleProcessesHaveDistinctSchedules) { task_env().RunUntilIdle(); testing::Mock::VerifyAndClearExpectations(&reporter2); - EXPECT_EQ(1u, decorator->GetUnassociatedBytesForTesting(process1.get())); - EXPECT_EQ(2u, decorator->GetUnassociatedBytesForTesting(process2.get())); + EXPECT_TRUE(ProcessData::ForProcessNode(process1.get())); + EXPECT_EQ(1u, ProcessData::ForProcessNode(process1.get()) + ->unassociated_v8_bytes_used()); + EXPECT_TRUE(ProcessData::ForProcessNode(process2.get())); + EXPECT_EQ(2u, ProcessData::ForProcessNode(process2.get()) + ->unassociated_v8_bytes_used()); // Capture the request time from each process. auto capture_time_lambda = @@ -362,7 +387,7 @@ TEST_F(V8PerFrameMemoryDecoratorTest, MultipleProcessesHaveDistinctSchedules) { } TEST_F(V8PerFrameMemoryDecoratorTest, PerFrameDataIsDistributed) { - auto* decorator = CreateDecorator(); + CreateDecorator(); MockV8PerFrameMemoryReporter reporter; { @@ -382,7 +407,10 @@ TEST_F(V8PerFrameMemoryDecoratorTest, PerFrameDataIsDistributed) { testing::Mock::VerifyAndClearExpectations(&reporter); // Since the frame was unknown, the usage should have accrued to unassociated. - EXPECT_EQ(1024u, decorator->GetUnassociatedBytesForTesting(process.get())); + EXPECT_TRUE(ProcessData::ForProcessNode(process.get())); + EXPECT_EQ( + 1024u, + ProcessData::ForProcessNode(process.get())->unassociated_v8_bytes_used()); // Create a couple of frames with specified IDs. auto page = CreateNode(); @@ -404,8 +432,10 @@ TEST_F(V8PerFrameMemoryDecoratorTest, PerFrameDataIsDistributed) { task_env().FastForwardBy(kMinTimeBetweenRequests * 1.5); testing::Mock::VerifyAndClearExpectations(&reporter); - EXPECT_EQ(1001u, decorator->GetAssociatedBytesForTesting(frame1.get())); - EXPECT_EQ(1002u, decorator->GetAssociatedBytesForTesting(frame2.get())); + ASSERT_TRUE(FrameData::ForFrameNode(frame1.get())); + EXPECT_EQ(1001u, FrameData::ForFrameNode(frame1.get())->v8_bytes_used()); + ASSERT_TRUE(FrameData::ForFrameNode(frame2.get())); + EXPECT_EQ(1002u, FrameData::ForFrameNode(frame2.get())->v8_bytes_used()); // Now verify that data is cleared for any frame that doesn't get an update, // plus verify that unknown frame data toes to unassociated bytes. @@ -419,10 +449,13 @@ TEST_F(V8PerFrameMemoryDecoratorTest, PerFrameDataIsDistributed) { task_env().FastForwardBy(kMinTimeBetweenRequests); testing::Mock::VerifyAndClearExpectations(&reporter); - EXPECT_EQ(1003u, decorator->GetAssociatedBytesForTesting(frame1.get())); - EXPECT_FALSE(decorator->HasAssociatedBytesForTesting(frame2.get())); - EXPECT_EQ(0u, decorator->GetAssociatedBytesForTesting(frame2.get())); - EXPECT_EQ(2233u, decorator->GetUnassociatedBytesForTesting(process.get())); + ASSERT_TRUE(FrameData::ForFrameNode(frame1.get())); + EXPECT_EQ(1003u, FrameData::ForFrameNode(frame1.get())->v8_bytes_used()); + EXPECT_FALSE(FrameData::ForFrameNode(frame2.get())); + ASSERT_TRUE(ProcessData::ForProcessNode(process.get())); + EXPECT_EQ( + 2233u, + ProcessData::ForProcessNode(process.get())->unassociated_v8_bytes_used()); } } // namespace performance_manager diff --git a/components/performance_manager/public/decorators/v8_per_frame_memory_decorator.h b/components/performance_manager/public/decorators/v8_per_frame_memory_decorator.h index 64e15877ee1329..294329c0fc31fe 100644 --- a/components/performance_manager/public/decorators/v8_per_frame_memory_decorator.h +++ b/components/performance_manager/public/decorators/v8_per_frame_memory_decorator.h @@ -13,10 +13,17 @@ namespace performance_manager { +namespace internal { +class ProxyHostReceiverBinder; +} + class V8PerFrameMemoryDecorator : public GraphOwned, public ProcessNode::ObserverDefaultImpl, public NodeDataDescriberDefaultImpl { public: + class FrameData; + class ProcessData; + // Creates a new decorator with the given time between requests per process, // which bounds the number of requests over time. explicit V8PerFrameMemoryDecorator( @@ -42,15 +49,8 @@ class V8PerFrameMemoryDecorator : public GraphOwned, return min_time_between_requests_per_process_; } - uint64_t GetUnassociatedBytesForTesting(const ProcessNode* process_node); - uint64_t GetAssociatedBytesForTesting(const FrameNode* frame_node); - bool HasAssociatedBytesForTesting(const FrameNode* frame_node); - private: - class ProcessData; - class FrameData; - - friend class ProcessData; + friend class internal::ProxyHostReceiverBinder; // Testing seam. virtual void BindReceiverWithProxyHost( @@ -62,6 +62,58 @@ class V8PerFrameMemoryDecorator : public GraphOwned, Graph* graph_ = nullptr; }; +class V8PerFrameMemoryDecorator::FrameData { + public: + FrameData() = default; + virtual ~FrameData() = default; + + FrameData(const FrameData&) = delete; + FrameData& operator=(const FrameData&) = delete; + + // Returns the number of bytes used by V8 for this frame at the last + // measurement. + uint64_t v8_bytes_used() const { return v8_bytes_used_; } + + void set_v8_bytes_used(uint64_t v8_bytes_used) { + v8_bytes_used_ = v8_bytes_used; + } + + // Returns FrameData for the given node, or nullptr if no measurement has + // been taken. The returned pointer must only be accessed on the graph + // sequence and may go invalid at any time after leaving the calling scope. + static const FrameData* ForFrameNode(const FrameNode* node); + + private: + uint64_t v8_bytes_used_ = 0; +}; + +class V8PerFrameMemoryDecorator::ProcessData { + public: + ProcessData() = default; + virtual ~ProcessData() = default; + + ProcessData(const ProcessData&) = delete; + ProcessData& operator=(const ProcessData&) = delete; + + // Returns the number of bytes used by V8 at the last measurement in this + // process that could not be attributed to a frame. + uint64_t unassociated_v8_bytes_used() const { + return unassociated_v8_bytes_used_; + } + + void set_unassociated_v8_bytes_used(uint64_t unassociated_v8_bytes_used) { + unassociated_v8_bytes_used_ = unassociated_v8_bytes_used; + } + + // Returns FrameData for the given node, or nullptr if no measurement has + // been taken. The returned pointer must only be accessed on the graph + // sequence and may go invalid at any time after leaving the calling scope. + static const ProcessData* ForProcessNode(const ProcessNode* node); + + private: + uint64_t unassociated_v8_bytes_used_ = 0; +}; + } // namespace performance_manager #endif // COMPONENTS_PERFORMANCE_MANAGER_PUBLIC_DECORATORS_V8_PER_FRAME_MEMORY_DECORATOR_H_