123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415 |
- // 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_memory_test_helpers.h"
- #include <utility>
- #include "base/bind.h"
- #include "base/callback.h"
- #include "base/check.h"
- #include "base/command_line.h"
- #include "base/feature_list.h"
- #include "base/run_loop.h"
- #include "base/test/bind.h"
- #include "base/test/task_environment.h"
- #include "components/performance_manager/embedder/graph_features.h"
- #include "components/performance_manager/graph/frame_node_impl.h"
- #include "components/performance_manager/graph/page_node_impl.h"
- #include "components/performance_manager/graph/process_node_impl.h"
- #include "components/performance_manager/public/features.h"
- #include "components/performance_manager/public/mojom/v8_contexts.mojom.h"
- #include "components/performance_manager/public/performance_manager.h"
- #include "components/performance_manager/v8_memory/v8_context_tracker.h"
- #include "content/public/browser/render_process_host.h"
- #include "content/public/browser/web_contents.h"
- #include "content/public/test/navigation_simulator.h"
- #include "content/public/test/test_renderer_host.h"
- #include "content/public/test/test_utils.h"
- #include "url/gurl.h"
- namespace performance_manager {
- namespace v8_memory {
- using ::testing::_;
- ////////////////////////////////////////////////////////////////////////////////
- // LenientMockV8DetailedMemoryReporter
- LenientMockV8DetailedMemoryReporter::LenientMockV8DetailedMemoryReporter() =
- default;
- LenientMockV8DetailedMemoryReporter::~LenientMockV8DetailedMemoryReporter() =
- default;
- void LenientMockV8DetailedMemoryReporter::Bind(
- mojo::PendingReceiver<blink::mojom::V8DetailedMemoryReporter>
- pending_receiver) {
- return receiver_.Bind(std::move(pending_receiver));
- }
- ////////////////////////////////////////////////////////////////////////////////
- // V8MemoryTestBase
- V8MemoryTestBase::V8MemoryTestBase()
- : bind_callback_(
- base::BindRepeating(&V8MemoryTestBase::BindReceiverOnMainSequence,
- base::Unretained(this))) {
- internal::SetBindV8DetailedMemoryReporterCallbackForTesting(&bind_callback_);
- }
- V8MemoryTestBase::~V8MemoryTestBase() {
- internal::SetBindV8DetailedMemoryReporterCallbackForTesting(nullptr);
- }
- void V8MemoryTestBase::ReplyWithData(
- blink::mojom::PerProcessV8MemoryUsagePtr data,
- MockV8DetailedMemoryReporter::GetV8MemoryUsageCallback callback) {
- std::move(callback).Run(std::move(data));
- }
- void V8MemoryTestBase::DelayedReplyWithData(
- const base::TimeDelta& delay,
- blink::mojom::PerProcessV8MemoryUsagePtr data,
- MockV8DetailedMemoryReporter::GetV8MemoryUsageCallback callback) {
- GetMainThreadTaskRunner()->PostDelayedTask(
- FROM_HERE, base::BindOnce(std::move(callback), std::move(data)), delay);
- }
- void V8MemoryTestBase::ExpectQuery(
- MockV8DetailedMemoryReporter* mock_reporter,
- base::RepeatingCallback<
- void(MockV8DetailedMemoryReporter::GetV8MemoryUsageCallback callback)>
- responder,
- ExpectedMode expected_mode) {
- EXPECT_CALL(*mock_reporter, GetV8MemoryUsage(expected_mode, _))
- .WillOnce(
- [this, responder](
- ExpectedMode mode,
- MockV8DetailedMemoryReporter::GetV8MemoryUsageCallback callback) {
- this->last_query_time_ = base::TimeTicks::Now();
- responder.Run(std::move(callback));
- });
- }
- void V8MemoryTestBase::ExpectQueryAndReply(
- MockV8DetailedMemoryReporter* mock_reporter,
- blink::mojom::PerProcessV8MemoryUsagePtr data,
- ExpectedMode expected_mode) {
- ExpectQuery(mock_reporter,
- base::BindRepeating(&V8MemoryTestBase::ReplyWithData,
- base::Unretained(this), base::Passed(&data)),
- expected_mode);
- }
- void V8MemoryTestBase::ExpectQueryAndDelayReply(
- MockV8DetailedMemoryReporter* mock_reporter,
- const base::TimeDelta& delay,
- blink::mojom::PerProcessV8MemoryUsagePtr data,
- ExpectedMode expected_mode) {
- ExpectQuery(
- mock_reporter,
- base::BindRepeating(&V8MemoryTestBase::DelayedReplyWithData,
- base::Unretained(this), delay, base::Passed(&data)),
- expected_mode);
- }
- void V8MemoryTestBase::ExpectBindReceiver(
- MockV8DetailedMemoryReporter* mock_reporter,
- RenderProcessHostId expected_process_id) {
- using ::testing::Eq;
- using ::testing::Invoke;
- using ::testing::Property;
- using ::testing::WithArg;
- // Arg 0 is a
- // mojo::PendingReceiver<blink::mojom::V8DetailedMemoryReporter>. Pass it
- // to mock_reporter->Bind().
- //
- // Arg 1 is a RenderProcessHostProxy. Expect it to have the expected
- // process ID.
- EXPECT_CALL(*this,
- BindReceiverWithProxyHost(
- _, Property(&RenderProcessHostProxy::render_process_host_id,
- Eq(expected_process_id))))
- .WillOnce(WithArg<0>(
- Invoke(mock_reporter, &MockV8DetailedMemoryReporter::Bind)));
- }
- void V8MemoryTestBase::ExpectBindAndRespondToQuery(
- MockV8DetailedMemoryReporter* mock_reporter,
- blink::mojom::PerProcessV8MemoryUsagePtr data,
- RenderProcessHostId expected_process_id,
- ExpectedMode expected_mode) {
- ::testing::InSequence seq;
- ExpectBindReceiver(mock_reporter, expected_process_id);
- ExpectQueryAndReply(mock_reporter, std::move(data), expected_mode);
- }
- void V8MemoryTestBase::BindReceiverOnMainSequence(
- mojo::PendingReceiver<blink::mojom::V8DetailedMemoryReporter>
- pending_receiver,
- RenderProcessHostProxy proxy) {
- GetMainThreadTaskRunner()->PostTask(
- FROM_HERE, base::BindOnce(&V8MemoryTestBase::BindReceiverWithProxyHost,
- base::Unretained(this),
- std::move(pending_receiver), proxy));
- }
- // Storage for static members.
- constexpr RenderProcessHostId V8MemoryTestBase::kTestProcessID;
- ////////////////////////////////////////////////////////////////////////////////
- // V8MemoryPerformanceManagerTestHarness
- V8MemoryPerformanceManagerTestHarness::V8MemoryPerformanceManagerTestHarness()
- : PerformanceManagerTestHarness(
- // Use MOCK_TIME so that ExpectQueryAndDelayReply can be used.
- base::test::TaskEnvironment::TimeSource::MOCK_TIME) {
- GetGraphFeatures().EnableExecutionContextRegistry();
- GetGraphFeatures().EnableV8ContextTracker();
- }
- V8MemoryPerformanceManagerTestHarness::
- ~V8MemoryPerformanceManagerTestHarness() = default;
- void V8MemoryPerformanceManagerTestHarness::SetUp() {
- PerformanceManagerTestHarness::SetUp();
- if (!base::FeatureList::IsEnabled(features::kRunOnMainThread)) {
- // Precondition: CallOnGraph must run on a different sequence. Note that
- // all tasks passed to CallOnGraph will only run when run_loop.Run() is
- // called.
- ASSERT_TRUE(GetMainThreadTaskRunner()->RunsTasksInCurrentSequence());
- base::RunLoop run_loop;
- PerformanceManager::CallOnGraph(
- FROM_HERE, base::BindLambdaForTesting([&] {
- EXPECT_FALSE(
- this->GetMainThreadTaskRunner()->RunsTasksInCurrentSequence());
- run_loop.Quit();
- }));
- run_loop.Run();
- }
- // Set the active contents and simulate a navigation, which adds nodes to
- // the graph.
- content::IsolateAllSitesForTesting(base::CommandLine::ForCurrentProcess());
- SetContents(CreateTestWebContents());
- main_frame_ = content::NavigationSimulator::NavigateAndCommitFromBrowser(
- web_contents(), GURL(kMainFrameUrl));
- main_process_id_ = RenderProcessHostId(main_frame_->GetProcess()->GetID());
- }
- void V8MemoryPerformanceManagerTestHarness::CreateCrossProcessChildFrame() {
- // Since kMainFrameUrl has a different domain than kChildFrameUrl, the main
- // and child frames should end up in different processes.
- child_frame_ =
- content::RenderFrameHostTester::For(main_frame_)->AppendChild("frame1");
- child_frame_ = content::NavigationSimulator::NavigateAndCommitFromDocument(
- GURL(kChildFrameUrl), child_frame_);
- child_process_id_ = RenderProcessHostId(child_frame_->GetProcess()->GetID());
- ASSERT_NE(main_process_id_, child_process_id_);
- }
- scoped_refptr<base::SingleThreadTaskRunner>
- V8MemoryPerformanceManagerTestHarness::GetMainThreadTaskRunner() {
- return task_environment()->GetMainThreadTaskRunner();
- }
- // Storage for static members.
- constexpr char V8MemoryPerformanceManagerTestHarness::kMainFrameUrl[];
- constexpr char V8MemoryPerformanceManagerTestHarness::kChildFrameUrl[];
- ////////////////////////////////////////////////////////////////////////////////
- // WebMemoryTestHarness
- WebMemoryTestHarness::WebMemoryTestHarness() = default;
- WebMemoryTestHarness::~WebMemoryTestHarness() = default;
- void WebMemoryTestHarness::SetUp() {
- GetGraphFeatures().EnableV8ContextTracker();
- Super::SetUp();
- process_ = CreateNode<ProcessNodeImpl>();
- other_process_ = CreateNode<ProcessNodeImpl>();
- pages_.push_back(CreateNode<PageNodeImpl>());
- }
- int WebMemoryTestHarness::GetNextUniqueId() {
- return next_unique_id_++;
- }
- FrameNodeImpl* WebMemoryTestHarness::AddFrameNodeImpl(
- absl::optional<std::string> url,
- int browsing_instance_id,
- Bytes memory_usage,
- FrameNodeImpl* parent,
- FrameNodeImpl* opener,
- ProcessNodeImpl* process,
- absl::optional<std::string> id_attribute,
- absl::optional<std::string> src_attribute,
- Bytes canvas_memory_usage) {
- // If there's an opener, the new frame is also a new page.
- auto* page = pages_.front().get();
- if (opener) {
- pages_.push_back(CreateNode<PageNodeImpl>());
- page = pages_.back().get();
- page->SetOpenerFrameNode(opener);
- }
- int frame_routing_id = GetNextUniqueId();
- auto frame_token = blink::LocalFrameToken();
- auto frame = CreateNode<FrameNodeImpl>(
- process, page, parent, frame_routing_id, frame_token,
- content::BrowsingInstanceId(browsing_instance_id));
- if (url) {
- frame->OnNavigationCommitted(GURL(*url), /*same document*/ true);
- }
- if (memory_usage || canvas_memory_usage) {
- auto* data =
- V8DetailedMemoryExecutionContextData::CreateForTesting(frame.get());
- if (memory_usage)
- data->set_v8_bytes_used(memory_usage.value());
- if (canvas_memory_usage)
- data->set_canvas_bytes_used(canvas_memory_usage.value());
- }
- frames_.push_back(std::move(frame));
- FrameNodeImpl* frame_impl = frames_.back().get();
- // Create a V8ContextDescription with attribution data for this frame. (In
- // production this is done by PerformanceManager monitoring frame lifetime
- // events.)
- auto description = mojom::V8ContextDescription::New();
- description->token = blink::V8ContextToken();
- description->world_type = mojom::V8ContextWorldType::kMain;
- description->execution_context_token = frame_token;
- mojom::IframeAttributionDataPtr attribution;
- if (parent) {
- // Frame attribution attributes come from the frame's parent node, so
- // V8ContextTracker expects an IframeAttributionData. The attribute values
- // may be empty.
- attribution = mojom::IframeAttributionData::New();
- attribution->id = id_attribute;
- attribution->src = src_attribute;
- } else {
- // V8ContextTracker expects no IframeAttributionData.
- DCHECK(!id_attribute);
- DCHECK(!src_attribute);
- }
- // If the frame is in the same process as its parent include the attribution
- // in OnV8ContextCreated, otherwise it must be attached separately with
- // OnRemoteIframeAttached.
- DCHECK(frame_impl->process_node());
- if (parent && parent->process_node() != frame_impl->process_node()) {
- frame_impl->process_node()->OnV8ContextCreated(
- std::move(description), mojom::IframeAttributionDataPtr());
- V8ContextTracker::GetFromGraph(graph())->OnRemoteIframeAttachedForTesting(
- frame_impl, parent, blink::RemoteFrameToken(), std::move(attribution));
- } else {
- frame_impl->process_node()->OnV8ContextCreated(std::move(description),
- std::move(attribution));
- }
- return frame_impl;
- }
- WorkerNodeImpl* WebMemoryTestHarness::AddWorkerNode(
- WorkerNode::WorkerType worker_type,
- std::string url,
- Bytes bytes,
- FrameNodeImpl* parent) {
- auto* worker_node = AddWorkerNodeImpl(worker_type, url, bytes);
- worker_node->AddClientFrame(parent);
- return worker_node;
- }
- WorkerNodeImpl* WebMemoryTestHarness::AddWorkerNodeWithoutData(
- WorkerNode::WorkerType worker_type,
- FrameNodeImpl* parent) {
- auto worker_node = CreateNode<WorkerNodeImpl>(worker_type, process_.get());
- worker_node->AddClientFrame(parent);
- workers_.push_back(std::move(worker_node));
- return workers_.back().get();
- }
- WorkerNodeImpl* WebMemoryTestHarness::AddWorkerNode(
- WorkerNode::WorkerType worker_type,
- std::string url,
- Bytes bytes,
- WorkerNodeImpl* parent) {
- auto* worker_node = AddWorkerNodeImpl(worker_type, url, bytes);
- worker_node->AddClientWorker(parent);
- return worker_node;
- }
- WorkerNodeImpl* WebMemoryTestHarness::AddWorkerNodeImpl(
- WorkerNode::WorkerType worker_type,
- std::string url,
- Bytes bytes) {
- auto worker_node = CreateNode<WorkerNodeImpl>(worker_type, process_.get());
- worker_node->OnFinalResponseURLDetermined(GURL(url));
- if (bytes) {
- V8DetailedMemoryExecutionContextData::CreateForTesting(worker_node.get())
- ->set_v8_bytes_used(*bytes);
- }
- workers_.push_back(std::move(worker_node));
- return workers_.back().get();
- }
- void WebMemoryTestHarness::SetBlinkMemory(Bytes bytes) {
- V8DetailedMemoryProcessData::GetOrCreateForTesting(process_node())
- ->set_blink_bytes_used(*bytes);
- }
- ////////////////////////////////////////////////////////////////////////////////
- // Free functions
- blink::mojom::PerProcessV8MemoryUsagePtr NewPerProcessV8MemoryUsage(
- size_t number_of_isolates) {
- auto data = blink::mojom::PerProcessV8MemoryUsage::New();
- for (size_t i = 0; i < number_of_isolates; ++i) {
- data->isolates.push_back(blink::mojom::PerIsolateV8MemoryUsage::New());
- }
- return data;
- }
- void AddIsolateMemoryUsage(blink::ExecutionContextToken token,
- uint64_t bytes_used,
- blink::mojom::PerIsolateV8MemoryUsage* isolate) {
- for (auto& entry : isolate->contexts) {
- if (entry->token == token) {
- entry->bytes_used = bytes_used;
- return;
- }
- }
- auto context = blink::mojom::PerContextV8MemoryUsage::New();
- context->token = token;
- context->bytes_used = bytes_used;
- isolate->contexts.push_back(std::move(context));
- }
- void AddIsolateCanvasMemoryUsage(
- blink::ExecutionContextToken token,
- uint64_t bytes_used,
- blink::mojom::PerIsolateV8MemoryUsage* isolate) {
- for (auto& entry : isolate->canvas_contexts) {
- if (entry->token == token) {
- entry->bytes_used = bytes_used;
- return;
- }
- }
- auto context = blink::mojom::PerContextCanvasMemoryUsage::New();
- context->token = token;
- context->bytes_used = bytes_used;
- isolate->canvas_contexts.push_back(std::move(context));
- }
- } // namespace v8_memory
- } // namespace performance_manager
|