Commit 48992e13 authored by Joe Mason's avatar Joe Mason Committed by Chromium LUCI CQ

[PM] Make WebMemoryAggregator report 0 bytes for cross-process frames.

This conflicts with the use of 0 bytes as a sentinel for frames that
hadn't been measured and should be dropped from the result. So this also
makes the bytes optional in WebMemoryBreakdownEntry, so that frames with
a byte count of 0 are reported and frames without a byte count are
dropped from the result.

R=ulan

Bug: 1085129
Change-Id: Ie838c72049465e3a3bef9c0a9bb3a11aa648e62c
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2616818Reviewed-by: default avatarUlan Degenbaev <ulan@chromium.org>
Commit-Queue: Joe Mason <joenotcharles@chromium.org>
Cr-Commit-Position: refs/heads/master@{#841480}
parent f6b1c6df
...@@ -47,13 +47,21 @@ struct WebMemoryAttribution { ...@@ -47,13 +47,21 @@ struct WebMemoryAttribution {
string? id; string? id;
}; };
// The amount of memory used by a breakdown.
struct WebMemoryUsage {
uint64 bytes;
};
// Describes a memory region and attributes it to a set of contexts. // Describes a memory region and attributes it to a set of contexts.
// Usually the set consists of a single context. If there are multiple // Usually the set consists of a single context. If there are multiple
// contexts then this means that the memory may be owned by any of them. // contexts then this means that the memory may be owned by any of them.
struct WebMemoryBreakdownEntry { struct WebMemoryBreakdownEntry {
uint64 bytes; // The memory used in this breakdown. It is null for breakdowns that did not
array<WebMemoryAttribution> attribution; // have a memory measurement (for example a frame that was added after the
// measurement started).
WebMemoryUsage? memory;
array<WebMemoryAttribution> attribution;
// TODO(1085129): Add memory types once they are implemented. // TODO(1085129): Add memory types once they are implemented.
}; };
......
...@@ -19,6 +19,7 @@ ...@@ -19,6 +19,7 @@
#include "components/performance_manager/graph/process_node_impl.h" #include "components/performance_manager/graph/process_node_impl.h"
#include "components/performance_manager/public/mojom/v8_contexts.mojom.h" #include "components/performance_manager/public/mojom/v8_contexts.mojom.h"
#include "components/performance_manager/public/performance_manager.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/render_process_host.h"
#include "content/public/test/navigation_simulator.h" #include "content/public/test/navigation_simulator.h"
#include "content/public/test/test_renderer_host.h" #include "content/public/test/test_renderer_host.h"
...@@ -226,6 +227,7 @@ void WebMemoryTestHarness::SetUp() { ...@@ -226,6 +227,7 @@ void WebMemoryTestHarness::SetUp() {
GetGraphFeaturesHelper().EnableV8ContextTracker(); GetGraphFeaturesHelper().EnableV8ContextTracker();
Super::SetUp(); Super::SetUp();
process_ = CreateNode<ProcessNodeImpl>(); process_ = CreateNode<ProcessNodeImpl>();
other_process_ = CreateNode<ProcessNodeImpl>();
pages_.push_back(CreateNode<PageNodeImpl>()); pages_.push_back(CreateNode<PageNodeImpl>());
} }
...@@ -239,6 +241,7 @@ FrameNodeImpl* WebMemoryTestHarness::AddFrameNodeImpl( ...@@ -239,6 +241,7 @@ FrameNodeImpl* WebMemoryTestHarness::AddFrameNodeImpl(
Bytes memory_usage, Bytes memory_usage,
FrameNodeImpl* parent, FrameNodeImpl* parent,
FrameNodeImpl* opener, FrameNodeImpl* opener,
ProcessNodeImpl* process,
base::Optional<std::string> id_attribute, base::Optional<std::string> id_attribute,
base::Optional<std::string> src_attribute) { base::Optional<std::string> src_attribute) {
// If there's an opener, the new frame is also a new page. // If there's an opener, the new frame is also a new page.
...@@ -252,14 +255,16 @@ FrameNodeImpl* WebMemoryTestHarness::AddFrameNodeImpl( ...@@ -252,14 +255,16 @@ FrameNodeImpl* WebMemoryTestHarness::AddFrameNodeImpl(
int frame_tree_node_id = GetNextUniqueId(); int frame_tree_node_id = GetNextUniqueId();
int frame_routing_id = GetNextUniqueId(); int frame_routing_id = GetNextUniqueId();
auto frame_token = blink::LocalFrameToken(); auto frame_token = blink::LocalFrameToken();
auto frame = CreateNode<FrameNodeImpl>(process_.get(), page, parent, auto frame = CreateNode<FrameNodeImpl>(process, page, parent,
frame_tree_node_id, frame_routing_id, frame_tree_node_id, frame_routing_id,
frame_token, browsing_instance_id); frame_token, browsing_instance_id);
if (url) { if (url) {
frame->OnNavigationCommitted(GURL(*url), /*same document*/ true); frame->OnNavigationCommitted(GURL(*url), /*same document*/ true);
} }
V8DetailedMemoryExecutionContextData::CreateForTesting(frame.get()) if (memory_usage) {
->set_v8_bytes_used(memory_usage.bytes); V8DetailedMemoryExecutionContextData::CreateForTesting(frame.get())
->set_v8_bytes_used(memory_usage.value());
}
frames_.push_back(std::move(frame)); frames_.push_back(std::move(frame));
FrameNodeImpl* frame_impl = frames_.back().get(); FrameNodeImpl* frame_impl = frames_.back().get();
...@@ -284,9 +289,20 @@ FrameNodeImpl* WebMemoryTestHarness::AddFrameNodeImpl( ...@@ -284,9 +289,20 @@ FrameNodeImpl* WebMemoryTestHarness::AddFrameNodeImpl(
DCHECK(!id_attribute); DCHECK(!id_attribute);
DCHECK(!src_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()); DCHECK(frame_impl->process_node());
frame_impl->process_node()->OnV8ContextCreated(std::move(description), if (parent && parent->process_node() != frame_impl->process_node()) {
std::move(attribution)); 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; return frame_impl;
} }
......
...@@ -225,10 +225,7 @@ class WebMemoryTestHarness : public GraphTestHarness { ...@@ -225,10 +225,7 @@ class WebMemoryTestHarness : public GraphTestHarness {
using Super = GraphTestHarness; using Super = GraphTestHarness;
// Wrapper for memory usage bytes to improve test readability. // Wrapper for memory usage bytes to improve test readability.
struct Bytes { using Bytes = base::Optional<uint64_t>;
uint64_t bytes;
bool operator==(const Bytes& other) const { return bytes == other.bytes; }
};
WebMemoryTestHarness(); WebMemoryTestHarness();
~WebMemoryTestHarness() override; ~WebMemoryTestHarness() override;
...@@ -243,7 +240,8 @@ class WebMemoryTestHarness : public GraphTestHarness { ...@@ -243,7 +240,8 @@ class WebMemoryTestHarness : public GraphTestHarness {
base::Optional<std::string> id_attribute = base::nullopt, base::Optional<std::string> id_attribute = base::nullopt,
base::Optional<std::string> src_attribute = base::nullopt) { base::Optional<std::string> src_attribute = base::nullopt) {
return AddFrameNodeImpl(url, kDefaultBrowsingInstanceId, bytes, parent, return AddFrameNodeImpl(url, kDefaultBrowsingInstanceId, bytes, parent,
/*opener=*/nullptr, id_attribute, src_attribute); /*opener=*/nullptr, process_.get(), id_attribute,
src_attribute);
} }
// Creates a frame node as if from window.open and adds it to the graph. // Creates a frame node as if from window.open and adds it to the graph.
...@@ -251,7 +249,9 @@ class WebMemoryTestHarness : public GraphTestHarness { ...@@ -251,7 +249,9 @@ class WebMemoryTestHarness : public GraphTestHarness {
Bytes bytes, Bytes bytes,
FrameNodeImpl* opener) { FrameNodeImpl* opener) {
return AddFrameNodeImpl(url, kDefaultBrowsingInstanceId, bytes, return AddFrameNodeImpl(url, kDefaultBrowsingInstanceId, bytes,
/*parent=*/nullptr, opener); /*parent=*/nullptr, opener, process_.get(),
/*id_attribute=*/base::nullopt,
/*src_attribute=*/base::nullopt);
} }
// Creates a frame node in a different browsing instance and adds it to the // Creates a frame node in a different browsing instance and adds it to the
...@@ -263,7 +263,8 @@ class WebMemoryTestHarness : public GraphTestHarness { ...@@ -263,7 +263,8 @@ class WebMemoryTestHarness : public GraphTestHarness {
base::Optional<std::string> id_attribute = base::nullopt, base::Optional<std::string> id_attribute = base::nullopt,
base::Optional<std::string> src_attribute = base::nullopt) { base::Optional<std::string> src_attribute = base::nullopt) {
return AddFrameNodeImpl(url, kDefaultBrowsingInstanceId + 1, bytes, parent, return AddFrameNodeImpl(url, kDefaultBrowsingInstanceId + 1, bytes, parent,
/*opener=*/nullptr, id_attribute, src_attribute); /*opener=*/nullptr, process_.get(), id_attribute,
src_attribute);
} }
// Creates a frame node in a different browsing instance as if from // Creates a frame node in a different browsing instance as if from
...@@ -273,7 +274,21 @@ class WebMemoryTestHarness : public GraphTestHarness { ...@@ -273,7 +274,21 @@ class WebMemoryTestHarness : public GraphTestHarness {
Bytes bytes, Bytes bytes,
FrameNodeImpl* opener) { FrameNodeImpl* opener) {
return AddFrameNodeImpl(url, kDefaultBrowsingInstanceId + 1, bytes, return AddFrameNodeImpl(url, kDefaultBrowsingInstanceId + 1, bytes,
/*parent=*/nullptr, opener); /*parent=*/nullptr, opener, process_.get(),
/*id_attribute=*/base::nullopt,
/*src_attribute=*/base::nullopt);
}
// Creates a frame node in a different process and adds it to the graph.
FrameNodeImpl* AddCrossProcessFrameNode(
std::string url,
Bytes bytes,
FrameNodeImpl* parent,
base::Optional<std::string> id_attribute = base::nullopt,
base::Optional<std::string> src_attribute = base::nullopt) {
return AddFrameNodeImpl(url, kDefaultBrowsingInstanceId, bytes, parent,
/*opener=*/nullptr, other_process_.get(),
id_attribute, src_attribute);
} }
ProcessNode* process_node() const { return process_.get(); } ProcessNode* process_node() const { return process_.get(); }
...@@ -282,16 +297,17 @@ class WebMemoryTestHarness : public GraphTestHarness { ...@@ -282,16 +297,17 @@ class WebMemoryTestHarness : public GraphTestHarness {
static constexpr int kDefaultBrowsingInstanceId = 0; static constexpr int kDefaultBrowsingInstanceId = 0;
// Creates and adds a new frame node to the graph. // Creates and adds a new frame node to the graph.
FrameNodeImpl* AddFrameNodeImpl( FrameNodeImpl* AddFrameNodeImpl(base::Optional<std::string> url,
base::Optional<std::string> url, int browsing_instance_id,
int browsing_instance_id, Bytes bytes,
Bytes bytes, FrameNodeImpl* parent,
FrameNodeImpl* parent = nullptr, FrameNodeImpl* opener,
FrameNodeImpl* opener = nullptr, ProcessNodeImpl* process,
base::Optional<std::string> id_attribute = base::nullopt, base::Optional<std::string> id_attribute,
base::Optional<std::string> src_attribute = base::nullopt); base::Optional<std::string> src_attribute);
int GetNextUniqueId(); int GetNextUniqueId();
TestNodeWrapper<ProcessNodeImpl> process_; TestNodeWrapper<ProcessNodeImpl> process_;
TestNodeWrapper<ProcessNodeImpl> other_process_;
std::vector<TestNodeWrapper<PageNodeImpl>> pages_; std::vector<TestNodeWrapper<PageNodeImpl>> pages_;
std::vector<TestNodeWrapper<FrameNodeImpl>> frames_; std::vector<TestNodeWrapper<FrameNodeImpl>> frames_;
int next_unique_id_ = 0; int next_unique_id_ = 0;
......
...@@ -141,13 +141,6 @@ WebMemoryAggregator::AggregateMeasureMemoryResult() { ...@@ -141,13 +141,6 @@ WebMemoryAggregator::AggregateMeasureMemoryResult() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
aggregation_result_ = mojom::WebMemoryMeasurement::New(); aggregation_result_ = mojom::WebMemoryMeasurement::New();
VisitFrame(nullptr, aggregation_start_node_); VisitFrame(nullptr, aggregation_start_node_);
// Don't report breakdowns without any memory use.
base::EraseIf(aggregation_result_->breakdown,
[](const mojom::WebMemoryBreakdownEntryPtr& entry) {
return entry->bytes == 0;
});
return std::move(aggregation_result_); return std::move(aggregation_result_);
} }
...@@ -221,7 +214,18 @@ bool WebMemoryAggregator::VisitFrame( ...@@ -221,7 +214,18 @@ bool WebMemoryAggregator::VisitFrame(
DCHECK(aggregation_point); DCHECK(aggregation_point);
if (auto* frame_data = if (auto* frame_data =
V8DetailedMemoryExecutionContextData::ForFrameNode(frame_node)) { V8DetailedMemoryExecutionContextData::ForFrameNode(frame_node)) {
aggregation_point->bytes += frame_data->v8_bytes_used(); if (!aggregation_point->memory) {
aggregation_point->memory = mojom::WebMemoryUsage::New();
}
// Ensure this frame is actually in the same process as the requesting
// frame. If not it should be considered to have 0 bytes.
// (https://github.com/WICG/performance-measure-memory/issues/20).
uint64_t bytes_used = (frame_node->GetProcessNode() ==
aggregation_start_node_->GetProcessNode())
? frame_data->v8_bytes_used()
: 0;
aggregation_point->memory->bytes += bytes_used;
} }
// Recurse into children and opened pages. This node's aggregation point // Recurse into children and opened pages. This node's aggregation point
...@@ -269,10 +273,16 @@ const FrameNode* FindAggregationStartNode(const FrameNode* requesting_node) { ...@@ -269,10 +273,16 @@ const FrameNode* FindAggregationStartNode(const FrameNode* requesting_node) {
// Follow parent and opener links to find the most general same-site node to // Follow parent and opener links to find the most general same-site node to
// start the aggregation traversal from. // start the aggregation traversal from.
const FrameNode* start_node = requesting_node; const FrameNode* start_node = nullptr;
while (auto* parent_or_opener = for (auto* parent_or_opener = requesting_node; parent_or_opener;
GetSameOriginParentOrOpener(start_node, requesting_origin)) { parent_or_opener =
start_node = parent_or_opener; GetSameOriginParentOrOpener(parent_or_opener, requesting_origin)) {
// Only consider nodes in the same process as potential start nodes.
// (https://github.com/WICG/performance-measure-memory/issues/20).
if (parent_or_opener->GetProcessNode() ==
requesting_node->GetProcessNode()) {
start_node = parent_or_opener;
}
} }
DCHECK(start_node); DCHECK(start_node);
......
...@@ -30,7 +30,7 @@ using NodeAggregationType = WebMemoryAggregator::NodeAggregationType; ...@@ -30,7 +30,7 @@ using NodeAggregationType = WebMemoryAggregator::NodeAggregationType;
using WebMemoryAggregatorTest = WebMemoryTestHarness; using WebMemoryAggregatorTest = WebMemoryTestHarness;
struct ExpectedMemoryBreakdown { struct ExpectedMemoryBreakdown {
uint64_t bytes = 0U; WebMemoryTestHarness::Bytes bytes;
AttributionScope scope = AttributionScope::kWindow; AttributionScope scope = AttributionScope::kWindow;
base::Optional<std::string> url; base::Optional<std::string> url;
base::Optional<std::string> id; base::Optional<std::string> id;
...@@ -38,7 +38,7 @@ struct ExpectedMemoryBreakdown { ...@@ -38,7 +38,7 @@ struct ExpectedMemoryBreakdown {
ExpectedMemoryBreakdown() = default; ExpectedMemoryBreakdown() = default;
ExpectedMemoryBreakdown( ExpectedMemoryBreakdown(
uint64_t expected_bytes, WebMemoryTestHarness::Bytes expected_bytes,
AttributionScope expected_scope, AttributionScope expected_scope,
base::Optional<std::string> expected_url = base::nullopt, base::Optional<std::string> expected_url = base::nullopt,
base::Optional<std::string> expected_id = base::nullopt, base::Optional<std::string> expected_id = base::nullopt,
...@@ -59,7 +59,10 @@ mojom::WebMemoryMeasurementPtr CreateExpectedMemoryMeasurement( ...@@ -59,7 +59,10 @@ mojom::WebMemoryMeasurementPtr CreateExpectedMemoryMeasurement(
auto expected_measurement = mojom::WebMemoryMeasurement::New(); auto expected_measurement = mojom::WebMemoryMeasurement::New();
for (const auto& breakdown : breakdowns) { for (const auto& breakdown : breakdowns) {
auto expected_breakdown = mojom::WebMemoryBreakdownEntry::New(); auto expected_breakdown = mojom::WebMemoryBreakdownEntry::New();
expected_breakdown->bytes = breakdown.bytes; if (breakdown.bytes) {
expected_breakdown->memory = mojom::WebMemoryUsage::New();
expected_breakdown->memory->bytes = breakdown.bytes.value();
}
auto attribution = mojom::WebMemoryAttribution::New(); auto attribution = mojom::WebMemoryAttribution::New();
attribution->scope = breakdown.scope; attribution->scope = breakdown.scope;
...@@ -129,15 +132,18 @@ TEST_F(WebMemoryAggregatorTest, CreateBreakdownEntry) { ...@@ -129,15 +132,18 @@ TEST_F(WebMemoryAggregatorTest, CreateBreakdownEntry) {
internal::CopyBreakdownAttribution(breakdown_with_url, internal::CopyBreakdownAttribution(breakdown_with_url,
breakdown_with_empty_url); breakdown_with_empty_url);
// All measurements should be created with 0 bytes. // All measurements should be created without measurement results.
auto expected_result = CreateExpectedMemoryMeasurement({ auto expected_result = CreateExpectedMemoryMeasurement({
ExpectedMemoryBreakdown(0, AttributionScope::kCrossOriginAggregated, ExpectedMemoryBreakdown(/*bytes=*/base::nullopt,
AttributionScope::kCrossOriginAggregated,
/*expected_url=*/base::nullopt, /*expected_url=*/base::nullopt,
/*expected_id=*/base::nullopt, /*expected_id=*/base::nullopt,
/*expected_src=*/base::nullopt), /*expected_src=*/base::nullopt),
ExpectedMemoryBreakdown(0, AttributionScope::kWindow, ExpectedMemoryBreakdown(/*bytes=*/base::nullopt,
AttributionScope::kWindow,
"https://example.com", attribute, attribute), "https://example.com", attribute, attribute),
ExpectedMemoryBreakdown(0, AttributionScope::kWindow, ExpectedMemoryBreakdown(/*bytes=*/base::nullopt,
AttributionScope::kWindow,
/*expected_url=*/"", attribute, attribute), /*expected_url=*/"", attribute, attribute),
}); });
EXPECT_EQ(MeasurementToJSON(measurement), EXPECT_EQ(MeasurementToJSON(measurement),
...@@ -293,10 +299,21 @@ TEST_F(WebMemoryAggregatorTest, AggregateNestedCrossOrigin) { ...@@ -293,10 +299,21 @@ TEST_F(WebMemoryAggregatorTest, AggregateNestedCrossOrigin) {
AddFrameNode("https://example.com/iframe3", Bytes{6}, subframe3, AddFrameNode("https://example.com/iframe3", Bytes{6}, subframe3,
"example-id6", "https://example.com/iframe3"); "example-id6", "https://example.com/iframe3");
// A frame with 0 bytes of memory use (eg. a frame that's added to the frame // To test aggregation all the frames above are in the same process, even
// tree during the measurement) should not appear in the result. // though in production frames with different origins will be in different
// processes whenever possible. Frames in a different process from the
// requesting frame should all have 0 bytes reported.
FrameNodeImpl* cross_process_frame =
AddCrossProcessFrameNode("https://example.com/cross_process", Bytes{100},
subframe3, "cross-process-id1");
FrameNodeImpl* cross_process_frame2 =
AddCrossProcessFrameNode("https://foo.com/cross_process", Bytes{200},
subframe3, "cross-process-id2");
// A frame without a memory measurement (eg. a frame that's added to the frame
// tree during the measurement) should not have a memory entry in the result.
FrameNodeImpl* empty_frame = FrameNodeImpl* empty_frame =
AddFrameNode("https://example.com/empty_frame", Bytes{0}, subframe3); AddFrameNode("https://example.com/empty_frame", base::nullopt, subframe3);
EXPECT_EQ(internal::FindAggregationStartNode(main_frame), main_frame); EXPECT_EQ(internal::FindAggregationStartNode(main_frame), main_frame);
WebMemoryAggregator aggregator(main_frame); WebMemoryAggregator aggregator(main_frame);
...@@ -339,6 +356,16 @@ TEST_F(WebMemoryAggregatorTest, AggregateNestedCrossOrigin) { ...@@ -339,6 +356,16 @@ TEST_F(WebMemoryAggregatorTest, AggregateNestedCrossOrigin) {
EXPECT_EQ(internal::GetSameOriginParentOrOpener( EXPECT_EQ(internal::GetSameOriginParentOrOpener(
empty_frame, aggregator.requesting_origin()), empty_frame, aggregator.requesting_origin()),
subframe3); subframe3);
EXPECT_EQ(aggregator.FindNodeAggregationType(cross_process_frame),
NodeAggregationType::kSameOriginAggregationPoint);
EXPECT_EQ(internal::GetSameOriginParentOrOpener(
cross_process_frame, aggregator.requesting_origin()),
subframe3);
EXPECT_EQ(aggregator.FindNodeAggregationType(cross_process_frame2),
NodeAggregationType::kCrossOriginAggregationPoint);
EXPECT_EQ(internal::GetSameOriginParentOrOpener(
cross_process_frame2, aggregator.requesting_origin()),
subframe3);
auto expected_result = CreateExpectedMemoryMeasurement({ auto expected_result = CreateExpectedMemoryMeasurement({
ExpectedMemoryBreakdown(10, AttributionScope::kWindow, ExpectedMemoryBreakdown(10, AttributionScope::kWindow,
...@@ -358,6 +385,13 @@ TEST_F(WebMemoryAggregatorTest, AggregateNestedCrossOrigin) { ...@@ -358,6 +385,13 @@ TEST_F(WebMemoryAggregatorTest, AggregateNestedCrossOrigin) {
ExpectedMemoryBreakdown(6, AttributionScope::kWindow, ExpectedMemoryBreakdown(6, AttributionScope::kWindow,
"https://example.com/iframe3", "example-id6", "https://example.com/iframe3", "example-id6",
"https://example.com/iframe3"), "https://example.com/iframe3"),
ExpectedMemoryBreakdown(0, AttributionScope::kWindow,
"https://example.com/cross_process",
"cross-process-id1"),
ExpectedMemoryBreakdown(0, AttributionScope::kCrossOriginAggregated,
base::nullopt, "cross-process-id2"),
ExpectedMemoryBreakdown(base::nullopt, AttributionScope::kWindow,
"https://example.com/empty_frame"),
}); });
auto result = aggregator.AggregateMeasureMemoryResult(); auto result = aggregator.AggregateMeasureMemoryResult();
EXPECT_EQ(MeasurementToJSON(result), MeasurementToJSON(expected_result)); EXPECT_EQ(MeasurementToJSON(result), MeasurementToJSON(expected_result));
...@@ -407,6 +441,29 @@ TEST_F(WebMemoryAggregatorTest, FindAggregationStartNode) { ...@@ -407,6 +441,29 @@ TEST_F(WebMemoryAggregatorTest, FindAggregationStartNode) {
MeasurementToJSON(main_frame_expected_result)); MeasurementToJSON(main_frame_expected_result));
} }
TEST_F(WebMemoryAggregatorTest, FindCrossProcessAggregationStartNode) {
FrameNodeImpl* main_frame = AddFrameNode("https://example.com/", Bytes{1});
FrameNodeImpl* cross_process_child = AddCrossProcessFrameNode(
"https://example.com/cross_process.html", Bytes{2}, main_frame);
FrameNodeImpl* same_process_child = AddFrameNode(
"https://example.com/same_process.html", Bytes{3}, cross_process_child);
auto origin = url::Origin::Create(GURL("https://example.com"));
ASSERT_EQ(internal::GetSameOriginParentOrOpener(cross_process_child, origin),
main_frame);
ASSERT_EQ(internal::GetSameOriginParentOrOpener(same_process_child, origin),
cross_process_child);
// |cross_process_child| has no ancestor in the same process as it.
EXPECT_EQ(internal::FindAggregationStartNode(cross_process_child),
cross_process_child);
// The search starting from |same_process_child| should skip over
// |cross_process_child|, which is in a different process, and find
// |main_frame| which is in the same process.
EXPECT_EQ(internal::FindAggregationStartNode(same_process_child), main_frame);
}
TEST_F(WebMemoryAggregatorTest, AggregateWindowOpener) { TEST_F(WebMemoryAggregatorTest, AggregateWindowOpener) {
FrameNodeImpl* main_frame = AddFrameNode("https://example.com/", Bytes{10}); FrameNodeImpl* main_frame = AddFrameNode("https://example.com/", Bytes{10});
FrameNodeImpl* child_frame = AddFrameNode("https://example.com/iframe.html", FrameNodeImpl* child_frame = AddFrameNode("https://example.com/iframe.html",
......
...@@ -70,12 +70,13 @@ void WebMemoryImplTest::MeasureAndVerify( ...@@ -70,12 +70,13 @@ void WebMemoryImplTest::MeasureAndVerify(
base::flat_map<std::string, Bytes> actual; base::flat_map<std::string, Bytes> actual;
for (const auto& entry : result->breakdown) { for (const auto& entry : result->breakdown) {
EXPECT_EQ(1u, entry->attribution.size()); EXPECT_EQ(1u, entry->attribution.size());
if (mojom::WebMemoryAttribution::Scope::kWindow == std::string attribution_tag =
entry->attribution[0]->scope) { (mojom::WebMemoryAttribution::Scope::kWindow ==
actual[*entry->attribution[0]->url] = Bytes{entry->bytes}; entry->attribution[0]->scope)
} else { ? *entry->attribution[0]->url
actual[*entry->attribution[0]->src] = Bytes{entry->bytes}; : *entry->attribution[0]->src;
} actual[attribution_tag] =
entry->memory ? Bytes{entry->memory->bytes} : base::nullopt;
} }
EXPECT_EQ(expected, actual); EXPECT_EQ(expected, actual);
measurement_done = true; measurement_done = true;
...@@ -130,7 +131,8 @@ TEST_F(WebMemoryImplPMTest, WebMeasureMemory) { ...@@ -130,7 +131,8 @@ TEST_F(WebMemoryImplPMTest, WebMeasureMemory) {
const auto& entry = result->breakdown[0]; const auto& entry = result->breakdown[0];
EXPECT_EQ(1u, entry->attribution.size()); EXPECT_EQ(1u, entry->attribution.size());
EXPECT_EQ(kMainFrameUrl, *(entry->attribution[0]->url)); EXPECT_EQ(kMainFrameUrl, *(entry->attribution[0]->url));
EXPECT_EQ(1001u, entry->bytes); ASSERT_TRUE(entry->memory);
EXPECT_EQ(1001u, entry->memory->bytes);
run_loop.Quit(); run_loop.Quit();
}); });
auto bad_message_callback = auto bad_message_callback =
......
...@@ -166,7 +166,8 @@ MemoryAttribution* ConvertAttribution( ...@@ -166,7 +166,8 @@ MemoryAttribution* ConvertAttribution(
MemoryBreakdownEntry* ConvertBreakdown( MemoryBreakdownEntry* ConvertBreakdown(
const WebMemoryBreakdownEntryPtr& breakdown_entry) { const WebMemoryBreakdownEntryPtr& breakdown_entry) {
auto* result = MemoryBreakdownEntry::Create(); auto* result = MemoryBreakdownEntry::Create();
result->setBytes(breakdown_entry->bytes); DCHECK(breakdown_entry->memory);
result->setBytes(breakdown_entry->memory->bytes);
HeapVector<Member<MemoryAttribution>> attribution; HeapVector<Member<MemoryAttribution>> attribution;
for (const auto& entry : breakdown_entry->attribution) { for (const auto& entry : breakdown_entry->attribution) {
attribution.push_back(ConvertAttribution(entry)); attribution.push_back(ConvertAttribution(entry));
...@@ -187,7 +188,9 @@ MemoryBreakdownEntry* EmptyBreakdown() { ...@@ -187,7 +188,9 @@ MemoryBreakdownEntry* EmptyBreakdown() {
MemoryMeasurement* ConvertResult(const WebMemoryMeasurementPtr& measurement) { MemoryMeasurement* ConvertResult(const WebMemoryMeasurementPtr& measurement) {
HeapVector<Member<MemoryBreakdownEntry>> breakdown; HeapVector<Member<MemoryBreakdownEntry>> breakdown;
for (const auto& entry : measurement->breakdown) { for (const auto& entry : measurement->breakdown) {
breakdown.push_back(ConvertBreakdown(entry)); // Skip breakdowns that didn't get a measurement.
if (entry->memory)
breakdown.push_back(ConvertBreakdown(entry));
} }
// Add an empty breakdown entry as required by the spec. // Add an empty breakdown entry as required by the spec.
// See https://github.com/WICG/performance-measure-memory/issues/10. // See https://github.com/WICG/performance-measure-memory/issues/10.
......
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