Files
sdk/runtime/vm/virtual_memory_test.cc
T
Liam Appelbe f0f732b1a6 [vm] Migrate FFI callbacks to the new metadata system.
This is the same metadata system that will support async callbacks. The
main thing this does is take the trampolines that used to be JIT only,
and use them in AOT too. This is partly a safety thing (there's some
extra checks that used to be skipped on AOT), but mostly just so that
the metadata system is unified between the sync and async callbacks.

More details about the design:
https://docs.google.com/document/d/1QDjyY_6wOTOgURwpeYMKU9qEz0gKxx2MUrdruC6Kp6c/edit?usp=sharing

I split this off the async CL. Some of the comments refer to async
stuff that doesn't exist yet, but it's coming immediately after this so
I didn't update them.

Change-Id: Icd5e86934ee9ae34c2c0e2ed2bbd1b928a7184ac
TEST=ffi_callback_metadata_test.cc and CI
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/302903
Reviewed-by: Ryan Macnak <rmacnak@google.com>
Reviewed-by: Martin Kustermann <kustermann@google.com>
Commit-Queue: Liam Appelbe <liama@google.com>
Reviewed-by: Daco Harkes <dacoharkes@google.com>
Reviewed-by: Alexander Markov <alexmarkov@google.com>
2023-05-24 22:22:04 +00:00

125 lines
3.9 KiB
C++

// 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<uword>(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<uword>(-1)));
char* buf = reinterpret_cast<char*>(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 = 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;
}
}
static int testFunction(int x) {
return x * 2;
}
NO_SANITIZE_UNDEFINED("function") // See #52440
VM_UNIT_TEST_CASE(DuplicateRXVirtualMemory) {
const uword page_size = VirtualMemory::PageSize();
const uword pointer = reinterpret_cast<uword>(&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<void*>(page_start), 2 * page_size);
EXPECT_NE(nullptr, vm);
VirtualMemory* vm2 = VirtualMemory::AllocateAligned(
vm->size(), kPageSize, /*is_executable=*/false,
/*is_compressed=*/false, "FfiCallbackMetadata::TrampolinePage");
bool ok = vm->DuplicateRX(vm2);
EXPECT_EQ(true, ok);
auto testFunction2 = reinterpret_cast<int (*)(int)>(vm2->start() + offset);
EXPECT_NE(&testFunction, testFunction2);
EXPECT_EQ(246, testFunction2(123));
delete vm;
delete vm2;
}
} // namespace dart