| // Copyright 2014 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 <cmath> |
| #include <iostream> |
| #include <limits> |
| |
| #include "src/handles/handles-inl.h" |
| #include "src/heap/heap.h" |
| #include "src/heap/spaces-inl.h" |
| #include "src/objects/objects-inl.h" |
| #include "test/unittests/test-utils.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| |
| namespace v8 { |
| namespace internal { |
| |
| using HeapTest = TestWithIsolate; |
| using HeapWithPointerCompressionTest = TestWithIsolateAndPointerCompression; |
| |
| TEST(Heap, YoungGenerationSizeFromOldGenerationSize) { |
| const size_t MB = static_cast<size_t>(i::MB); |
| const size_t KB = static_cast<size_t>(i::KB); |
| const size_t pm = i::Heap::kPointerMultiplier; |
| ASSERT_EQ(3 * 512u * pm * KB, |
| i::Heap::YoungGenerationSizeFromOldGenerationSize(128u * pm * MB)); |
| ASSERT_EQ(3 * 2048u * pm * KB, |
| i::Heap::YoungGenerationSizeFromOldGenerationSize(256u * pm * MB)); |
| ASSERT_EQ(3 * 4096u * pm * KB, |
| i::Heap::YoungGenerationSizeFromOldGenerationSize(512u * pm * MB)); |
| ASSERT_EQ(3 * 8192u * pm * KB, |
| i::Heap::YoungGenerationSizeFromOldGenerationSize(1024u * pm * MB)); |
| } |
| |
| TEST(Heap, GenerationSizesFromHeapSize) { |
| const size_t MB = static_cast<size_t>(i::MB); |
| const size_t KB = static_cast<size_t>(i::KB); |
| const size_t pm = i::Heap::kPointerMultiplier; |
| size_t old, young; |
| |
| i::Heap::GenerationSizesFromHeapSize(1 * KB, &young, &old); |
| ASSERT_EQ(0u, old); |
| ASSERT_EQ(0u, young); |
| |
| i::Heap::GenerationSizesFromHeapSize(1 * KB + 3 * 512u * pm * KB, &young, |
| &old); |
| ASSERT_EQ(1 * KB, old); |
| ASSERT_EQ(3 * 512u * pm * KB, young); |
| |
| i::Heap::GenerationSizesFromHeapSize(128 * pm * MB + 3 * 512 * pm * KB, |
| &young, &old); |
| ASSERT_EQ(128u * pm * MB, old); |
| ASSERT_EQ(3 * 512u * pm * KB, young); |
| |
| i::Heap::GenerationSizesFromHeapSize(256u * pm * MB + 3 * 2048 * pm * KB, |
| &young, &old); |
| ASSERT_EQ(256u * pm * MB, old); |
| ASSERT_EQ(3 * 2048u * pm * KB, young); |
| |
| i::Heap::GenerationSizesFromHeapSize(512u * pm * MB + 3 * 4096 * pm * KB, |
| &young, &old); |
| ASSERT_EQ(512u * pm * MB, old); |
| ASSERT_EQ(3 * 4096u * pm * KB, young); |
| |
| i::Heap::GenerationSizesFromHeapSize(1024u * pm * MB + 3 * 8192 * pm * KB, |
| &young, &old); |
| ASSERT_EQ(1024u * pm * MB, old); |
| ASSERT_EQ(3 * 8192u * pm * KB, young); |
| } |
| |
| TEST(Heap, HeapSizeFromPhysicalMemory) { |
| const size_t MB = static_cast<size_t>(i::MB); |
| const size_t pm = i::Heap::kPointerMultiplier; |
| |
| // The expected value is old_generation_size + 3 * semi_space_size. |
| ASSERT_EQ(128 * pm * MB + 3 * 512 * pm * KB, |
| i::Heap::HeapSizeFromPhysicalMemory(0u)); |
| ASSERT_EQ(128 * pm * MB + 3 * 512 * pm * KB, |
| i::Heap::HeapSizeFromPhysicalMemory(512u * MB)); |
| ASSERT_EQ(256 * pm * MB + 3 * 2048 * pm * KB, |
| i::Heap::HeapSizeFromPhysicalMemory(1024u * MB)); |
| ASSERT_EQ(512 * pm * MB + 3 * 4096 * pm * KB, |
| i::Heap::HeapSizeFromPhysicalMemory(2048u * MB)); |
| ASSERT_EQ( |
| 1024 * pm * MB + 3 * 8192 * pm * KB, |
| i::Heap::HeapSizeFromPhysicalMemory(static_cast<uint64_t>(4096u) * MB)); |
| ASSERT_EQ( |
| 1024 * pm * MB + 3 * 8192 * pm * KB, |
| i::Heap::HeapSizeFromPhysicalMemory(static_cast<uint64_t>(8192u) * MB)); |
| } |
| |
| TEST_F(HeapTest, ASLR) { |
| #if V8_TARGET_ARCH_X64 |
| #if V8_OS_MACOSX |
| Heap* heap = i_isolate()->heap(); |
| std::set<void*> hints; |
| for (int i = 0; i < 1000; i++) { |
| hints.insert(heap->GetRandomMmapAddr()); |
| } |
| if (hints.size() == 1) { |
| EXPECT_TRUE((*hints.begin()) == nullptr); |
| EXPECT_TRUE(i::GetRandomMmapAddr() == nullptr); |
| } else { |
| // It is unlikely that 1000 random samples will collide to less then 500 |
| // values. |
| EXPECT_GT(hints.size(), 500u); |
| const uintptr_t kRegionMask = 0xFFFFFFFFu; |
| void* first = *hints.begin(); |
| for (void* hint : hints) { |
| uintptr_t diff = reinterpret_cast<uintptr_t>(first) ^ |
| reinterpret_cast<uintptr_t>(hint); |
| EXPECT_LE(diff, kRegionMask); |
| } |
| } |
| #endif // V8_OS_MACOSX |
| #endif // V8_TARGET_ARCH_X64 |
| } |
| |
| TEST_F(HeapTest, ExternalLimitDefault) { |
| Heap* heap = i_isolate()->heap(); |
| EXPECT_EQ(kExternalAllocationSoftLimit, |
| heap->isolate()->isolate_data()->external_memory_limit_); |
| } |
| |
| TEST_F(HeapTest, ExternalLimitStaysAboveDefaultForExplicitHandling) { |
| v8_isolate()->AdjustAmountOfExternalAllocatedMemory(+10 * MB); |
| v8_isolate()->AdjustAmountOfExternalAllocatedMemory(-10 * MB); |
| Heap* heap = i_isolate()->heap(); |
| EXPECT_GE(heap->isolate()->isolate_data()->external_memory_limit_, |
| kExternalAllocationSoftLimit); |
| } |
| |
| #if V8_TARGET_ARCH_64_BIT |
| TEST_F(HeapWithPointerCompressionTest, HeapLayout) { |
| // Produce some garbage. |
| RunJS( |
| "let ar = [];" |
| "for (let i = 0; i < 100; i++) {" |
| " ar.push(Array(i));" |
| "}" |
| "ar.push(Array(32 * 1024 * 1024));"); |
| |
| Address isolate_root = i_isolate()->isolate_root(); |
| EXPECT_TRUE(IsAligned(isolate_root, size_t{4} * GB)); |
| |
| // Check that all memory chunks belong this region. |
| base::AddressRegion heap_reservation(isolate_root - size_t{2} * GB, |
| size_t{4} * GB); |
| |
| OldGenerationMemoryChunkIterator iter(i_isolate()->heap()); |
| for (;;) { |
| MemoryChunk* chunk = iter.next(); |
| if (chunk == nullptr) break; |
| |
| Address address = chunk->address(); |
| size_t size = chunk->area_end() - address; |
| EXPECT_TRUE(heap_reservation.contains(address, size)); |
| } |
| } |
| #endif // V8_TARGET_ARCH_64_BIT |
| |
| } // namespace internal |
| } // namespace v8 |