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") {
"service_worker_context_adapter.h",
"tab_helper_frame_node_source.cc",
"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.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.h",
"v8_memory/v8_per_frame_memory_decorator.cc",
......@@ -228,6 +232,7 @@ source_set("unit_tests") {
"performance_manager_unittest.cc",
"registered_objects_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",
"web_contents_proxy_unittest.cc",
"worker_watcher_unittest.cc",
......
......@@ -93,6 +93,12 @@ void ExecutionContextRegistryImpl::AddObserver(
observers_.AddObserver(observer);
}
bool ExecutionContextRegistryImpl::HasObserver(
ExecutionContextObserver* observer) const {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
return observers_.HasObserver(observer);
}
void ExecutionContextRegistryImpl::RemoveObserver(
ExecutionContextObserver* observer) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
......
......@@ -38,6 +38,7 @@ class ExecutionContextRegistryImpl
// ExecutionContextRegistry implementation:
void AddObserver(ExecutionContextObserver* observer) override;
bool HasObserver(ExecutionContextObserver* observer) const override;
void RemoveObserver(ExecutionContextObserver* observer) override;
const ExecutionContext* GetExecutionContextByToken(
const blink::ExecutionContextToken& token) override;
......
......@@ -72,7 +72,9 @@ TEST_F(ExecutionContextRegistryImplTest, RegistryWorks) {
// Create an observer.
MockExecutionContextObserver obs;
EXPECT_FALSE(registry_->HasObserver(&obs));
registry_->AddObserver(&obs);
EXPECT_TRUE(registry_->HasObserver(&obs));
// Create some mock nodes. This creates a graph with 1 page containing 2
// frames in 1 process.
......
......@@ -37,6 +37,9 @@ class ExecutionContextRegistry {
// the registry is torn down.
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.
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_
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