// 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 GetNativeContext(Isolate* isolate, v8::Local v8_context) { Handle 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 native_context = GetNativeContext(isolate, env.local()); Handle 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 native_context = GetNativeContext(isolate, env.local()); v8::Local result = CompileRun("(function () { return 1; })"); Handle object = Utils::OpenHandle(*result); Handle function = Handle::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 native_context = GetNativeContext(isolate, env.local()); v8::Local result = CompileRun("({a : 10})"); Handle object = Utils::OpenHandle(*result); Handle function = Handle::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 native_context = GetNativeContext(isolate, env.local()); v8::Local result = CompileRun("({a : 10})"); Handle object = Handle::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 native_context = GetNativeContext(isolate, env.local()); v8::Local array_buffer = v8::ArrayBuffer::New(CcTest::isolate(), 1000); Handle 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 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 string = v8::String::NewExternalTwoByte(CcTest::isolate(), resource) .ToLocalChecked(); Handle 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 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 task) override {} void PostDelayedTask(std::unique_ptr task, double delay_in_seconds) override { task_ = std::move(task); delay_ = delay_in_seconds; } void PostIdleTask(std::unique_ptr 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 = std::move(task_); task->Run(); } bool TaskPosted() { return task_.get(); } private: double delay_ = -1; std::unique_ptr task_; }; std::shared_ptr mock_task_runner_; }; class MockMeasureMemoryDelegate : public v8::MeasureMemoryDelegate { public: bool ShouldMeasure(v8::Local context) override { return true; } void MeasurementComplete( const std::vector, 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 delays; for (int i = 0; i < 10; i++) { isolate->MeasureMemory(std::make_unique()); 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(), v8::MeasureMemoryExecution::kLazy); CHECK(!platform.TaskPosted()); } TEST(PartiallyInitializedJSFunction) { LocalContext env; Isolate* isolate = CcTest::i_isolate(); Factory* factory = isolate->factory(); HandleScope scope(isolate); Handle js_function = factory->NewFunctionForTesting( factory->NewStringFromAsciiChecked("test")); Handle context = handle(js_function->context(), isolate); // 1. Start simulating deserializaiton. isolate->RegisterDeserializerStarted(); // 2. Set the context field to the uninitialized sentintel. TaggedField::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(), v8::MeasureMemoryExecution::kEager); while (v8::platform::PumpMessageLoop(v8::internal::V8::GetCurrentPlatform(), CcTest::isolate())) { } // 4. Restore the value and complete deserialization. TaggedField::store(*js_function, *context); isolate->RegisterDeserializerFinished(); } TEST(PartiallyInitializedContext) { LocalContext env; Isolate* isolate = CcTest::i_isolate(); Factory* factory = isolate->factory(); HandleScope scope(isolate); Handle scope_info = ReadOnlyRoots(isolate).global_this_binding_scope_info_handle(); Handle context = factory->NewScriptContext( GetNativeContext(isolate, env.local()), scope_info); Handle map = handle(context->map(), isolate); Handle native_context = handle(map->native_context(), isolate); // 1. Start simulating deserializaiton. isolate->RegisterDeserializerStarted(); // 2. Set the native context field to the uninitialized sentintel. TaggedField:: 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(), v8::MeasureMemoryExecution::kEager); while (v8::platform::PumpMessageLoop(v8::internal::V8::GetCurrentPlatform(), CcTest::isolate())) { } // 4. Restore the value and complete deserialization. TaggedField:: store(*map, *native_context); isolate->RegisterDeserializerFinished(); } } // namespace heap } // namespace internal } // namespace v8