Commit b464ed94 authored by Chris Hamilton's avatar Chris Hamilton Committed by Commit Bot

[PM] Create V8ContextTrackerImpl.

This CL implements the data structures that are needed for bookkeeping
V8 contexts, and related concepts. The lifetime management of these
structures is testing in isolation.

A further CL will implement business logic on V8ContextTracker, and add
tests for that.

BUG=1085129

Change-Id: Iececc631fa38f04359ebeb823c53b641e57a4f24
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2443490
Commit-Queue: Chris Hamilton <chrisha@chromium.org>
Reviewed-by: default avatarJoe Mason <joenotcharles@chromium.org>
Cr-Commit-Position: refs/heads/master@{#813266}
parent 612c2cd0
...@@ -133,8 +133,12 @@ static_library("performance_manager") { ...@@ -133,8 +133,12 @@ static_library("performance_manager") {
"service_worker_context_adapter.h", "service_worker_context_adapter.h",
"tab_helper_frame_node_source.cc", "tab_helper_frame_node_source.cc",
"tab_helper_frame_node_source.h", "tab_helper_frame_node_source.h",
"v8_memory/v8_context_tracker.cc",
"v8_memory/v8_context_tracker.h",
"v8_memory/v8_context_tracker_helpers.cc", "v8_memory/v8_context_tracker_helpers.cc",
"v8_memory/v8_context_tracker_helpers.h", "v8_memory/v8_context_tracker_helpers.h",
"v8_memory/v8_context_tracker_internal.cc",
"v8_memory/v8_context_tracker_internal.h",
"v8_memory/v8_context_tracker_types.cc", "v8_memory/v8_context_tracker_types.cc",
"v8_memory/v8_context_tracker_types.h", "v8_memory/v8_context_tracker_types.h",
"v8_memory/v8_per_frame_memory_decorator.cc", "v8_memory/v8_per_frame_memory_decorator.cc",
...@@ -228,6 +232,7 @@ source_set("unit_tests") { ...@@ -228,6 +232,7 @@ source_set("unit_tests") {
"performance_manager_unittest.cc", "performance_manager_unittest.cc",
"registered_objects_unittest.cc", "registered_objects_unittest.cc",
"v8_memory/v8_context_tracker_helpers_unittest.cc", "v8_memory/v8_context_tracker_helpers_unittest.cc",
"v8_memory/v8_context_tracker_internal_unittest.cc",
"v8_memory/v8_per_frame_memory_decorator_unittest.cc", "v8_memory/v8_per_frame_memory_decorator_unittest.cc",
"web_contents_proxy_unittest.cc", "web_contents_proxy_unittest.cc",
"worker_watcher_unittest.cc", "worker_watcher_unittest.cc",
......
...@@ -93,6 +93,12 @@ void ExecutionContextRegistryImpl::AddObserver( ...@@ -93,6 +93,12 @@ void ExecutionContextRegistryImpl::AddObserver(
observers_.AddObserver(observer); observers_.AddObserver(observer);
} }
bool ExecutionContextRegistryImpl::HasObserver(
ExecutionContextObserver* observer) const {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
return observers_.HasObserver(observer);
}
void ExecutionContextRegistryImpl::RemoveObserver( void ExecutionContextRegistryImpl::RemoveObserver(
ExecutionContextObserver* observer) { ExecutionContextObserver* observer) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
......
...@@ -38,6 +38,7 @@ class ExecutionContextRegistryImpl ...@@ -38,6 +38,7 @@ class ExecutionContextRegistryImpl
// ExecutionContextRegistry implementation: // ExecutionContextRegistry implementation:
void AddObserver(ExecutionContextObserver* observer) override; void AddObserver(ExecutionContextObserver* observer) override;
bool HasObserver(ExecutionContextObserver* observer) const override;
void RemoveObserver(ExecutionContextObserver* observer) override; void RemoveObserver(ExecutionContextObserver* observer) override;
const ExecutionContext* GetExecutionContextByToken( const ExecutionContext* GetExecutionContextByToken(
const blink::ExecutionContextToken& token) override; const blink::ExecutionContextToken& token) override;
......
...@@ -72,7 +72,9 @@ TEST_F(ExecutionContextRegistryImplTest, RegistryWorks) { ...@@ -72,7 +72,9 @@ TEST_F(ExecutionContextRegistryImplTest, RegistryWorks) {
// Create an observer. // Create an observer.
MockExecutionContextObserver obs; MockExecutionContextObserver obs;
EXPECT_FALSE(registry_->HasObserver(&obs));
registry_->AddObserver(&obs); registry_->AddObserver(&obs);
EXPECT_TRUE(registry_->HasObserver(&obs));
// Create some mock nodes. This creates a graph with 1 page containing 2 // Create some mock nodes. This creates a graph with 1 page containing 2
// frames in 1 process. // frames in 1 process.
......
...@@ -37,6 +37,9 @@ class ExecutionContextRegistry { ...@@ -37,6 +37,9 @@ class ExecutionContextRegistry {
// the registry is torn down. // the registry is torn down.
virtual void AddObserver(ExecutionContextObserver* observer) = 0; virtual void AddObserver(ExecutionContextObserver* observer) = 0;
// Determines if an observer is in the registry.
virtual bool HasObserver(ExecutionContextObserver* observer) const = 0;
// Removes an observer from the registry. // Removes an observer from the registry.
virtual void RemoveObserver(ExecutionContextObserver* observer) = 0; virtual void RemoveObserver(ExecutionContextObserver* observer) = 0;
......
// Copyright 2020 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/v8_memory/v8_context_tracker.h"
#include "base/values.h"
#include "components/performance_manager/public/execution_context/execution_context_registry.h"
#include "components/performance_manager/public/graph/node_data_describer_registry.h"
#include "components/performance_manager/v8_memory/v8_context_tracker_internal.h"
namespace performance_manager {
namespace v8_memory {
////////////////////////////////////////////////////////////////////////////////
// V8ContextTracker::ExecutionContextState implementation:
V8ContextTracker::ExecutionContextState::ExecutionContextState(
const blink::ExecutionContextToken& token,
const base::Optional<IframeAttributionData>& iframe_attribution_data)
: token(token), iframe_attribution_data(iframe_attribution_data) {}
V8ContextTracker::ExecutionContextState::~ExecutionContextState() = default;
////////////////////////////////////////////////////////////////////////////////
// V8ContextTracker::V8ContextState implementation:
V8ContextTracker::V8ContextState::V8ContextState(
const V8ContextDescription& description,
ExecutionContextState* execution_context_state)
: description(description),
execution_context_state(execution_context_state) {}
V8ContextTracker::V8ContextState::~V8ContextState() = default;
////////////////////////////////////////////////////////////////////////////////
// V8ContextTracker implementation:
V8ContextTracker::V8ContextTracker()
: data_store_(std::make_unique<DataStore>()) {}
V8ContextTracker::~V8ContextTracker() = default;
void V8ContextTracker::OnBeforeExecutionContextRemoved(
const execution_context::ExecutionContext* ec) {
DCHECK_ON_GRAPH_SEQUENCE(ec->GetGraph());
// TODO(chrisha): Implement me.
}
void V8ContextTracker::OnBeforeGraphDestroyed(Graph* graph) {
DCHECK_ON_GRAPH_SEQUENCE(graph);
// Remove ourselves from the execution context registry observer list here as
// it may get torn down before our OnTakenFromGraph is called. This is also
// called from "OnTakenFromGraph", so it is resistant to the
// ExecutionContextRegistry no longer existing.
auto* registry =
execution_context::ExecutionContextRegistry::GetFromGraph(graph);
if (registry && registry->HasObserver(this))
registry->RemoveObserver(this);
}
void V8ContextTracker::OnPassedToGraph(Graph* graph) {
DCHECK_ON_GRAPH_SEQUENCE(graph);
graph->AddGraphObserver(this);
graph->AddProcessNodeObserver(this);
graph->RegisterObject(this);
graph->GetNodeDataDescriberRegistry()->RegisterDescriber(this,
"V8ContextTracker");
auto* registry =
execution_context::ExecutionContextRegistry::GetFromGraph(graph);
// We expect the registry to exist before we are passed to the graph.
DCHECK(registry);
registry->AddObserver(this);
}
void V8ContextTracker::OnTakenFromGraph(Graph* graph) {
DCHECK_ON_GRAPH_SEQUENCE(graph);
// Call OnBeforeGraphDestroyed as well. This unregisters us from the
// ExecutionContextRegistry in case we're being removed from the graph
// prior to its destruction.
OnBeforeGraphDestroyed(graph);
graph->GetNodeDataDescriberRegistry()->UnregisterDescriber(this);
graph->UnregisterObject(this);
graph->RemoveProcessNodeObserver(this);
graph->RemoveGraphObserver(this);
}
base::Value V8ContextTracker::DescribeFrameNodeData(
const FrameNode* node) const {
DCHECK_ON_GRAPH_SEQUENCE(node->GetGraph());
base::Value dict(base::Value::Type::DICTIONARY);
// TODO(chrisha): Implement me.
return dict;
}
base::Value V8ContextTracker::DescribeProcessNodeData(
const ProcessNode* node) const {
DCHECK_ON_GRAPH_SEQUENCE(node->GetGraph());
base::Value dict(base::Value::Type::DICTIONARY);
// TODO(chrisha): Implement me.
return dict;
}
base::Value V8ContextTracker::DescribeWorkerNodeData(
const WorkerNode* node) const {
DCHECK_ON_GRAPH_SEQUENCE(node->GetGraph());
base::Value dict(base::Value::Type::DICTIONARY);
// TODO(chrisha): Implement me.
return dict;
}
void V8ContextTracker::OnBeforeProcessNodeRemoved(const ProcessNode* node) {
DCHECK_ON_GRAPH_SEQUENCE(node->GetGraph());
// TODO(chrisha): Implement me by deleting all knowledge of execution contexts
// and v8 contexts in this process!
}
} // namespace v8_memory
} // namespace performance_manager
// Copyright 2020 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_V8_MEMORY_V8_CONTEXT_TRACKER_H_
#define COMPONENTS_PERFORMANCE_MANAGER_V8_MEMORY_V8_CONTEXT_TRACKER_H_
#include <memory>
#include "base/optional.h"
#include "components/performance_manager/public/execution_context/execution_context.h"
#include "components/performance_manager/public/graph/graph.h"
#include "components/performance_manager/public/graph/graph_registered.h"
#include "components/performance_manager/public/graph/node_data_describer.h"
#include "components/performance_manager/public/graph/process_node.h"
#include "components/performance_manager/v8_memory/v8_context_tracker_types.h"
#include "third_party/blink/public/common/tokens/tokens.h"
namespace performance_manager {
namespace v8_memory {
// Forward declaration.
namespace internal {
class V8ContextTrackerDataStore;
} // namespace internal
// A class that tracks individual V8Contexts in renderers as they go through
// their lifecycle. This tracks information such as detached (leaked) contexts
// and remote frame attribution, for surfacing in the performance.measureMemory
// API. This information is tracked per-process in ProcessNode-attached data.
// The tracker lets you query a V8ContextToken and retrieve information about
// that context, including its iframe attributes and associated
// ExecutionContext.
//
// Note that this component relies on the ExecutionContextRegistry having been
// added to the Graph.
class V8ContextTracker
: public execution_context::ExecutionContextObserverDefaultImpl,
public GraphObserver,
public GraphOwned,
public GraphRegisteredImpl<V8ContextTracker>,
public NodeDataDescriberDefaultImpl,
public ProcessNode::ObserverDefaultImpl {
public:
using DataStore = internal::V8ContextTrackerDataStore;
// Data about an individual ExecutionContext. Note that this information can
// outlive the ExecutionContext itself, and in that case it stores information
// about the last known state of the ExecutionContext prior to it being
// torn down in a renderer.
struct ExecutionContextState {
ExecutionContextState() = delete;
ExecutionContextState(
const blink::ExecutionContextToken& token,
const base::Optional<IframeAttributionData>& iframe_attribution_data);
ExecutionContextState(const ExecutionContextState&) = delete;
ExecutionContextState& operator=(const ExecutionContextState&) = delete;
virtual ~ExecutionContextState();
// Returns the associated execution_context::ExecutionContext (which wraps
// the underlying FrameNode or WorkerNode associated with this context) *if*
// the node is available.
const execution_context::ExecutionContext* GetExecutionContext() const;
// The token identifying this context.
const blink::ExecutionContextToken token;
// The iframe attribution data most recently associated with this context.
// This is sometimes only available asynchronously so is optional. Note that
// this value can change over time, but will generally reflect the most up
// to date data (with some lag).
base::Optional<IframeAttributionData> iframe_attribution_data;
// Whether or not the corresponding blink::ExecutionContext has been
// destroyed. This occurs when the main V8Context associated with this
// execution context has itself become detached. This starts as false and
// can transition to true exactly once.
bool destroyed = false;
};
struct V8ContextState {
V8ContextState() = delete;
V8ContextState(const V8ContextDescription& description,
ExecutionContextState* execution_context_state);
V8ContextState(const V8ContextState&) = delete;
V8ContextState& operator=(const V8ContextState&) = delete;
virtual ~V8ContextState();
// The full description of this context.
const V8ContextDescription description;
// A pointer to the upstream ExecutionContextState that this V8Context is
// associated with. Note that this can be nullptr for V8Contexts that are
// not associated with an ExecutionContext.
ExecutionContextState* const execution_context_state;
// Whether or not this context is detached. A context becomes detached
// when the blink::ExecutionContext it was associated with is torn down.
// When a V8Context remains detached for a long time (is not collected by
// GC) it is effectively a leak (it is being kept alive by a stray
// cross-context reference). This starts as false and can transition to
// true exactly once.
bool detached = false;
};
V8ContextTracker();
~V8ContextTracker() final;
DataStore* data_store() const { return data_store_.get(); }
// Returns the ExecutionContextState for the given token, nullptr if none
// exists.
const ExecutionContextState* GetExecutionContextState(
const blink::ExecutionContextToken& token) const;
// Returns V8ContextState for the given token, nullptr if none exists.
const V8ContextState* GetV8ContextState(
const blink::V8ContextToken& token) const;
private:
// Implementation of execution_context::ExecutionContextObserverDefaultImpl.
void OnBeforeExecutionContextRemoved(
const execution_context::ExecutionContext* ec) final;
// Implementation of GraphObserver.
void OnBeforeGraphDestroyed(Graph* graph) final;
// Implementation of GraphOwned.
void OnPassedToGraph(Graph* graph) final;
void OnTakenFromGraph(Graph* graph) final;
// Implementation of NodeDataDescriber. We have things to say about
// execution contexts (frames and workers), as well as processes.
base::Value DescribeFrameNodeData(const FrameNode* node) const final;
base::Value DescribeProcessNodeData(const ProcessNode* node) const final;
base::Value DescribeWorkerNodeData(const WorkerNode* node) const final;
// Implementation of ProcessNode::ObserverDefaultImpl.
void OnBeforeProcessNodeRemoved(const ProcessNode* node) final;
// Stores Chrome-wide data store used by the tracking.
std::unique_ptr<DataStore> data_store_;
};
} // namespace v8_memory
} // namespace performance_manager
#endif // COMPONENTS_PERFORMANCE_MANAGER_V8_MEMORY_V8_CONTEXT_TRACKER_H_
// Copyright 2020 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/v8_memory/v8_context_tracker_internal.h"
#include "base/check.h"
namespace performance_manager {
namespace v8_memory {
namespace internal {
////////////////////////////////////////////////////////////////////////////////
// ExecutionContextData implementation:
ExecutionContextData::ExecutionContextData(
ProcessData* process_data,
const blink::ExecutionContextToken& token,
const base::Optional<IframeAttributionData> iframe_attribution_data)
: ExecutionContextState(token, iframe_attribution_data),
process_data_(process_data) {}
ExecutionContextData::~ExecutionContextData() {
DCHECK(!IsTracked());
DCHECK(ShouldDestroy());
}
bool ExecutionContextData::IsTracked() const {
return previous() || next();
}
bool ExecutionContextData::ShouldDestroy() const {
return v8_context_count_ == 0 && !remote_frame_data_;
}
void ExecutionContextData::SetRemoteFrameData(
util::PassKey<RemoteFrameData>,
RemoteFrameData* remote_frame_data) {
DCHECK(!remote_frame_data_);
DCHECK(remote_frame_data);
remote_frame_data_ = remote_frame_data;
}
bool ExecutionContextData::ClearRemoteFrameData(
util::PassKey<RemoteFrameData>) {
DCHECK(remote_frame_data_);
remote_frame_data_ = nullptr;
return ShouldDestroy();
}
void ExecutionContextData::IncrementV8ContextCount(
util::PassKey<V8ContextData>) {
++v8_context_count_;
}
bool ExecutionContextData::DecrementV8ContextCount(
util::PassKey<V8ContextData>) {
DCHECK_LT(0u, v8_context_count_);
--v8_context_count_;
return ShouldDestroy();
}
////////////////////////////////////////////////////////////////////////////////
// RemoteFrameData implementation:
RemoteFrameData::RemoteFrameData(ProcessData* process_data,
const blink::RemoteFrameToken& token,
ExecutionContextData* execution_context_data)
: process_data_(process_data),
token_(token),
execution_context_data_(execution_context_data) {
DCHECK(process_data);
DCHECK(execution_context_data);
// This and the ExecutionContext *must* be cross-process.
DCHECK_NE(process_data, execution_context_data->process_data());
execution_context_data->SetRemoteFrameData(PassKey(), this);
}
RemoteFrameData::~RemoteFrameData() {
DCHECK(!IsTracked());
// If this is the last reference keeping alive a tracked ExecutionContextData,
// then clean it up as well. Untracked ExecutionContextDatas will go out of
// scope on their own.
if (execution_context_data_->ClearRemoteFrameData(PassKey()) &&
execution_context_data_->IsTracked()) {
process_data_->data_store()->Destroy(execution_context_data_->GetToken());
}
}
bool RemoteFrameData::IsTracked() const {
return previous() || next();
}
////////////////////////////////////////////////////////////////////////////////
// V8ContextData implementation:
V8ContextData::V8ContextData(ProcessData* process_data,
const V8ContextDescription& description,
ExecutionContextData* execution_context_data)
: V8ContextState(description, execution_context_data),
process_data_(process_data) {
DCHECK(process_data);
if (execution_context_data) {
// These must be same process.
DCHECK_EQ(process_data, execution_context_data->process_data());
execution_context_data->IncrementV8ContextCount(PassKey());
}
}
V8ContextData::~V8ContextData() {
DCHECK(!IsTracked());
// If this is the last reference keeping alive a tracked ExecutionContextData,
// then clean it up as well. Untracked ExecutionContextDatas will go out of
// scope on their own.
if (auto* ecd = GetExecutionContextData()) {
if (ecd->DecrementV8ContextCount(PassKey()) && ecd->IsTracked())
process_data_->data_store()->Destroy(ecd->GetToken());
}
}
bool V8ContextData::IsTracked() const {
return previous() || next();
}
ExecutionContextData* V8ContextData::GetExecutionContextData() const {
return static_cast<ExecutionContextData*>(execution_context_state);
}
////////////////////////////////////////////////////////////////////////////////
// ProcessData implementation:
ProcessData::ProcessData(const ProcessNodeImpl* process_node)
: data_store_(GetDataStore(process_node)) {}
ProcessData::~ProcessData() {
DCHECK(execution_context_datas_.empty());
DCHECK(remote_frame_datas_.empty());
DCHECK(v8_context_datas_.empty());
}
void ProcessData::TearDown() {
// First, remove any RemoteFrameData references owned by this ProcessData
// that are keeping alive ExecutionContextDatas in other ProcessDatas. This
// can cause ExecutionContextDatas to be torn down.
while (!remote_frame_datas_.empty()) {
auto* node = remote_frame_datas_.head();
data_store_->Destroy(node->value()->GetToken());
}
// Drain the list of V8ContextTokens. This will also indirectly clean up and
// ExecutionContextDatas that are only being kept alive by V8ContextData
// references.
while (!v8_context_datas_.empty()) {
auto* node = v8_context_datas_.head();
data_store_->Destroy(node->value()->GetToken());
}
// Any ExecutionContextDatas still alive are only being kept alive because of
// RemoteFrameData references from another ProcessData. Clean those up.
while (!execution_context_datas_.empty()) {
auto* node = execution_context_datas_.head();
auto* ec_data = node->value();
auto* rfd = ec_data->remote_frame_data();
DCHECK(rfd);
DCHECK_EQ(0u, ec_data->v8_context_count());
data_store_->Destroy(rfd->GetToken());
}
// We now expect everything to have been cleaned up.
DCHECK(execution_context_datas_.empty());
DCHECK(remote_frame_datas_.empty());
DCHECK(v8_context_datas_.empty());
}
void ProcessData::Add(util::PassKey<V8ContextTrackerDataStore>,
ExecutionContextData* ec_data) {
DCHECK(ec_data);
DCHECK_EQ(this, ec_data->process_data());
DCHECK(!ec_data->ShouldDestroy());
DCHECK(!ec_data->IsTracked());
execution_context_datas_.Append(ec_data);
}
void ProcessData::Add(util::PassKey<V8ContextTrackerDataStore>,
RemoteFrameData* rf_data) {
DCHECK(rf_data);
DCHECK_EQ(this, rf_data->process_data());
DCHECK(!rf_data->IsTracked());
remote_frame_datas_.Append(rf_data);
}
void ProcessData::Add(util::PassKey<V8ContextTrackerDataStore>,
V8ContextData* v8_data) {
DCHECK(v8_data);
DCHECK_EQ(this, v8_data->process_data());
DCHECK(!v8_data->IsTracked());
v8_context_datas_.Append(v8_data);
}
void ProcessData::Remove(util::PassKey<V8ContextTrackerDataStore>,
ExecutionContextData* ec_data) {
DCHECK(ec_data);
DCHECK_EQ(this, ec_data->process_data());
DCHECK(ec_data->IsTracked());
DCHECK(ec_data->ShouldDestroy());
ec_data->RemoveFromList();
}
void ProcessData::Remove(util::PassKey<V8ContextTrackerDataStore>,
RemoteFrameData* rf_data) {
DCHECK(rf_data);
DCHECK_EQ(this, rf_data->process_data());
DCHECK(rf_data->IsTracked());
rf_data->RemoveFromList();
}
void ProcessData::Remove(util::PassKey<V8ContextTrackerDataStore>,
V8ContextData* v8_data) {
DCHECK(v8_data);
DCHECK_EQ(this, v8_data->process_data());
DCHECK(v8_data->IsTracked());
v8_data->RemoveFromList();
}
////////////////////////////////////////////////////////////////////////////////
// V8ContextTrackerDataStore implementation:
V8ContextTrackerDataStore::V8ContextTrackerDataStore() = default;
V8ContextTrackerDataStore::~V8ContextTrackerDataStore() {
DCHECK(global_execution_context_datas_.empty());
DCHECK(global_remote_frame_datas_.empty());
DCHECK(global_v8_context_datas_.empty());
}
void V8ContextTrackerDataStore::Pass(
std::unique_ptr<ExecutionContextData> ec_data) {
DCHECK(ec_data.get());
ec_data->process_data()->Add(PassKey(), ec_data.get());
auto result = global_execution_context_datas_.insert(std::move(ec_data));
DCHECK(result.second);
}
void V8ContextTrackerDataStore::Pass(std::unique_ptr<RemoteFrameData> rf_data) {
DCHECK(rf_data.get());
rf_data->process_data()->Add(PassKey(), rf_data.get());
auto result = global_remote_frame_datas_.insert(std::move(rf_data));
DCHECK(result.second);
}
void V8ContextTrackerDataStore::Pass(std::unique_ptr<V8ContextData> v8_data) {
DCHECK(v8_data.get());
v8_data->process_data()->Add(PassKey(), v8_data.get());
auto result = global_v8_context_datas_.insert(std::move(v8_data));
DCHECK(result.second);
}
ExecutionContextData* V8ContextTrackerDataStore::Get(
const blink::ExecutionContextToken& token) {
auto it = global_execution_context_datas_.find(token);
if (it == global_execution_context_datas_.end())
return nullptr;
return it->get();
}
RemoteFrameData* V8ContextTrackerDataStore::Get(
const blink::RemoteFrameToken& token) {
auto it = global_remote_frame_datas_.find(token);
if (it == global_remote_frame_datas_.end())
return nullptr;
return it->get();
}
V8ContextData* V8ContextTrackerDataStore::Get(
const blink::V8ContextToken& token) {
auto it = global_v8_context_datas_.find(token);
if (it == global_v8_context_datas_.end())
return nullptr;
return it->get();
}
void V8ContextTrackerDataStore::Destroy(
const blink::ExecutionContextToken& token) {
auto it = global_execution_context_datas_.find(token);
DCHECK(it != global_execution_context_datas_.end());
auto* ec_data = it->get();
ec_data->process_data()->Remove(PassKey(), ec_data);
global_execution_context_datas_.erase(it);
}
void V8ContextTrackerDataStore::Destroy(const blink::RemoteFrameToken& token) {
auto it = global_remote_frame_datas_.find(token);
DCHECK(it != global_remote_frame_datas_.end());
auto* rf_data = it->get();
rf_data->process_data()->Remove(PassKey(), rf_data);
global_remote_frame_datas_.erase(it);
}
void V8ContextTrackerDataStore::Destroy(const blink::V8ContextToken& token) {
auto it = global_v8_context_datas_.find(token);
DCHECK(it != global_v8_context_datas_.end());
auto* v8_data = it->get();
v8_data->process_data()->Remove(PassKey(), v8_data);
global_v8_context_datas_.erase(it);
}
} // namespace internal
} // namespace v8_memory
} // namespace performance_manager
// Copyright 2020 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.
// Internal data structures used by V8ContextTracker. These are only exposed in
// a header for testing. Everything in this header lives in an "internal"
// namespace so as not to pollute the "v8_memory", which houses the actual
// consumer API.
#ifndef COMPONENTS_PERFORMANCE_MANAGER_V8_MEMORY_V8_CONTEXT_TRACKER_INTERNAL_H_
#define COMPONENTS_PERFORMANCE_MANAGER_V8_MEMORY_V8_CONTEXT_TRACKER_INTERNAL_H_
#include <memory>
#include <set>
#include "base/compiler_specific.h"
#include "base/containers/linked_list.h"
#include "base/optional.h"
#include "base/util/type_safety/pass_key.h"
#include "components/performance_manager/graph/node_attached_data_impl.h"
#include "components/performance_manager/graph/process_node_impl.h"
#include "components/performance_manager/v8_memory/v8_context_tracker.h"
#include "components/performance_manager/v8_memory/v8_context_tracker_types.h"
#include "third_party/blink/public/common/tokens/tokens.h"
namespace performance_manager {
namespace v8_memory {
namespace internal {
using ExecutionContextState = V8ContextTracker::ExecutionContextState;
using V8ContextState = V8ContextTracker::V8ContextState;
// Forward declarations.
class ExecutionContextData;
class ProcessData;
class RemoteFrameData;
class V8ContextData;
// A comparator for "Data" objects that compares by token.
template <typename DataType, typename TokenType>
struct TokenComparator {
using is_transparent = int;
static const TokenType& GetToken(const TokenType& token) { return token; }
static const TokenType& GetToken(const std::unique_ptr<DataType>& data) {
return data->GetToken();
}
template <typename Type1, typename Type2>
bool operator()(const Type1& obj1, const Type2& obj2) const {
return GetToken(obj1) < GetToken(obj2);
}
};
////////////////////////////////////////////////////////////////////////////////
// ExecutionContextData declaration:
// Internal wrapper of ExecutionContextState. Augments with additional data
// needed for the implementation. Since these objects also need to be tracked
// per-process, they are kept in a process-associated doubly-linked list.
class ExecutionContextData : public base::LinkNode<ExecutionContextData>,
public ExecutionContextState {
public:
using Comparator =
TokenComparator<ExecutionContextData, blink::ExecutionContextToken>;
ExecutionContextData() = delete;
ExecutionContextData(const ExecutionContextData&) = delete;
ExecutionContextData(
ProcessData* process_data,
const blink::ExecutionContextToken& token,
const base::Optional<IframeAttributionData> iframe_attribution_data);
ExecutionContextData& operator=(const ExecutionContextData&) = delete;
~ExecutionContextData() override;
// Simple accessors.
ProcessData* process_data() const { return process_data_; }
RemoteFrameData* remote_frame_data() { return remote_frame_data_; }
size_t v8_context_count() const { return v8_context_count_; }
// For consistency, all Data objects have a GetToken() function.
const blink::ExecutionContextToken& GetToken() const { return token; }
// Returns true if this object is currently being tracked (it is in
// ProcessData::execution_context_datas_, and
// V8ContextTrackerDataStore::global_execution_context_datas_).
WARN_UNUSED_RESULT bool IsTracked() const;
// Returns true if this object *should* be destroyed (there are no references
// to it keeping it alive).
WARN_UNUSED_RESULT bool ShouldDestroy() const;
// Manages remote frame data associated with this ExecutionContextData.
void SetRemoteFrameData(util::PassKey<RemoteFrameData>,
RemoteFrameData* remote_frame_data);
WARN_UNUSED_RESULT bool ClearRemoteFrameData(util::PassKey<RemoteFrameData>);
// Increments |v8_context_count_|.
void IncrementV8ContextCount(util::PassKey<V8ContextData>);
// Decrements |v8_context_count_|, and returns true if the object has
// transitioned to "ShouldDestroy".
WARN_UNUSED_RESULT bool DecrementV8ContextCount(util::PassKey<V8ContextData>);
private:
ProcessData* const process_data_;
RemoteFrameData* remote_frame_data_ = nullptr;
// The count of V8ContextDatas keeping this object alive.
size_t v8_context_count_ = 0;
};
////////////////////////////////////////////////////////////////////////////////
// RemoteFrameData declaration:
// Represents data about an ExecutionCOntext from the point of view of the
// parent frame that owns it.
class RemoteFrameData : public base::LinkNode<RemoteFrameData> {
public:
using Comparator = TokenComparator<RemoteFrameData, blink::RemoteFrameToken>;
using PassKey = util::PassKey<RemoteFrameData>;
RemoteFrameData() = delete;
RemoteFrameData(ProcessData* process_data,
const blink::RemoteFrameToken& token,
ExecutionContextData* execution_context_data);
RemoteFrameData(const RemoteFrameData&) = delete;
RemoteFrameData& operator=(const RemoteFrameData&) = delete;
~RemoteFrameData();
// Simple accessors.
ProcessData* process_data() const { return process_data_; }
ExecutionContextData* execution_context_data() const {
return execution_context_data_;
}
// For consistency, all Data objects have a GetToken() function.
const blink::RemoteFrameToken& GetToken() const { return token_; }
// Returns true if this object is currently being tracked (it is in
// ProcessData::remote_frame_datas_, and
// V8ContextTrackerDataStore::global_remote_frame_datas_).
WARN_UNUSED_RESULT bool IsTracked() const;
private:
ProcessData* const process_data_;
const blink::RemoteFrameToken token_;
ExecutionContextData* const execution_context_data_;
};
////////////////////////////////////////////////////////////////////////////////
// V8ContextData declaration:
// Internal wrapper of V8ContextState. Augments with additional data needed for
// the implementation.
class V8ContextData : public base::LinkNode<V8ContextData>,
public V8ContextState {
public:
using Comparator = TokenComparator<V8ContextData, blink::V8ContextToken>;
using PassKey = util::PassKey<V8ContextData>;
V8ContextData() = delete;
V8ContextData(ProcessData* process_data,
const V8ContextDescription& description,
ExecutionContextData* execution_context_data);
V8ContextData(const V8ContextData&) = delete;
V8ContextData& operator=(const V8ContextData&) = delete;
~V8ContextData() override;
// Simple accessors.
ProcessData* process_data() const { return process_data_; }
// For consistency, all Data objects have a GetToken() function.
const blink::V8ContextToken& GetToken() const { return description.token; }
// Returns true if this object is currently being tracked (its in
// ProcessData::v8_context_datas_, and
// V8ContextTrackerDataStore::global_v8_context_datas_).
WARN_UNUSED_RESULT bool IsTracked() const;
// Returns the ExecutionContextData associated with this V8ContextData.
ExecutionContextData* GetExecutionContextData() const;
private:
ProcessData* const process_data_;
};
////////////////////////////////////////////////////////////////////////////////
// ProcessData declaration:
class ProcessData : public NodeAttachedDataImpl<ProcessData> {
public:
struct Traits : public NodeAttachedDataInMap<ProcessNodeImpl> {};
explicit ProcessData(const ProcessNodeImpl* process_node);
~ProcessData() override;
// Simple accessors.
V8ContextTrackerDataStore* data_store() const { return data_store_; }
// Tears down this ProcessData by ensuring that all associated
// ExecutionContextDatas and V8ContextDatas are cleaned up. This must be
// called *prior* to the destructor being invoked.
void TearDown();
// Adds the provided object to the list of process-associated objects. The
// object must not be part of a list, its process data must match this one,
// and it must return false for "ShouldDestroy" (if applicable). For removal,
// the object must be part of a list, the process data must match this one and
// "ShouldDestroy" must return false.
void Add(util::PassKey<V8ContextTrackerDataStore>,
ExecutionContextData* ec_data);
void Add(util::PassKey<V8ContextTrackerDataStore>, RemoteFrameData* rf_data);
void Add(util::PassKey<V8ContextTrackerDataStore>, V8ContextData* v8_data);
void Remove(util::PassKey<V8ContextTrackerDataStore>,
ExecutionContextData* ec_data);
void Remove(util::PassKey<V8ContextTrackerDataStore>,
RemoteFrameData* rf_data);
void Remove(util::PassKey<V8ContextTrackerDataStore>, V8ContextData* v8_data);
private:
// Used to initialize |data_store_| at construction.
static V8ContextTrackerDataStore* GetDataStore(
const ProcessNodeImpl* process_node) {
return V8ContextTracker::GetFromGraph(process_node->graph())->data_store();
}
// Pointer to the DataStore that implicitly owns us.
V8ContextTrackerDataStore* const data_store_;
// List of ExecutionContextDatas associated with this process.
base::LinkedList<ExecutionContextData> execution_context_datas_;
// List of RemoteFrameDatas associated with this process.
base::LinkedList<RemoteFrameData> remote_frame_datas_;
// List of V8ContextDatas associated with this process.
base::LinkedList<V8ContextData> v8_context_datas_;
};
////////////////////////////////////////////////////////////////////////////////
// V8ContextTrackerDataStore declaration:
// This class acts as the owner of all tracked objects. Objects are created
// in isolation, and ownership passed to this object. Management of all
// per-process lists is centralized through this object.
class V8ContextTrackerDataStore {
public:
using PassKey = util::PassKey<V8ContextTrackerDataStore>;
V8ContextTrackerDataStore();
~V8ContextTrackerDataStore();
// Passes ownership of an object. An object with the same token must not
// already exist ("Get" should return nullptr). Note that when passing an
// |ec_data| to the impl that "ShouldDestroy" should return false.
void Pass(std::unique_ptr<ExecutionContextData> ec_data);
void Pass(std::unique_ptr<RemoteFrameData> rf_data);
void Pass(std::unique_ptr<V8ContextData> v8_data);
// Looks up owned objects by token.
ExecutionContextData* Get(const blink::ExecutionContextToken& token);
RemoteFrameData* Get(const blink::RemoteFrameToken& token);
V8ContextData* Get(const blink::V8ContextToken& token);
// Destroys objects by token. They must exist ("Get" should return non
// nullptr).
void Destroy(const blink::ExecutionContextToken& token);
void Destroy(const blink::RemoteFrameToken& token);
void Destroy(const blink::V8ContextToken& token);
size_t GetExecutionContextDataCount() const {
return global_execution_context_datas_.size();
}
size_t GetRemoteFrameDataCount() const {
return global_remote_frame_datas_.size();
}
size_t GetV8ContextDataCount() const {
return global_v8_context_datas_.size();
}
private:
// Browser wide registry of ExecutionContextData objects.
std::set<std::unique_ptr<ExecutionContextData>,
ExecutionContextData::Comparator>
global_execution_context_datas_;
// Browser-wide registry of RemoteFrameData objects.
std::set<std::unique_ptr<RemoteFrameData>, RemoteFrameData::Comparator>
global_remote_frame_datas_;
// Browser wide registry of V8ContextData objects.
std::set<std::unique_ptr<V8ContextData>, V8ContextData::Comparator>
global_v8_context_datas_;
};
} // namespace internal
} // namespace v8_memory
} // namespace performance_manager
#endif // COMPONENTS_PERFORMANCE_MANAGER_V8_MEMORY_V8_CONTEXT_TRACKER_INTERNAL_H_
// Copyright 2020 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/v8_memory/v8_context_tracker_internal.h"
#include <memory>
#include "base/optional.h"
#include "base/test/gtest_util.h"
#include "components/performance_manager/execution_context/execution_context_registry_impl.h"
#include "components/performance_manager/test_support/graph_test_harness.h"
#include "components/performance_manager/test_support/mock_graphs.h"
#include "components/performance_manager/v8_memory/v8_context_tracker.h"
#include "components/performance_manager/v8_memory/v8_context_tracker_types.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/blink/public/common/tokens/tokens.h"
namespace performance_manager {
namespace v8_memory {
namespace internal {
namespace {
class V8ContextTrackerInternalTest : public GraphTestHarness {
public:
V8ContextTrackerInternalTest()
: registry_(graph()->PassToGraph(
std::make_unique<
execution_context::ExecutionContextRegistryImpl>())),
tracker_(graph()->PassToGraph(std::make_unique<V8ContextTracker>())),
mock_graph_(graph()) {}
~V8ContextTrackerInternalTest() override = default;
V8ContextTrackerDataStore* data_store() const {
return tracker_->data_store();
}
execution_context::ExecutionContextRegistry* const registry_ = nullptr;
V8ContextTracker* const tracker_ = nullptr;
MockSinglePageWithMultipleProcessesGraph mock_graph_;
};
using V8ContextTrackerInternalDeathTest = V8ContextTrackerInternalTest;
} // namespace
TEST_F(V8ContextTrackerInternalDeathTest,
PassingUnreferencedExecutionContextDataFails) {
auto* process_data = ProcessData::GetOrCreate(
static_cast<ProcessNodeImpl*>(mock_graph_.process.get()));
std::unique_ptr<ExecutionContextData> ec_data =
std::make_unique<ExecutionContextData>(
process_data, mock_graph_.frame->frame_token(), base::nullopt);
EXPECT_TRUE(ec_data->ShouldDestroy());
EXPECT_DCHECK_DEATH(data_store()->Pass(std::move(ec_data)));
}
TEST_F(V8ContextTrackerInternalDeathTest, SameProcessRemoteFrameDataExplodes) {
auto* process_data = ProcessData::GetOrCreate(
static_cast<ProcessNodeImpl*>(mock_graph_.process.get()));
std::unique_ptr<ExecutionContextData> ec_data =
std::make_unique<ExecutionContextData>(
process_data, mock_graph_.frame->frame_token(), base::nullopt);
std::unique_ptr<RemoteFrameData> rf_data;
EXPECT_DCHECK_DEATH(
rf_data = std::make_unique<RemoteFrameData>(
process_data, blink::RemoteFrameToken(), ec_data.get()));
}
TEST_F(V8ContextTrackerInternalDeathTest, CrossProcessV8ContextDataExplodes) {
auto* process_data = ProcessData::GetOrCreate(
static_cast<ProcessNodeImpl*>(mock_graph_.process.get()));
auto* other_process_data = ProcessData::GetOrCreate(
static_cast<ProcessNodeImpl*>(mock_graph_.other_process.get()));
std::unique_ptr<ExecutionContextData> ec_data =
std::make_unique<ExecutionContextData>(
process_data, mock_graph_.frame->frame_token(), base::nullopt);
std::unique_ptr<V8ContextData> v8_data;
EXPECT_DCHECK_DEATH(
v8_data = std::make_unique<V8ContextData>(
other_process_data, V8ContextDescription(), ec_data.get()));
}
TEST_F(V8ContextTrackerInternalTest, ExecutionContextDataShouldDestroy) {
auto* process_data = ProcessData::GetOrCreate(
static_cast<ProcessNodeImpl*>(mock_graph_.process.get()));
// With no references "ShouldDestroy" should return true.
std::unique_ptr<ExecutionContextData> ec_data =
std::make_unique<ExecutionContextData>(
process_data, mock_graph_.frame->frame_token(), base::nullopt);
EXPECT_FALSE(ec_data->remote_frame_data());
EXPECT_EQ(0u, ec_data->v8_context_count());
EXPECT_TRUE(ec_data->ShouldDestroy());
// Adding a RemoteFrameData reference should mark "ShouldDestroy" as false.
auto* other_process_data = ProcessData::GetOrCreate(
static_cast<ProcessNodeImpl*>(mock_graph_.other_process.get()));
std::unique_ptr<RemoteFrameData> rf_data = std::make_unique<RemoteFrameData>(
other_process_data, blink::RemoteFrameToken(), ec_data.get());
EXPECT_TRUE(ec_data->remote_frame_data());
EXPECT_EQ(0u, ec_data->v8_context_count());
EXPECT_FALSE(ec_data->ShouldDestroy());
// Adding a V8ContextData should also keep the object alive.
std::unique_ptr<V8ContextData> v8_data1 = std::make_unique<V8ContextData>(
process_data, V8ContextDescription(), ec_data.get());
EXPECT_TRUE(ec_data->remote_frame_data());
EXPECT_EQ(1u, ec_data->v8_context_count());
EXPECT_FALSE(ec_data->ShouldDestroy());
// Add another V8ContextData.
std::unique_ptr<V8ContextData> v8_data2 = std::make_unique<V8ContextData>(
process_data, V8ContextDescription(), ec_data.get());
EXPECT_TRUE(ec_data->remote_frame_data());
EXPECT_EQ(2u, ec_data->v8_context_count());
EXPECT_FALSE(ec_data->ShouldDestroy());
// Destroy one of the V8ContextDatas.
v8_data1.reset();
EXPECT_TRUE(ec_data->remote_frame_data());
EXPECT_EQ(1u, ec_data->v8_context_count());
EXPECT_FALSE(ec_data->ShouldDestroy());
// Destroy the RemoteFrameData.
rf_data.reset();
EXPECT_FALSE(ec_data->remote_frame_data());
EXPECT_EQ(1u, ec_data->v8_context_count());
EXPECT_FALSE(ec_data->ShouldDestroy());
// Destroy the last V8COntextData.
v8_data2.reset();
EXPECT_FALSE(ec_data->remote_frame_data());
EXPECT_EQ(0u, ec_data->v8_context_count());
EXPECT_TRUE(ec_data->ShouldDestroy());
}
TEST_F(V8ContextTrackerInternalTest,
ExecutionContextDataTornDownByRemoteFrameData) {
auto* process_data = ProcessData::GetOrCreate(
static_cast<ProcessNodeImpl*>(mock_graph_.process.get()));
// Create an ExecutionContextData.
std::unique_ptr<ExecutionContextData> ec_data =
std::make_unique<ExecutionContextData>(
process_data, mock_graph_.frame->frame_token(), base::nullopt);
auto* raw_ec_data = ec_data.get();
EXPECT_FALSE(ec_data->IsTracked());
// Create a RemoteFrameData.
auto* other_process_data = ProcessData::GetOrCreate(
static_cast<ProcessNodeImpl*>(mock_graph_.other_process.get()));
std::unique_ptr<RemoteFrameData> rf_data = std::make_unique<RemoteFrameData>(
other_process_data, blink::RemoteFrameToken(), ec_data.get());
auto* raw_rf_data = rf_data.get();
EXPECT_FALSE(rf_data->IsTracked());
// Pass both of these to the Impl.
data_store()->Pass(std::move(ec_data));
data_store()->Pass(std::move(rf_data));
EXPECT_TRUE(raw_ec_data->IsTracked());
EXPECT_TRUE(raw_rf_data->IsTracked());
EXPECT_EQ(1u, data_store()->GetExecutionContextDataCount());
EXPECT_EQ(1u, data_store()->GetRemoteFrameDataCount());
// Ensure that lookup works.
auto ec_token = raw_ec_data->GetToken();
auto rf_token = raw_rf_data->GetToken();
EXPECT_EQ(raw_ec_data, data_store()->Get(ec_token));
EXPECT_EQ(raw_rf_data, data_store()->Get(rf_token));
// Delete the RemoteFrameData, and also expect the ExecutionContextData to
// have been cleaned up.
data_store()->Destroy(rf_token);
EXPECT_EQ(nullptr, data_store()->Get(ec_token));
EXPECT_EQ(nullptr, data_store()->Get(rf_token));
EXPECT_EQ(0u, data_store()->GetExecutionContextDataCount());
EXPECT_EQ(0u, data_store()->GetRemoteFrameDataCount());
}
TEST_F(V8ContextTrackerInternalTest,
ExecutionContextDataTornDownByV8ContextData) {
auto* process_data = ProcessData::GetOrCreate(
static_cast<ProcessNodeImpl*>(mock_graph_.process.get()));
// Create an ExecutionContextData.
std::unique_ptr<ExecutionContextData> ec_data =
std::make_unique<ExecutionContextData>(
process_data, mock_graph_.frame->frame_token(), base::nullopt);
auto* raw_ec_data = ec_data.get();
EXPECT_FALSE(ec_data->IsTracked());
// Create a V8ContextData.
std::unique_ptr<V8ContextData> v8_data = std::make_unique<V8ContextData>(
process_data, V8ContextDescription(), ec_data.get());
auto* raw_v8_data = v8_data.get();
EXPECT_FALSE(v8_data->IsTracked());
// Pass both of these to the Impl.
data_store()->Pass(std::move(ec_data));
data_store()->Pass(std::move(v8_data));
EXPECT_TRUE(raw_ec_data->IsTracked());
EXPECT_TRUE(raw_v8_data->IsTracked());
EXPECT_EQ(1u, data_store()->GetExecutionContextDataCount());
EXPECT_EQ(1u, data_store()->GetV8ContextDataCount());
// Ensure that lookup works.
auto ec_token = raw_ec_data->GetToken();
auto v8_token = raw_v8_data->GetToken();
EXPECT_EQ(raw_ec_data, data_store()->Get(ec_token));
EXPECT_EQ(raw_v8_data, data_store()->Get(v8_token));
// Delete the V8ContextData, and also expect the ExecutionContextData to
// have been cleaned up.
data_store()->Destroy(v8_token);
EXPECT_EQ(nullptr, data_store()->Get(ec_token));
EXPECT_EQ(nullptr, data_store()->Get(v8_token));
EXPECT_EQ(0u, data_store()->GetExecutionContextDataCount());
EXPECT_EQ(0u, data_store()->GetV8ContextDataCount());
}
namespace {
class V8ContextTrackerInternalTearDownOrderTest
: public V8ContextTrackerInternalTest {
public:
using Super = V8ContextTrackerInternalTest;
void SetUp() override {
Super::SetUp();
process_data_ = ProcessData::GetOrCreate(
static_cast<ProcessNodeImpl*>(mock_graph_.process.get()));
other_process_data_ = ProcessData::GetOrCreate(
static_cast<ProcessNodeImpl*>(mock_graph_.other_process.get()));
EXPECT_EQ(0u, data_store()->GetExecutionContextDataCount());
EXPECT_EQ(0u, data_store()->GetRemoteFrameDataCount());
EXPECT_EQ(0u, data_store()->GetV8ContextDataCount());
// Create an ExecutionContextData.
std::unique_ptr<ExecutionContextData> ec_data =
std::make_unique<ExecutionContextData>(
process_data_, mock_graph_.frame->frame_token(), base::nullopt);
ec_data_ = ec_data.get();
// Create a RemoteFrameData.
std::unique_ptr<RemoteFrameData> rf_data =
std::make_unique<RemoteFrameData>(other_process_data_,
blink::RemoteFrameToken(), ec_data_);
// Pass these to the tracker_.
data_store()->Pass(std::move(ec_data));
data_store()->Pass(std::move(rf_data));
// Create a couple V8ContextDatas.
std::unique_ptr<V8ContextData> v8_data = std::make_unique<V8ContextData>(
process_data_, V8ContextDescription(), ec_data_);
data_store()->Pass(std::move(v8_data));
v8_data = std::make_unique<V8ContextData>(process_data_,
V8ContextDescription(), ec_data_);
data_store()->Pass(std::move(v8_data));
EXPECT_EQ(1u, data_store()->GetExecutionContextDataCount());
EXPECT_EQ(1u, data_store()->GetRemoteFrameDataCount());
EXPECT_EQ(2u, data_store()->GetV8ContextDataCount());
}
ProcessData* process_data_ = nullptr;
ProcessData* other_process_data_ = nullptr;
ExecutionContextData* ec_data_ = nullptr;
};
} // namespace
TEST_F(V8ContextTrackerInternalTearDownOrderTest, RemoteBeforeLocal) {
// Tear down the |other_process| which has "RemoteFrame" entries.
other_process_data_->TearDown();
EXPECT_EQ(1u, data_store()->GetExecutionContextDataCount());
EXPECT_EQ(0u, data_store()->GetRemoteFrameDataCount());
EXPECT_EQ(2u, data_store()->GetV8ContextDataCount());
EXPECT_FALSE(ec_data_->remote_frame_data());
// Now tear down the main |process|.
process_data_->TearDown();
EXPECT_EQ(0u, data_store()->GetExecutionContextDataCount());
EXPECT_EQ(0u, data_store()->GetRemoteFrameDataCount());
EXPECT_EQ(0u, data_store()->GetV8ContextDataCount());
}
TEST_F(V8ContextTrackerInternalTearDownOrderTest, LocalBeforeRemote) {
// Tear down the main |process|. This should tear down everything.
process_data_->TearDown();
EXPECT_EQ(0u, data_store()->GetExecutionContextDataCount());
EXPECT_EQ(0u, data_store()->GetRemoteFrameDataCount());
EXPECT_EQ(0u, data_store()->GetV8ContextDataCount());
// Tearing down the |other_process| should do nothing.
other_process_data_->TearDown();
EXPECT_EQ(0u, data_store()->GetExecutionContextDataCount());
EXPECT_EQ(0u, data_store()->GetRemoteFrameDataCount());
EXPECT_EQ(0u, data_store()->GetV8ContextDataCount());
}
} // namespace internal
} // namespace v8_memory
} // namespace performance_manager
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