Commit 59b9bf8d authored by Joe Mason's avatar Joe Mason Committed by Commit Bot

[PM] Fix memory leak when WebMemoryAggregator gets no result

Make WebMemoryAggregator owned by its callback instead of by itself so
that it is also deleted if the callback is dropped.

R=ulan

Bug: 1085129
Change-Id: I4a7dcdd208c1b02933cf9e94cfcc6bd84502b3ba
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2515042Reviewed-by: default avatarUlan Degenbaev <ulan@chromium.org>
Commit-Queue: Joe Mason <joenotcharles@chromium.org>
Cr-Commit-Position: refs/heads/master@{#823547}
parent 1dc6175a
...@@ -10,7 +10,6 @@ ...@@ -10,7 +10,6 @@
#include "base/callback.h" #include "base/callback.h"
#include "components/performance_manager/public/graph/frame_node.h" #include "components/performance_manager/public/graph/frame_node.h"
#include "components/performance_manager/public/graph/process_node.h" #include "components/performance_manager/public/graph/process_node.h"
#include "components/performance_manager/public/v8_memory/v8_detailed_memory.h"
#include "components/performance_manager/public/v8_memory/web_memory.h" #include "components/performance_manager/public/v8_memory/web_memory.h"
#include "url/gurl.h" #include "url/gurl.h"
...@@ -85,21 +84,28 @@ void WebMeasureMemory( ...@@ -85,21 +84,28 @@ void WebMeasureMemory(
mojom::WebMemoryMeasurement::Mode mode, mojom::WebMemoryMeasurement::Mode mode,
base::OnceCallback<void(mojom::WebMemoryMeasurementPtr)> callback) { base::OnceCallback<void(mojom::WebMemoryMeasurementPtr)> callback) {
auto web_memory_aggregator = std::make_unique<WebMemoryAggregator>( auto web_memory_aggregator = std::make_unique<WebMemoryAggregator>(
frame_node->GetFrameToken(), std::move(callback)); frame_node->GetFrameToken(),
// Start memory measurementfor the process of the given frame. WebMeasurementModeToRequestMeasurementMode(mode), std::move(callback));
auto request = std::make_unique<V8DetailedMemoryRequestOneShot>(
frame_node->GetProcessNode(), // Create a measurement complete callback to own |web_memory_aggregator|. It
// will be deleted when the callback is executed or dropped.
V8DetailedMemoryRequestOneShot* request = web_memory_aggregator->request();
auto measurement_complete_callback =
base::BindOnce(&WebMemoryAggregator::MeasurementComplete, base::BindOnce(&WebMemoryAggregator::MeasurementComplete,
base::Unretained(web_memory_aggregator.get())), std::move(web_memory_aggregator));
WebMeasurementModeToRequestMeasurementMode(mode));
WebMemoryAggregator::MakeSelfOwned(std::move(web_memory_aggregator), // Start memory measurement for the process of the given frame.
std::move(request)); request->StartMeasurement(frame_node->GetProcessNode(),
std::move(measurement_complete_callback));
} }
WebMemoryAggregator::WebMemoryAggregator( WebMemoryAggregator::WebMemoryAggregator(
const blink::LocalFrameToken& frame_token, const blink::LocalFrameToken& frame_token,
V8DetailedMemoryRequest::MeasurementMode mode,
MeasurementCallback callback) MeasurementCallback callback)
: frame_token_(frame_token), callback_(std::move(callback)) {} : frame_token_(frame_token),
callback_(std::move(callback)),
request_(std::make_unique<V8DetailedMemoryRequestOneShot>(mode)) {}
WebMemoryAggregator::~WebMemoryAggregator() = default; WebMemoryAggregator::~WebMemoryAggregator() = default;
...@@ -107,17 +113,6 @@ void WebMemoryAggregator::MeasurementComplete( ...@@ -107,17 +113,6 @@ void WebMemoryAggregator::MeasurementComplete(
const ProcessNode* process_node, const ProcessNode* process_node,
const V8DetailedMemoryProcessData*) { const V8DetailedMemoryProcessData*) {
std::move(callback_).Run(BuildMemoryUsageResult(frame_token_, process_node)); std::move(callback_).Run(BuildMemoryUsageResult(frame_token_, process_node));
self_reference_.reset();
}
void WebMemoryAggregator::MakeSelfOwned(
std::unique_ptr<WebMemoryAggregator> web_memory_aggregator,
std::unique_ptr<V8DetailedMemoryRequestOneShot> request) {
// Stash the request to ensure that it lives until the measurement is done.
web_memory_aggregator->request_ = std::move(request);
// Transfer the ownership to itself to make it self-owned. This reference will
// be reset in MeasurementCompleted.
web_memory_aggregator->self_reference_ = std::move(web_memory_aggregator);
} }
} // namespace v8_memory } // namespace v8_memory
......
...@@ -9,6 +9,7 @@ ...@@ -9,6 +9,7 @@
#include "base/callback.h" #include "base/callback.h"
#include "components/performance_manager/public/mojom/web_memory.mojom.h" #include "components/performance_manager/public/mojom/web_memory.mojom.h"
#include "components/performance_manager/public/v8_memory/v8_detailed_memory.h"
#include "third_party/blink/public/common/tokens/tokens.h" #include "third_party/blink/public/common/tokens/tokens.h"
namespace performance_manager { namespace performance_manager {
...@@ -17,34 +18,28 @@ class ProcessNode; ...@@ -17,34 +18,28 @@ class ProcessNode;
namespace v8_memory { namespace v8_memory {
class V8DetailedMemoryRequestOneShot;
class V8DetailedMemoryProcessData;
// A helper class for implementing WebMeasureMemory(). // A helper class for implementing WebMeasureMemory().
class WebMemoryAggregator { class WebMemoryAggregator {
public: public:
using MeasurementCallback = using MeasurementCallback =
base::OnceCallback<void(mojom::WebMemoryMeasurementPtr)>; base::OnceCallback<void(mojom::WebMemoryMeasurementPtr)>;
WebMemoryAggregator(const blink::LocalFrameToken&, MeasurementCallback); WebMemoryAggregator(const blink::LocalFrameToken&,
V8DetailedMemoryRequest::MeasurementMode,
MeasurementCallback);
~WebMemoryAggregator(); ~WebMemoryAggregator();
V8DetailedMemoryRequestOneShot* request() const { return request_.get(); }
// A callback for V8DetailedMemoryRequestOneShot. // A callback for V8DetailedMemoryRequestOneShot.
void MeasurementComplete(const ProcessNode*, void MeasurementComplete(const ProcessNode*,
const V8DetailedMemoryProcessData*); const V8DetailedMemoryProcessData*);
// Transfer ownership of the given request to the given WebMemoryAggregator
// and makes the latter self-owned.
static void MakeSelfOwned(std::unique_ptr<WebMemoryAggregator>,
std::unique_ptr<V8DetailedMemoryRequestOneShot>);
private: private:
blink::LocalFrameToken frame_token_; blink::LocalFrameToken frame_token_;
MeasurementCallback callback_; MeasurementCallback callback_;
std::unique_ptr<V8DetailedMemoryRequestOneShot> request_; std::unique_ptr<V8DetailedMemoryRequestOneShot> request_;
// WebMemory is self-owned and lives until the measurement request
// is completed or failed.
std::unique_ptr<WebMemoryAggregator> self_reference_;
}; };
} // namespace v8_memory } // namespace v8_memory
......
...@@ -97,7 +97,7 @@ void WebMemoryAggregatorTest::MeasureAndVerify( ...@@ -97,7 +97,7 @@ void WebMemoryAggregatorTest::MeasureAndVerify(
base::flat_map<std::string, Bytes> expected) { base::flat_map<std::string, Bytes> expected) {
bool measurement_done = false; bool measurement_done = false;
WebMemoryAggregator web_memory( WebMemoryAggregator web_memory(
frame->frame_token(), frame->frame_token(), V8DetailedMemoryRequest::MeasurementMode::kDefault,
base::BindLambdaForTesting([&measurement_done, &expected]( base::BindLambdaForTesting([&measurement_done, &expected](
mojom::WebMemoryMeasurementPtr result) { mojom::WebMemoryMeasurementPtr result) {
base::flat_map<std::string, Bytes> actual; base::flat_map<std::string, Bytes> actual;
...@@ -146,8 +146,8 @@ TEST_F(WebMemoryAggregatorPMTest, WebMeasureMemory) { ...@@ -146,8 +146,8 @@ TEST_F(WebMemoryAggregatorPMTest, WebMeasureMemory) {
blink::LocalFrameToken frame_token = blink::LocalFrameToken frame_token =
blink::LocalFrameToken(main_frame()->GetFrameToken()); blink::LocalFrameToken(main_frame()->GetFrameToken());
// Call WebMemory::Measure on the performance manager sequence and verify // Call WebMeasureMemory on the performance manager sequence and verify that
// that the result matches the data provided by the mock reporter. // the result matches the data provided by the mock reporter.
base::RunLoop run_loop; base::RunLoop run_loop;
auto measurement_callback = auto measurement_callback =
base::BindLambdaForTesting([&](mojom::WebMemoryMeasurementPtr result) { base::BindLambdaForTesting([&](mojom::WebMemoryMeasurementPtr result) {
...@@ -184,6 +184,53 @@ TEST_F(WebMemoryAggregatorPMTest, WebMeasureMemory) { ...@@ -184,6 +184,53 @@ TEST_F(WebMemoryAggregatorPMTest, WebMeasureMemory) {
run_loop.Run(); run_loop.Run();
} }
TEST_F(WebMemoryAggregatorPMTest, MeasurementInterrupted) {
CreateCrossProcessChildFrame();
blink::LocalFrameToken frame_token =
blink::LocalFrameToken(child_frame()->GetFrameToken());
// Call WebMeasureMemory on the performance manager sequence but delete the
// process being measured before the result arrives.
auto measurement_callback =
base::BindOnce([](mojom::WebMemoryMeasurementPtr result) {
FAIL() << "Measurement callback ran unexpectedly";
});
base::WeakPtr<FrameNode> frame_node_wrapper =
PerformanceManager::GetFrameNodeForRenderFrameHost(child_frame());
PerformanceManager::CallOnGraph(
FROM_HERE, base::BindLambdaForTesting([&]() {
ASSERT_TRUE(frame_node_wrapper);
FrameNode* frame_node = frame_node_wrapper.get();
WebMeasureMemory(frame_node,
mojom::WebMemoryMeasurement::Mode::kDefault,
std::move(measurement_callback));
}));
// Set up and bind the mock reporter.
MockV8DetailedMemoryReporter mock_reporter;
{
::testing::InSequence seq;
ExpectBindReceiver(&mock_reporter, child_process_id());
auto data = NewPerProcessV8MemoryUsage(1);
AddIsolateMemoryUsage(frame_token, 1001u, data->isolates[0].get());
ExpectQueryAndDelayReply(&mock_reporter, base::TimeDelta::FromSeconds(10),
std::move(data));
}
// Verify that requests are sent but reply is not yet received.
task_environment()->FastForwardBy(base::TimeDelta::FromSeconds(5));
::testing::Mock::VerifyAndClearExpectations(&mock_reporter);
// Remove the child frame, which will destroy the child process.
content::RenderFrameHostTester::For(child_frame())->Detach();
// Advance until the reply is expected to make sure nothing explodes.
task_environment()->FastForwardBy(base::TimeDelta::FromSeconds(5));
}
} // namespace v8_memory } // namespace v8_memory
} // namespace performance_manager } // 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