// SPDX-License-Identifier: MIT #include #include #include "Utils/Allocator/HostAllocator.h" #include #include template bool HasSyscallError(T Result) { constexpr uint64_t MAX_ERRNO = 0xFFFF'FFFF'FFFF'0001ULL; return reinterpret_cast(Result) >= MAX_ERRNO; } TEST_CASE("Allocator - Fixed replacement") { const auto RegionSize = 128 * 1024 * 1024; fextl::vector MemoryRegions {}; for (size_t i = 0; i < 2; ++i) { auto Ptr = mmap(nullptr, RegionSize, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); MemoryRegions.emplace_back(FEXCore::Allocator::MemoryRegion { .Ptr = Ptr, .Size = RegionSize, }); } auto Allocator = Alloc::OSAllocator::Create64BitAllocatorWithRegions(MemoryRegions); auto Base = Allocator->Mmap(nullptr, 4096, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); REQUIRE(!HasSyscallError(Base)); // Allocate perfectly overlapping pages. Allocate as many pages as the region. // FEX had a bug where the allocator could run out of memory with MAP_FIXED. for (size_t i = 0; i < (RegionSize / 4096); ++i) { auto NewBase = Allocator->Mmap(Base, 4096, PROT_NONE, MAP_FIXED | MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); REQUIRE(Base == NewBase); } Alloc::OSAllocator::ReleaseAllocatorWorkaround(std::move(Allocator)); } TEST_CASE("Allocator - Non-Fit") { const auto RegionSize = 128 * 1024 * 1024; fextl::vector MemoryRegions {}; for (size_t i = 0; i < 2; ++i) { auto Ptr = mmap(nullptr, RegionSize, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); MemoryRegions.emplace_back(FEXCore::Allocator::MemoryRegion { .Ptr = Ptr, .Size = RegionSize, }); } auto Allocator = Alloc::OSAllocator::Create64BitAllocatorWithRegions(MemoryRegions); auto Base = Allocator->Mmap(nullptr, RegionSize / 4, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); REQUIRE(!HasSyscallError(Base)); // Try to allocate within the whole VMA size minus a small amount. // FEX had a bug where if the allocation fit within a VMA region, it would try and allocate past the end without checking. // Only occurred when `MAP_FIXED` was used. auto NewBase = Allocator->Mmap(Base, RegionSize - (4096 * 64), PROT_NONE, MAP_FIXED | MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); // Must either fit in the VMA region, or fail. // - If it matches previous allocation, then it fit in the VMA region. // - This can happen if FEX's allocator gains support for VMA merging. // - If it errors, then it doesn't fit in the VMA region. REQUIRE((NewBase == Base || HasSyscallError(NewBase))); Alloc::OSAllocator::ReleaseAllocatorWorkaround(std::move(Allocator)); }