// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file // for details. All rights reserved. Use of this source code is governed by a // BSD-style license that can be found in the LICENSE file. #include "vm/virtual_memory.h" #include "platform/assert.h" #include "vm/heap/heap.h" #include "vm/unit_test.h" #include "vm/virtual_memory_compressed.h" namespace dart { bool IsZero(char* begin, char* end) { for (char* current = begin; current < end; ++current) { if (*current != 0) { return false; } } return true; } VM_UNIT_TEST_CASE(AllocateVirtualMemory) { const intptr_t kVirtualMemoryBlockSize = 64 * KB; VirtualMemory* vm = VirtualMemory::Allocate(kVirtualMemoryBlockSize, false, false, "test"); EXPECT(vm != nullptr); EXPECT(vm->address() != nullptr); EXPECT_EQ(vm->start(), reinterpret_cast(vm->address())); EXPECT_EQ(kVirtualMemoryBlockSize, vm->size()); EXPECT_EQ(vm->start() + kVirtualMemoryBlockSize, vm->end()); EXPECT(vm->Contains(vm->start())); EXPECT(vm->Contains(vm->start() + 1)); EXPECT(vm->Contains(vm->start() + kVirtualMemoryBlockSize - 1)); EXPECT(vm->Contains(vm->start() + (kVirtualMemoryBlockSize / 2))); EXPECT(!vm->Contains(vm->start() - 1)); EXPECT(!vm->Contains(vm->end())); EXPECT(!vm->Contains(vm->end() + 1)); EXPECT(!vm->Contains(0)); EXPECT(!vm->Contains(static_cast(-1))); char* buf = reinterpret_cast(vm->address()); EXPECT(IsZero(buf, buf + vm->size())); buf[0] = 'a'; buf[1] = 'c'; buf[2] = '/'; buf[3] = 'd'; buf[4] = 'c'; buf[5] = 0; EXPECT_STREQ("ac/dc", buf); delete vm; } VM_UNIT_TEST_CASE(AllocateAlignedVirtualMemory) { intptr_t kHeapPageSize = Page::kPageSize; intptr_t kVirtualPageSize = 4096; intptr_t kIterations = kHeapPageSize / kVirtualPageSize; for (intptr_t i = 0; i < kIterations; i++) { VirtualMemory* vm = VirtualMemory::AllocateAligned( kHeapPageSize, kHeapPageSize, false, false, "test"); EXPECT(Utils::IsAligned(vm->start(), kHeapPageSize)); EXPECT_EQ(kHeapPageSize, vm->size()); delete vm; } } VM_UNIT_TEST_CASE(FreeVirtualMemory) { // Reservations should always be handed back to OS upon destruction. const intptr_t kVirtualMemoryBlockSize = 10 * MB; const intptr_t kIterations = 900; // Enough to exhaust 32-bit address space. for (intptr_t i = 0; i < kIterations; ++i) { VirtualMemory* vm = VirtualMemory::Allocate(kVirtualMemoryBlockSize, false, false, "test"); delete vm; } // Check that truncation does not introduce leaks. for (intptr_t i = 0; i < kIterations; ++i) { VirtualMemory* vm = VirtualMemory::Allocate(kVirtualMemoryBlockSize, false, false, "test"); vm->Truncate(kVirtualMemoryBlockSize / 2); delete vm; } for (intptr_t i = 0; i < kIterations; ++i) { VirtualMemory* vm = VirtualMemory::Allocate(kVirtualMemoryBlockSize, true, false, "test"); vm->Truncate(0); delete vm; } } #if defined(DART_HOST_OS_MACOS) // TODO(https://dartbug.com/52579): Reenable on Fuchsia. static int testFunction(int x) { return x * 2; } NO_SANITIZE_UNDEFINED_FUNCTION // See https://dartbug.com/52440 VM_UNIT_TEST_CASE(DuplicateRXVirtualMemory) { const uword page_size = VirtualMemory::PageSize(); const uword pointer = reinterpret_cast(&testFunction); const uword page_start = Utils::RoundDown(pointer, page_size); const uword offset = pointer - page_start; // Grab 2 * page_size, in case testFunction happens to land near the end of // the page. VirtualMemory* vm = VirtualMemory::ForImagePage( reinterpret_cast(page_start), 2 * page_size); EXPECT_NE(nullptr, vm); const bool is_executable = false; VirtualMemory* vm2 = VirtualMemory::AllocateAligned( vm->size(), Page::kPageSize, is_executable, /*is_compressed=*/false, "FfiCallbackMetadata::TrampolinePage"); bool ok = vm->DuplicateRX(vm2); EXPECT_EQ(true, ok); auto testFunction2 = reinterpret_cast(vm2->start() + offset); EXPECT_NE(&testFunction, testFunction2); EXPECT_EQ(246, testFunction2(123)); delete vm; delete vm2; } #endif // defined(DART_HOST_OS_MACOS) } // namespace dart