v8/test/cctest/heap/test-external-string-tracker.cc
Dominik Inführ 9b78e758af [heap] Make creation of NewSpace and NewLargeObjectSpace optional
Both NewSpace and NewLargeObjectSpace aren't used with
FLAG_single_generation enabled. So far both spaces still existed but
weren't used in this mode. This CL makes both spaces optional, which
ensure that we do not inadvertently create objects in them or use them
in any other way.

Bug: v8:11644
Change-Id: I52a449c62e9d3df126c95419433d2abbd75539a5
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2862768
Commit-Queue: Dominik Inführ <dinfuehr@chromium.org>
Reviewed-by: Ulan Degenbaev <ulan@chromium.org>
Cr-Commit-Position: refs/heads/master@{#74345}
2021-05-04 06:47:54 +00:00

229 lines
7.4 KiB
C++

// Copyright 2018 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/api/api-inl.h"
#include "src/api/api.h"
#include "src/execution/isolate.h"
#include "src/heap/heap-inl.h"
#include "src/heap/spaces.h"
#include "src/objects/objects-inl.h"
#include "test/cctest/cctest.h"
#include "test/cctest/heap/heap-tester.h"
#include "test/cctest/heap/heap-utils.h"
#define TEST_STR "tests are great!"
namespace v8 {
namespace internal {
namespace heap {
// Adapted from cctest/test-api.cc
class TestOneByteResource : public v8::String::ExternalOneByteStringResource {
public:
explicit TestOneByteResource(const char* data, int* counter = nullptr,
size_t offset = 0)
: orig_data_(data),
data_(data + offset),
length_(strlen(data) - offset),
counter_(counter) {}
~TestOneByteResource() override {
i::DeleteArray(orig_data_);
if (counter_ != nullptr) ++*counter_;
}
const char* data() const override { return data_; }
size_t length() const override { return length_; }
private:
const char* orig_data_;
const char* data_;
size_t length_;
int* counter_;
};
TEST(ExternalString_ExternalBackingStoreSizeIncreases) {
CcTest::InitializeVM();
LocalContext env;
v8::Isolate* isolate = env->GetIsolate();
Heap* heap = reinterpret_cast<Isolate*>(isolate)->heap();
ExternalBackingStoreType type = ExternalBackingStoreType::kExternalString;
const size_t backing_store_before =
heap->old_space()->ExternalBackingStoreBytes(type);
{
v8::HandleScope handle_scope(isolate);
v8::Local<v8::String> es = v8::String::NewExternalOneByte(
isolate, new TestOneByteResource(i::StrDup(TEST_STR))).ToLocalChecked();
USE(es);
const size_t backing_store_after =
heap->old_space()->ExternalBackingStoreBytes(type);
CHECK_EQ(es->Length(), backing_store_after - backing_store_before);
}
}
TEST(ExternalString_ExternalBackingStoreSizeDecreases) {
ManualGCScope manual_gc_scope;
CcTest::InitializeVM();
LocalContext env;
v8::Isolate* isolate = env->GetIsolate();
Heap* heap = reinterpret_cast<Isolate*>(isolate)->heap();
ExternalBackingStoreType type = ExternalBackingStoreType::kExternalString;
const size_t backing_store_before =
heap->old_space()->ExternalBackingStoreBytes(type);
{
v8::HandleScope handle_scope(isolate);
v8::Local<v8::String> es = v8::String::NewExternalOneByte(
isolate, new TestOneByteResource(i::StrDup(TEST_STR))).ToLocalChecked();
USE(es);
}
heap::GcAndSweep(heap, OLD_SPACE);
const size_t backing_store_after =
heap->old_space()->ExternalBackingStoreBytes(type);
CHECK_EQ(0, backing_store_after - backing_store_before);
}
TEST(ExternalString_ExternalBackingStoreSizeIncreasesMarkCompact) {
if (FLAG_never_compact) return;
ManualGCScope manual_gc_scope;
FLAG_manual_evacuation_candidates_selection = true;
CcTest::InitializeVM();
LocalContext env;
v8::Isolate* isolate = env->GetIsolate();
Heap* heap = reinterpret_cast<Isolate*>(isolate)->heap();
heap::AbandonCurrentlyFreeMemory(heap->old_space());
ExternalBackingStoreType type = ExternalBackingStoreType::kExternalString;
const size_t backing_store_before =
heap->old_space()->ExternalBackingStoreBytes(type);
{
v8::HandleScope handle_scope(isolate);
v8::Local<v8::String> es = v8::String::NewExternalOneByte(
isolate, new TestOneByteResource(i::StrDup(TEST_STR))).ToLocalChecked();
v8::internal::Handle<v8::internal::String> esh = v8::Utils::OpenHandle(*es);
Page* page_before_gc = Page::FromHeapObject(*esh);
heap::ForceEvacuationCandidate(page_before_gc);
CcTest::CollectAllGarbage();
const size_t backing_store_after =
heap->old_space()->ExternalBackingStoreBytes(type);
CHECK_EQ(es->Length(), backing_store_after - backing_store_before);
}
heap::GcAndSweep(heap, OLD_SPACE);
const size_t backing_store_after =
heap->old_space()->ExternalBackingStoreBytes(type);
CHECK_EQ(0, backing_store_after - backing_store_before);
}
TEST(ExternalString_ExternalBackingStoreSizeIncreasesAfterExternalization) {
if (FLAG_single_generation) return;
ManualGCScope manual_gc_scope;
CcTest::InitializeVM();
LocalContext env;
v8::Isolate* isolate = env->GetIsolate();
Heap* heap = reinterpret_cast<Isolate*>(isolate)->heap();
ExternalBackingStoreType type = ExternalBackingStoreType::kExternalString;
size_t old_backing_store_before = 0, new_backing_store_before = 0;
{
v8::HandleScope handle_scope(isolate);
new_backing_store_before =
heap->new_space()->ExternalBackingStoreBytes(type);
old_backing_store_before =
heap->old_space()->ExternalBackingStoreBytes(type);
// Allocate normal string in the new gen.
v8::Local<v8::String> str =
v8::String::NewFromUtf8Literal(isolate, TEST_STR);
CHECK_EQ(0, heap->new_space()->ExternalBackingStoreBytes(type) -
new_backing_store_before);
// Trigger GCs so that the newly allocated string moves to old gen.
heap::GcAndSweep(heap, NEW_SPACE); // in survivor space now
heap::GcAndSweep(heap, NEW_SPACE); // in old gen now
bool success =
str->MakeExternal(new TestOneByteResource(i::StrDup(TEST_STR)));
CHECK(success);
CHECK_EQ(str->Length(), heap->old_space()->ExternalBackingStoreBytes(type) -
old_backing_store_before);
}
heap::GcAndSweep(heap, OLD_SPACE);
CHECK_EQ(0, heap->old_space()->ExternalBackingStoreBytes(type) -
old_backing_store_before);
}
TEST(ExternalString_PromotedThinString) {
if (FLAG_single_generation) return;
ManualGCScope manual_gc_scope;
CcTest::InitializeVM();
LocalContext env;
v8::Isolate* isolate = env->GetIsolate();
i::Isolate* i_isolate = CcTest::i_isolate();
i::Factory* factory = i_isolate->factory();
Heap* heap = i_isolate->heap();
{
v8::HandleScope handle_scope(isolate);
// New external string in the old space.
v8::internal::Handle<v8::internal::String> string1 =
factory
->NewExternalStringFromOneByte(
new TestOneByteResource(i::StrDup(TEST_STR)))
.ToHandleChecked();
// Internalize external string.
i::Handle<i::String> isymbol1 = factory->InternalizeString(string1);
CHECK(isymbol1->IsInternalizedString());
CHECK(string1->IsExternalString());
CHECK(!heap->InYoungGeneration(*isymbol1));
// New external string in the young space. This string has the same content
// as the previous one (that was already internalized).
v8::Local<v8::String> string2 =
v8::String::NewFromUtf8Literal(isolate, TEST_STR);
bool success =
string2->MakeExternal(new TestOneByteResource(i::StrDup(TEST_STR)));
CHECK(success);
// Internalize (it will create a thin string in the new space).
i::Handle<i::String> istring = v8::Utils::OpenHandle(*string2);
i::Handle<i::String> isymbol2 = factory->InternalizeString(istring);
CHECK(isymbol2->IsInternalizedString());
CHECK(istring->IsThinString());
CHECK(heap->InYoungGeneration(*istring));
// Collect thin string. References to the thin string will be updated to
// point to the actual external string in the old space.
heap::GcAndSweep(heap, NEW_SPACE);
USE(isymbol1);
USE(isymbol2);
}
}
} // namespace heap
} // namespace internal
} // namespace v8
#undef TEST_STR