v8/test/cctest/heap/test-memory-measurement.cc
Leszek Swirski 49c507dc99 [test] Make cctest run one test, with maybe custom platform
Remove cctest's ability to run multiple tests (which has long been
deprecated and mostly broken). We can then make platform & V8
initialisation be part of running the test's Run method.

In particular, this allows us to inject custom logic into the platform
initialisation, like setting up a platform wrapper. Add a
TEST_WITH_PLATFORM which exercises this by registering a platform
factory on the test, and wrapping the default platform using this
factory. This allows these tests to guarantee that the lifetime of the
platform is longer than the lifetime of the isolate.

As a result of this, we can also remove the complexity around draining
platform state in the TestPlatform (since it will now have a longer
lifetime than the Isolate using it), and as a drive-by clean up the
TestPlaform to use a CcTest-global "default platform" instead of trying
to scope over the "current" platform.

As another drive-by, change the linked-list of CcTests and the linear
search through it into an std::map of tests.

Change-Id: I610f6312fe042f29f45cc4dfba311e4184bc7759
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3569223
Reviewed-by: Michael Lippautz <mlippautz@chromium.org>
Commit-Queue: Leszek Swirski <leszeks@chromium.org>
Cr-Commit-Position: refs/heads/main@{#79772}
2022-04-05 09:39:18 +00:00

283 lines
9.8 KiB
C++

// Copyright 2019 the V8 project 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 "src/heap/memory-measurement-inl.h"
#include "src/heap/memory-measurement.h"
#include "src/objects/smi.h"
#include "test/cctest/cctest.h"
#include "test/cctest/heap/heap-tester.h"
#include "test/cctest/heap/heap-utils.h"
namespace v8 {
namespace internal {
namespace heap {
namespace {
Handle<NativeContext> GetNativeContext(Isolate* isolate,
v8::Local<v8::Context> v8_context) {
Handle<Context> context = v8::Utils::OpenHandle(*v8_context);
return handle(context->native_context(), isolate);
}
} // anonymous namespace
TEST(NativeContextInferrerGlobalObject) {
LocalContext env;
Isolate* isolate = CcTest::i_isolate();
HandleScope handle_scope(isolate);
Handle<NativeContext> native_context = GetNativeContext(isolate, env.local());
Handle<JSGlobalObject> global =
handle(native_context->global_object(), isolate);
NativeContextInferrer inferrer;
Address inferred_context = 0;
CHECK(inferrer.Infer(isolate, global->map(), *global, &inferred_context));
CHECK_EQ(native_context->ptr(), inferred_context);
}
TEST(NativeContextInferrerJSFunction) {
LocalContext env;
Isolate* isolate = CcTest::i_isolate();
HandleScope scope(isolate);
Handle<NativeContext> native_context = GetNativeContext(isolate, env.local());
v8::Local<v8::Value> result = CompileRun("(function () { return 1; })");
Handle<Object> object = Utils::OpenHandle(*result);
Handle<HeapObject> function = Handle<HeapObject>::cast(object);
NativeContextInferrer inferrer;
Address inferred_context = 0;
CHECK(inferrer.Infer(isolate, function->map(), *function, &inferred_context));
CHECK_EQ(native_context->ptr(), inferred_context);
}
TEST(NativeContextInferrerJSObject) {
LocalContext env;
Isolate* isolate = CcTest::i_isolate();
HandleScope scope(isolate);
Handle<NativeContext> native_context = GetNativeContext(isolate, env.local());
v8::Local<v8::Value> result = CompileRun("({a : 10})");
Handle<Object> object = Utils::OpenHandle(*result);
Handle<HeapObject> function = Handle<HeapObject>::cast(object);
NativeContextInferrer inferrer;
Address inferred_context = 0;
CHECK(inferrer.Infer(isolate, function->map(), *function, &inferred_context));
CHECK_EQ(native_context->ptr(), inferred_context);
}
TEST(NativeContextStatsMerge) {
LocalContext env;
Isolate* isolate = CcTest::i_isolate();
HandleScope scope(isolate);
Handle<NativeContext> native_context = GetNativeContext(isolate, env.local());
v8::Local<v8::Value> result = CompileRun("({a : 10})");
Handle<HeapObject> object =
Handle<HeapObject>::cast(Utils::OpenHandle(*result));
NativeContextStats stats1, stats2;
stats1.IncrementSize(native_context->ptr(), object->map(), *object, 10);
stats2.IncrementSize(native_context->ptr(), object->map(), *object, 20);
stats1.Merge(stats2);
CHECK_EQ(30, stats1.Get(native_context->ptr()));
}
TEST(NativeContextStatsArrayBuffers) {
LocalContext env;
Isolate* isolate = CcTest::i_isolate();
HandleScope scope(isolate);
Handle<NativeContext> native_context = GetNativeContext(isolate, env.local());
v8::Local<v8::ArrayBuffer> array_buffer =
v8::ArrayBuffer::New(CcTest::isolate(), 1000);
Handle<JSArrayBuffer> i_array_buffer = Utils::OpenHandle(*array_buffer);
NativeContextStats stats;
stats.IncrementSize(native_context->ptr(), i_array_buffer->map(),
*i_array_buffer, 10);
CHECK_EQ(1010, stats.Get(native_context->ptr()));
}
namespace {
class TestResource : public v8::String::ExternalStringResource {
public:
explicit TestResource(uint16_t* data) : data_(data), length_(0) {
while (data[length_]) ++length_;
}
~TestResource() override { i::DeleteArray(data_); }
const uint16_t* data() const override { return data_; }
size_t length() const override { return length_; }
private:
uint16_t* data_;
size_t length_;
};
} // anonymous namespace
TEST(NativeContextStatsExternalString) {
LocalContext env;
Isolate* isolate = CcTest::i_isolate();
HandleScope scope(isolate);
Handle<NativeContext> native_context = GetNativeContext(isolate, env.local());
const char* c_source = "0123456789";
uint16_t* two_byte_source = AsciiToTwoByteString(c_source);
TestResource* resource = new TestResource(two_byte_source);
Local<v8::String> string =
v8::String::NewExternalTwoByte(CcTest::isolate(), resource)
.ToLocalChecked();
Handle<String> i_string = Utils::OpenHandle(*string);
NativeContextStats stats;
stats.IncrementSize(native_context->ptr(), i_string->map(), *i_string, 10);
CHECK_EQ(10 + 10 * 2, stats.Get(native_context->ptr()));
}
namespace {
class MockPlatform : public TestPlatform {
public:
MockPlatform() : mock_task_runner_(new MockTaskRunner()) {}
std::shared_ptr<v8::TaskRunner> GetForegroundTaskRunner(
v8::Isolate*) override {
return mock_task_runner_;
}
double Delay() { return mock_task_runner_->Delay(); }
void PerformTask() { mock_task_runner_->PerformTask(); }
bool TaskPosted() { return mock_task_runner_->TaskPosted(); }
private:
class MockTaskRunner : public v8::TaskRunner {
public:
void PostTask(std::unique_ptr<v8::Task> task) override {}
void PostDelayedTask(std::unique_ptr<Task> task,
double delay_in_seconds) override {
task_ = std::move(task);
delay_ = delay_in_seconds;
}
void PostIdleTask(std::unique_ptr<IdleTask> task) override {
UNREACHABLE();
}
bool NonNestableTasksEnabled() const override { return true; }
bool NonNestableDelayedTasksEnabled() const override { return true; }
bool IdleTasksEnabled() override { return false; }
double Delay() { return delay_; }
void PerformTask() {
std::unique_ptr<Task> task = std::move(task_);
task->Run();
}
bool TaskPosted() { return task_.get(); }
private:
double delay_ = -1;
std::unique_ptr<Task> task_;
};
std::shared_ptr<MockTaskRunner> mock_task_runner_;
};
class MockMeasureMemoryDelegate : public v8::MeasureMemoryDelegate {
public:
bool ShouldMeasure(v8::Local<v8::Context> context) override { return true; }
void MeasurementComplete(
const std::vector<std::pair<v8::Local<v8::Context>, size_t>>&
context_sizes_in_bytes,
size_t unattributed_size_in_bytes) override {
// Empty.
}
};
} // namespace
TEST_WITH_PLATFORM(RandomizedTimeout, MockPlatform) {
v8::Isolate::CreateParams create_params;
create_params.array_buffer_allocator = CcTest::array_buffer_allocator();
v8::Isolate* isolate = CcTest::isolate();
std::vector<double> delays;
for (int i = 0; i < 10; i++) {
isolate->MeasureMemory(std::make_unique<MockMeasureMemoryDelegate>());
delays.push_back(platform.Delay());
platform.PerformTask();
}
std::sort(delays.begin(), delays.end());
CHECK_LT(delays[0], delays.back());
}
TEST(LazyMemoryMeasurement) {
CcTest::InitializeVM();
MockPlatform platform;
CcTest::isolate()->MeasureMemory(
std::make_unique<MockMeasureMemoryDelegate>(),
v8::MeasureMemoryExecution::kLazy);
CHECK(!platform.TaskPosted());
}
TEST(PartiallyInitializedJSFunction) {
LocalContext env;
Isolate* isolate = CcTest::i_isolate();
Factory* factory = isolate->factory();
HandleScope scope(isolate);
Handle<JSFunction> js_function = factory->NewFunctionForTesting(
factory->NewStringFromAsciiChecked("test"));
Handle<Context> context = handle(js_function->context(), isolate);
// 1. Start simulating deserializaiton.
isolate->RegisterDeserializerStarted();
// 2. Set the context field to the uninitialized sentintel.
TaggedField<Object, JSFunction::kContextOffset>::store(
*js_function, Smi::uninitialized_deserialization_value());
// 3. Request memory meaurement and run all tasks. GC that runs as part
// of the measurement should not crash.
CcTest::isolate()->MeasureMemory(
std::make_unique<MockMeasureMemoryDelegate>(),
v8::MeasureMemoryExecution::kEager);
while (v8::platform::PumpMessageLoop(v8::internal::V8::GetCurrentPlatform(),
CcTest::isolate())) {
}
// 4. Restore the value and complete deserialization.
TaggedField<Object, JSFunction::kContextOffset>::store(*js_function,
*context);
isolate->RegisterDeserializerFinished();
}
TEST(PartiallyInitializedContext) {
LocalContext env;
Isolate* isolate = CcTest::i_isolate();
Factory* factory = isolate->factory();
HandleScope scope(isolate);
Handle<ScopeInfo> scope_info =
ReadOnlyRoots(isolate).global_this_binding_scope_info_handle();
Handle<Context> context = factory->NewScriptContext(
GetNativeContext(isolate, env.local()), scope_info);
Handle<Map> map = handle(context->map(), isolate);
Handle<NativeContext> native_context = handle(map->native_context(), isolate);
// 1. Start simulating deserializaiton.
isolate->RegisterDeserializerStarted();
// 2. Set the native context field to the uninitialized sentintel.
TaggedField<Object, Map::kConstructorOrBackPointerOrNativeContextOffset>::
store(*map, Smi::uninitialized_deserialization_value());
// 3. Request memory meaurement and run all tasks. GC that runs as part
// of the measurement should not crash.
CcTest::isolate()->MeasureMemory(
std::make_unique<MockMeasureMemoryDelegate>(),
v8::MeasureMemoryExecution::kEager);
while (v8::platform::PumpMessageLoop(v8::internal::V8::GetCurrentPlatform(),
CcTest::isolate())) {
}
// 4. Restore the value and complete deserialization.
TaggedField<Object, Map::kConstructorOrBackPointerOrNativeContextOffset>::
store(*map, *native_context);
isolate->RegisterDeserializerFinished();
}
} // namespace heap
} // namespace internal
} // namespace v8