Commit c11c8757 authored by Ulan Degenbaev's avatar Ulan Degenbaev Committed by Commit Bot

Add worker memory measurement to V8DetailedMemoryReporterImpl

Now V8DetailedMemoryReporterImpl::GetV8MemoryUsage measures the memory
usage of the main V8 isolate and all workers in the renderer process.

Bug:1085129

Change-Id: I9522e73ba8e88abc5e2679702ac87ca19c3abb58
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2409914
Commit-Queue: Ulan Degenbaev <ulan@chromium.org>
Reviewed-by: default avatarChris Hamilton <chrisha@chromium.org>
Cr-Commit-Position: refs/heads/master@{#808320}
parent 3ae46cef
...@@ -8,13 +8,19 @@ ...@@ -8,13 +8,19 @@
#include <utility> #include <utility>
#include <vector> #include <vector>
#include "base/callback.h"
#include "base/check.h" #include "base/check.h"
#include "base/memory/scoped_refptr.h"
#include "base/notreached.h" #include "base/notreached.h"
#include "mojo/public/cpp/bindings/self_owned_receiver.h" #include "mojo/public/cpp/bindings/self_owned_receiver.h"
#include "third_party/blink/public/common/tokens/tokens.h"
#include "third_party/blink/renderer/bindings/core/v8/v8_binding_for_core.h" #include "third_party/blink/renderer/bindings/core/v8/v8_binding_for_core.h"
#include "third_party/blink/renderer/controller/performance_manager/v8_worker_memory_reporter.h"
#include "third_party/blink/renderer/core/frame/local_dom_window.h" #include "third_party/blink/renderer/core/frame/local_dom_window.h"
#include "third_party/blink/renderer/core/frame/local_frame.h" #include "third_party/blink/renderer/core/frame/local_frame.h"
#include "third_party/blink/renderer/platform/bindings/dom_wrapper_world.h" #include "third_party/blink/renderer/platform/bindings/dom_wrapper_world.h"
#include "third_party/blink/renderer/platform/wtf/functional.h"
#include "third_party/blink/renderer/platform/wtf/ref_counted.h"
#include "v8/include/v8.h" #include "v8/include/v8.h"
namespace blink { namespace blink {
...@@ -23,16 +29,15 @@ namespace { ...@@ -23,16 +29,15 @@ namespace {
class FrameAssociatedMeasurementDelegate : public v8::MeasureMemoryDelegate { class FrameAssociatedMeasurementDelegate : public v8::MeasureMemoryDelegate {
public: public:
using GetV8MemoryUsageCallback = using ResultCallback =
mojom::blink::V8DetailedMemoryReporter::GetV8MemoryUsageCallback; base::OnceCallback<void(mojom::blink::PerIsolateV8MemoryUsagePtr)>;
explicit FrameAssociatedMeasurementDelegate( explicit FrameAssociatedMeasurementDelegate(ResultCallback&& callback)
GetV8MemoryUsageCallback&& callback)
: callback_(std::move(callback)) {} : callback_(std::move(callback)) {}
~FrameAssociatedMeasurementDelegate() override { ~FrameAssociatedMeasurementDelegate() override {
if (callback_) { if (callback_) {
std::move(callback_).Run(mojom::blink::PerProcessV8MemoryUsage::New()); std::move(callback_).Run(mojom::blink::PerIsolateV8MemoryUsage::New());
} }
} }
...@@ -80,14 +85,11 @@ class FrameAssociatedMeasurementDelegate : public v8::MeasureMemoryDelegate { ...@@ -80,14 +85,11 @@ class FrameAssociatedMeasurementDelegate : public v8::MeasureMemoryDelegate {
#endif #endif
isolate_memory_usage->contexts.push_back(std::move(context_memory_usage)); isolate_memory_usage->contexts.push_back(std::move(context_memory_usage));
} }
std::move(callback_).Run(std::move(isolate_memory_usage));
mojom::blink::PerProcessV8MemoryUsagePtr result =
mojom::blink::PerProcessV8MemoryUsage::New();
result->isolates.push_back(std::move(isolate_memory_usage));
std::move(callback_).Run(std::move(result));
} }
GetV8MemoryUsageCallback callback_; private:
ResultCallback callback_;
}; };
v8::MeasureMemoryExecution ToV8MeasureMemoryExecution( v8::MeasureMemoryExecution ToV8MeasureMemoryExecution(
...@@ -103,6 +105,82 @@ v8::MeasureMemoryExecution ToV8MeasureMemoryExecution( ...@@ -103,6 +105,82 @@ v8::MeasureMemoryExecution ToV8MeasureMemoryExecution(
NOTREACHED(); NOTREACHED();
} }
ExecutionContextToken ToExecutionContextToken(WorkerToken token) {
if (token.Is<DedicatedWorkerToken>())
return ExecutionContextToken(token.GetAs<DedicatedWorkerToken>());
if (token.Is<SharedWorkerToken>())
return ExecutionContextToken(token.GetAs<SharedWorkerToken>());
return ExecutionContextToken(token.GetAs<ServiceWorkerToken>());
}
// A helper class that runs two async functions, combines their
// results, and invokes the given callback. The async functions are:
// - v8::Isolate::MeasureMemory - for the main V8 isolate.
// - V8WorkerMemoryReporter::GetMemoryUsage - for all worker isolates.
class V8ProcessMemoryReporter : public RefCounted<V8ProcessMemoryReporter> {
public:
using GetV8MemoryUsageCallback =
mojom::blink::V8DetailedMemoryReporter::GetV8MemoryUsageCallback;
explicit V8ProcessMemoryReporter(GetV8MemoryUsageCallback&& callback)
: callback_(std::move(callback)),
result_(mojom::blink::PerProcessV8MemoryUsage::New()) {}
void StartMeasurements(V8DetailedMemoryReporterImpl::Mode mode) {
v8::Isolate* isolate = v8::Isolate::GetCurrent();
// 1. Start measurement of the main V8 isolate.
if (!isolate) {
// This can happen in tests that do not set up the main V8 isolate
// or during setup/teardown of the process.
MainMeasurementComplete(mojom::blink::PerIsolateV8MemoryUsage::New());
} else {
auto delegate = std::make_unique<FrameAssociatedMeasurementDelegate>(
WTF::Bind(&V8ProcessMemoryReporter::MainMeasurementComplete,
scoped_refptr<V8ProcessMemoryReporter>(this)));
isolate->MeasureMemory(std::move(delegate),
ToV8MeasureMemoryExecution(mode));
}
// 2. Start measurement of all worker isolates.
V8WorkerMemoryReporter::GetMemoryUsage(
WTF::Bind(&V8ProcessMemoryReporter::WorkerMeasurementComplete,
scoped_refptr<V8ProcessMemoryReporter>(this)),
ToV8MeasureMemoryExecution(mode));
}
private:
void MainMeasurementComplete(
mojom::blink::PerIsolateV8MemoryUsagePtr isolate_memory_usage) {
result_->isolates.push_back(std::move(isolate_memory_usage));
main_measurement_done_ = true;
MaybeInvokeCallback();
}
void WorkerMeasurementComplete(const V8WorkerMemoryReporter::Result& result) {
for (auto& worker : result.workers) {
auto worker_memory_usage = mojom::blink::PerIsolateV8MemoryUsage::New();
auto context_memory_usage = mojom::blink::PerContextV8MemoryUsage::New();
context_memory_usage->token = ToExecutionContextToken(worker.token);
context_memory_usage->bytes_used = worker.bytes;
worker_memory_usage->contexts.push_back(std::move(context_memory_usage));
result_->isolates.push_back(std::move(worker_memory_usage));
}
worker_measurement_done_ = true;
MaybeInvokeCallback();
}
void MaybeInvokeCallback() {
if (!main_measurement_done_ || !worker_measurement_done_)
return;
std::move(callback_).Run(std::move(result_));
}
GetV8MemoryUsageCallback callback_;
mojom::blink::PerProcessV8MemoryUsagePtr result_;
bool main_measurement_done_ = false;
bool worker_measurement_done_ = false;
};
} // namespace } // namespace
// static // static
...@@ -115,17 +193,11 @@ void V8DetailedMemoryReporterImpl::Create( ...@@ -115,17 +193,11 @@ void V8DetailedMemoryReporterImpl::Create(
void V8DetailedMemoryReporterImpl::GetV8MemoryUsage( void V8DetailedMemoryReporterImpl::GetV8MemoryUsage(
V8DetailedMemoryReporterImpl::Mode mode, V8DetailedMemoryReporterImpl::Mode mode,
GetV8MemoryUsageCallback callback) { GetV8MemoryUsageCallback callback) {
v8::Isolate* isolate = v8::Isolate::GetCurrent(); auto v8_process_memory_reporter =
if (!isolate) { base::MakeRefCounted<V8ProcessMemoryReporter>(std::move(callback));
std::move(callback).Run(mojom::blink::PerProcessV8MemoryUsage::New()); // Start async measurements. The lifetime of the reporter is extended
} else { // using more shared pointers until the measuremnts complete.
std::unique_ptr<FrameAssociatedMeasurementDelegate> delegate = v8_process_memory_reporter->StartMeasurements(mode);
std::make_unique<FrameAssociatedMeasurementDelegate>(
std::move(callback));
isolate->MeasureMemory(std::move(delegate),
ToV8MeasureMemoryExecution(mode));
}
} }
} // namespace blink } // namespace blink
...@@ -8,6 +8,7 @@ ...@@ -8,6 +8,7 @@
#include "third_party/blink/renderer/core/testing/sim/sim_compositor.h" #include "third_party/blink/renderer/core/testing/sim/sim_compositor.h"
#include "third_party/blink/renderer/core/testing/sim/sim_request.h" #include "third_party/blink/renderer/core/testing/sim/sim_request.h"
#include "third_party/blink/renderer/core/testing/sim/sim_test.h" #include "third_party/blink/renderer/core/testing/sim/sim_test.h"
#include "third_party/blink/renderer/core/workers/dedicated_worker_test.h"
#include "third_party/blink/renderer/platform/testing/unit_test_helpers.h" #include "third_party/blink/renderer/platform/testing/unit_test_helpers.h"
#include "v8/include/v8.h" #include "v8/include/v8.h"
...@@ -15,21 +16,39 @@ namespace blink { ...@@ -15,21 +16,39 @@ namespace blink {
class V8DetailedMemoryReporterImplTest : public SimTest {}; class V8DetailedMemoryReporterImplTest : public SimTest {};
class V8DetailedMemoryReporterImplWorkerTest : public DedicatedWorkerTest {};
namespace { namespace {
class MemoryUsageChecker { class MemoryUsageChecker {
public: public:
enum class CallbackAction { kExitRunLoop, kNone };
MemoryUsageChecker(size_t expected_isolate_count,
size_t expected_context_count)
: expected_isolate_count_(expected_isolate_count),
expected_context_count_(expected_context_count) {}
void Callback(mojom::blink::PerProcessV8MemoryUsagePtr result) { void Callback(mojom::blink::PerProcessV8MemoryUsagePtr result) {
EXPECT_EQ(1u, result->isolates.size()); EXPECT_EQ(expected_isolate_count_, result->isolates.size());
EXPECT_EQ(2u, result->isolates[0]->contexts.size()); size_t actual_context_count = 0;
for (const auto& entry : result->isolates[0]->contexts) { for (const auto& isolate : result->isolates) {
EXPECT_LT(4000000u, entry->bytes_used); for (const auto& entry : isolate->contexts) {
// Each context allocates an array of 1000000u elements, thus 4000000u
// is a lower bound of the memory usage on any platform. We cannot make
// this check more strict without making the test fragile.
EXPECT_LT(4000000u, entry->bytes_used);
++actual_context_count;
}
} }
EXPECT_EQ(expected_context_count_, actual_context_count);
called_ = true; called_ = true;
test::ExitRunLoop();
} }
bool IsCalled() { return called_; } bool IsCalled() { return called_; }
private: private:
size_t expected_isolate_count_;
size_t expected_context_count_;
bool called_ = false; bool called_ = false;
}; };
...@@ -73,13 +92,36 @@ TEST_F(V8DetailedMemoryReporterImplTest, GetV8MemoryUsage) { ...@@ -73,13 +92,36 @@ TEST_F(V8DetailedMemoryReporterImplTest, GetV8MemoryUsage) {
EXPECT_TRUE(ConsoleMessages().Contains("iframe loaded")); EXPECT_TRUE(ConsoleMessages().Contains("iframe loaded"));
V8DetailedMemoryReporterImpl reporter; V8DetailedMemoryReporterImpl reporter;
MemoryUsageChecker checker; // We expect to see the main isolate with two contexts corresponding to
// the main page and the iframe.
size_t expected_isolate_count = 1;
size_t expected_context_count = 2;
MemoryUsageChecker checker(expected_isolate_count, expected_context_count);
reporter.GetV8MemoryUsage( reporter.GetV8MemoryUsage(
V8DetailedMemoryReporterImpl::Mode::EAGER, V8DetailedMemoryReporterImpl::Mode::EAGER,
WTF::Bind(&MemoryUsageChecker::Callback, WTF::Unretained(&checker))); WTF::Bind(&MemoryUsageChecker::Callback, WTF::Unretained(&checker)));
test::RunPendingTasks(); test::EnterRunLoop();
EXPECT_TRUE(checker.IsCalled()); EXPECT_TRUE(checker.IsCalled());
} }
TEST_F(V8DetailedMemoryReporterImplWorkerTest, GetV8MemoryUsage) {
const String source_code = "globalThis.array = new Array(1000000).fill(0);";
StartWorker(source_code);
WaitUntilWorkerIsRunning();
V8DetailedMemoryReporterImpl reporter;
// We expect to see two isolates: the main isolate and the worker isolate.
// Only the worker isolate has a context. The main isolate is empty because
// DedicatedWorkerTest does not set it up.
size_t expected_isolate_count = 2;
size_t expected_context_count = 1;
MemoryUsageChecker checker(expected_isolate_count, expected_context_count);
reporter.GetV8MemoryUsage(
V8DetailedMemoryReporterImpl::Mode::EAGER,
WTF::Bind(&MemoryUsageChecker::Callback, WTF::Unretained(&checker)));
test::EnterRunLoop();
EXPECT_TRUE(checker.IsCalled());
}
} // namespace blink } // namespace blink
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