1bf215546Sopenharmony_ci/* 2bf215546Sopenharmony_ci * Copyright © 2018 Intel Corporation 3bf215546Sopenharmony_ci * 4bf215546Sopenharmony_ci * Permission is hereby granted, free of charge, to any person obtaining a 5bf215546Sopenharmony_ci * copy of this software and associated documentation files (the "Software"), 6bf215546Sopenharmony_ci * to deal in the Software without restriction, including without limitation 7bf215546Sopenharmony_ci * the rights to use, copy, modify, merge, publish, distribute, sublicense, 8bf215546Sopenharmony_ci * and/or sell copies of the Software, and to permit persons to whom the 9bf215546Sopenharmony_ci * Software is furnished to do so, subject to the following conditions: 10bf215546Sopenharmony_ci * 11bf215546Sopenharmony_ci * The above copyright notice and this permission notice (including the next 12bf215546Sopenharmony_ci * paragraph) shall be included in all copies or substantial portions of the 13bf215546Sopenharmony_ci * Software. 14bf215546Sopenharmony_ci * 15bf215546Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16bf215546Sopenharmony_ci * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17bf215546Sopenharmony_ci * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 18bf215546Sopenharmony_ci * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19bf215546Sopenharmony_ci * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 20bf215546Sopenharmony_ci * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 21bf215546Sopenharmony_ci * IN THE SOFTWARE. 22bf215546Sopenharmony_ci */ 23bf215546Sopenharmony_ci 24bf215546Sopenharmony_ci/* it is a test after all */ 25bf215546Sopenharmony_ci#undef NDEBUG 26bf215546Sopenharmony_ci 27bf215546Sopenharmony_ci#include <algorithm> 28bf215546Sopenharmony_ci#include <cassert> 29bf215546Sopenharmony_ci#include <climits> 30bf215546Sopenharmony_ci#include <cstdint> 31bf215546Sopenharmony_ci#include <cstdio> 32bf215546Sopenharmony_ci#include <cstdlib> 33bf215546Sopenharmony_ci#include <random> 34bf215546Sopenharmony_ci#include <set> 35bf215546Sopenharmony_ci#include <vector> 36bf215546Sopenharmony_ci 37bf215546Sopenharmony_ci#ifndef _WIN32 38bf215546Sopenharmony_ci#include <err.h> 39bf215546Sopenharmony_ci#else 40bf215546Sopenharmony_ci#define errx(code, msg, ...) \ 41bf215546Sopenharmony_ci do { \ 42bf215546Sopenharmony_ci fprintf(stderr, msg, __VA_ARGS__); \ 43bf215546Sopenharmony_ci exit(code); \ 44bf215546Sopenharmony_ci } while (0); 45bf215546Sopenharmony_ci#endif 46bf215546Sopenharmony_ci 47bf215546Sopenharmony_ci#include "vma.h" 48bf215546Sopenharmony_ci 49bf215546Sopenharmony_cinamespace { 50bf215546Sopenharmony_ci 51bf215546Sopenharmony_cistatic const uint64_t MEM_PAGE_SIZE = 4096; 52bf215546Sopenharmony_ci 53bf215546Sopenharmony_cistruct allocation { 54bf215546Sopenharmony_ci uint64_t start_page; 55bf215546Sopenharmony_ci uint64_t num_pages; 56bf215546Sopenharmony_ci}; 57bf215546Sopenharmony_ci 58bf215546Sopenharmony_cistruct allocation_less { 59bf215546Sopenharmony_ci bool operator()(const allocation& lhs, const allocation& rhs) const 60bf215546Sopenharmony_ci { 61bf215546Sopenharmony_ci assert(lhs.start_page + lhs.num_pages > lhs.start_page); 62bf215546Sopenharmony_ci return lhs.start_page + lhs.num_pages <= rhs.start_page; 63bf215546Sopenharmony_ci } 64bf215546Sopenharmony_ci}; 65bf215546Sopenharmony_ci 66bf215546Sopenharmony_ciconstexpr uint64_t allocation_end_page(const allocation& a) { 67bf215546Sopenharmony_ci return a.start_page + a.num_pages; 68bf215546Sopenharmony_ci} 69bf215546Sopenharmony_ci 70bf215546Sopenharmony_cistruct random_test { 71bf215546Sopenharmony_ci static const uint64_t MEM_START_PAGE = 1; 72bf215546Sopenharmony_ci static const uint64_t MEM_SIZE = 0xfffffffffffff000; 73bf215546Sopenharmony_ci static const uint64_t MEM_PAGES = MEM_SIZE / MEM_PAGE_SIZE; 74bf215546Sopenharmony_ci 75bf215546Sopenharmony_ci random_test(uint_fast32_t seed) 76bf215546Sopenharmony_ci : heap_holes{allocation{MEM_START_PAGE, MEM_PAGES}}, rand{seed} 77bf215546Sopenharmony_ci { 78bf215546Sopenharmony_ci util_vma_heap_init(&heap, MEM_START_PAGE * MEM_PAGE_SIZE, MEM_SIZE); 79bf215546Sopenharmony_ci } 80bf215546Sopenharmony_ci 81bf215546Sopenharmony_ci ~random_test() 82bf215546Sopenharmony_ci { 83bf215546Sopenharmony_ci util_vma_heap_finish(&heap); 84bf215546Sopenharmony_ci } 85bf215546Sopenharmony_ci 86bf215546Sopenharmony_ci void test(unsigned long count) 87bf215546Sopenharmony_ci { 88bf215546Sopenharmony_ci std::uniform_int_distribution<> one_to_thousand(1, 1000); 89bf215546Sopenharmony_ci while (count-- > 0) { 90bf215546Sopenharmony_ci int action = one_to_thousand(rand); 91bf215546Sopenharmony_ci if (action == 1) fill(); 92bf215546Sopenharmony_ci else if (action == 2) empty(); 93bf215546Sopenharmony_ci else if (action < 374) dealloc(); 94bf215546Sopenharmony_ci else alloc(); 95bf215546Sopenharmony_ci } 96bf215546Sopenharmony_ci } 97bf215546Sopenharmony_ci 98bf215546Sopenharmony_ci bool alloc(uint64_t size_order=52, uint64_t align_order=52) 99bf215546Sopenharmony_ci { 100bf215546Sopenharmony_ci std::geometric_distribution<> dist; 101bf215546Sopenharmony_ci 102bf215546Sopenharmony_ci if (align_order > 51) 103bf215546Sopenharmony_ci align_order = std::min(dist(rand), 51); 104bf215546Sopenharmony_ci uint64_t align_pages = 1ULL << align_order; 105bf215546Sopenharmony_ci uint64_t align = align_pages * MEM_PAGE_SIZE; 106bf215546Sopenharmony_ci 107bf215546Sopenharmony_ci if (size_order > 51) 108bf215546Sopenharmony_ci size_order = std::min(dist(rand), 51); 109bf215546Sopenharmony_ci uint64_t size_pages = 1ULL << size_order; 110bf215546Sopenharmony_ci uint64_t size = size_pages * MEM_PAGE_SIZE; 111bf215546Sopenharmony_ci 112bf215546Sopenharmony_ci uint64_t addr = util_vma_heap_alloc(&heap, size, align); 113bf215546Sopenharmony_ci 114bf215546Sopenharmony_ci if (addr == 0) { 115bf215546Sopenharmony_ci /* assert no gaps are present in the tracker that could satisfy this 116bf215546Sopenharmony_ci * allocation. 117bf215546Sopenharmony_ci */ 118bf215546Sopenharmony_ci for (const auto& hole : heap_holes) { 119bf215546Sopenharmony_ci uint64_t hole_alignment_pages = 120bf215546Sopenharmony_ci (align_pages - (hole.start_page % align_pages)) % align_pages; 121bf215546Sopenharmony_ci assert(hole.num_pages < size_pages + hole_alignment_pages); 122bf215546Sopenharmony_ci } 123bf215546Sopenharmony_ci return false; 124bf215546Sopenharmony_ci } else { 125bf215546Sopenharmony_ci assert(addr % align == 0); 126bf215546Sopenharmony_ci uint64_t addr_page = addr / MEM_PAGE_SIZE; 127bf215546Sopenharmony_ci allocation a{addr_page, size_pages}; 128bf215546Sopenharmony_ci auto i = heap_holes.find(a); 129bf215546Sopenharmony_ci assert(i != end(heap_holes)); 130bf215546Sopenharmony_ci allocation hole = *i; 131bf215546Sopenharmony_ci 132bf215546Sopenharmony_ci assert(hole.start_page <= addr_page); 133bf215546Sopenharmony_ci assert(hole.num_pages >= size_pages + addr_page - hole.start_page); 134bf215546Sopenharmony_ci 135bf215546Sopenharmony_ci heap_holes.erase(i); 136bf215546Sopenharmony_ci if (hole.start_page < a.start_page) { 137bf215546Sopenharmony_ci heap_holes.emplace(allocation{hole.start_page, 138bf215546Sopenharmony_ci a.start_page - hole.start_page}); 139bf215546Sopenharmony_ci } 140bf215546Sopenharmony_ci if (allocation_end_page(hole) > allocation_end_page(a)) { 141bf215546Sopenharmony_ci heap_holes.emplace(allocation{allocation_end_page(a), 142bf215546Sopenharmony_ci allocation_end_page(hole) - allocation_end_page(a)}); 143bf215546Sopenharmony_ci } 144bf215546Sopenharmony_ci 145bf215546Sopenharmony_ci allocations.push_back(a); 146bf215546Sopenharmony_ci return true; 147bf215546Sopenharmony_ci } 148bf215546Sopenharmony_ci } 149bf215546Sopenharmony_ci 150bf215546Sopenharmony_ci void dealloc() 151bf215546Sopenharmony_ci { 152bf215546Sopenharmony_ci if (allocations.size() == 0) 153bf215546Sopenharmony_ci return; 154bf215546Sopenharmony_ci 155bf215546Sopenharmony_ci std::uniform_int_distribution<> dist(0, allocations.size() - 1); 156bf215546Sopenharmony_ci int to_dealloc = dist(rand); 157bf215546Sopenharmony_ci 158bf215546Sopenharmony_ci std::swap(allocations.at(to_dealloc), allocations.back()); 159bf215546Sopenharmony_ci allocation a = allocations.back(); 160bf215546Sopenharmony_ci allocations.pop_back(); 161bf215546Sopenharmony_ci 162bf215546Sopenharmony_ci util_vma_heap_free(&heap, a.start_page * MEM_PAGE_SIZE, 163bf215546Sopenharmony_ci a.num_pages * MEM_PAGE_SIZE); 164bf215546Sopenharmony_ci 165bf215546Sopenharmony_ci assert(heap_holes.find(a) == end(heap_holes)); 166bf215546Sopenharmony_ci auto next = heap_holes.upper_bound(a); 167bf215546Sopenharmony_ci if (next != end(heap_holes)) { 168bf215546Sopenharmony_ci if (next->start_page == allocation_end_page(a)) { 169bf215546Sopenharmony_ci allocation x {a.start_page, a.num_pages + next->num_pages}; 170bf215546Sopenharmony_ci next = heap_holes.erase(next); 171bf215546Sopenharmony_ci next = heap_holes.insert(next, x); 172bf215546Sopenharmony_ci 173bf215546Sopenharmony_ci if (next != begin(heap_holes)) { 174bf215546Sopenharmony_ci auto prev = next; 175bf215546Sopenharmony_ci prev--; 176bf215546Sopenharmony_ci if (allocation_end_page(*prev) == next->start_page) { 177bf215546Sopenharmony_ci allocation x {prev->start_page, 178bf215546Sopenharmony_ci prev->num_pages + next->num_pages}; 179bf215546Sopenharmony_ci 180bf215546Sopenharmony_ci heap_holes.erase(prev); 181bf215546Sopenharmony_ci next = heap_holes.erase(next); 182bf215546Sopenharmony_ci heap_holes.insert(next, x); 183bf215546Sopenharmony_ci } 184bf215546Sopenharmony_ci } 185bf215546Sopenharmony_ci 186bf215546Sopenharmony_ci return; 187bf215546Sopenharmony_ci } 188bf215546Sopenharmony_ci } 189bf215546Sopenharmony_ci 190bf215546Sopenharmony_ci if (next != begin(heap_holes)) { 191bf215546Sopenharmony_ci auto prev = next; 192bf215546Sopenharmony_ci prev--; 193bf215546Sopenharmony_ci if (allocation_end_page(*prev) == a.start_page) { 194bf215546Sopenharmony_ci allocation x {prev->start_page, prev->num_pages + a.num_pages}; 195bf215546Sopenharmony_ci next = heap_holes.erase(prev); 196bf215546Sopenharmony_ci heap_holes.insert(next, x); 197bf215546Sopenharmony_ci 198bf215546Sopenharmony_ci return; 199bf215546Sopenharmony_ci } 200bf215546Sopenharmony_ci } 201bf215546Sopenharmony_ci 202bf215546Sopenharmony_ci heap_holes.emplace(a); 203bf215546Sopenharmony_ci } 204bf215546Sopenharmony_ci 205bf215546Sopenharmony_ci void fill() 206bf215546Sopenharmony_ci { 207bf215546Sopenharmony_ci for (int sz = 51; sz >= 0; sz--) { 208bf215546Sopenharmony_ci while (alloc(sz, 0)) 209bf215546Sopenharmony_ci ; 210bf215546Sopenharmony_ci } 211bf215546Sopenharmony_ci assert(heap_holes.empty()); 212bf215546Sopenharmony_ci } 213bf215546Sopenharmony_ci 214bf215546Sopenharmony_ci void empty() 215bf215546Sopenharmony_ci { 216bf215546Sopenharmony_ci while (allocations.size() != 0) 217bf215546Sopenharmony_ci dealloc(); 218bf215546Sopenharmony_ci assert(heap_holes.size() == 1); 219bf215546Sopenharmony_ci auto& hole = *begin(heap_holes); 220bf215546Sopenharmony_ci assert(hole.start_page == MEM_START_PAGE && hole.num_pages == MEM_PAGES); 221bf215546Sopenharmony_ci } 222bf215546Sopenharmony_ci 223bf215546Sopenharmony_ci struct util_vma_heap heap; 224bf215546Sopenharmony_ci std::set<allocation, allocation_less> heap_holes; 225bf215546Sopenharmony_ci std::default_random_engine rand; 226bf215546Sopenharmony_ci std::vector<allocation> allocations; 227bf215546Sopenharmony_ci}; 228bf215546Sopenharmony_ci 229bf215546Sopenharmony_ci} 230bf215546Sopenharmony_ci 231bf215546Sopenharmony_ciint main(int argc, char **argv) 232bf215546Sopenharmony_ci{ 233bf215546Sopenharmony_ci unsigned long seed, count; 234bf215546Sopenharmony_ci if (argc == 3) { 235bf215546Sopenharmony_ci char *arg_end = NULL; 236bf215546Sopenharmony_ci seed = strtoul(argv[1], &arg_end, 0); 237bf215546Sopenharmony_ci if (!arg_end || *arg_end || seed == ULONG_MAX) 238bf215546Sopenharmony_ci errx(1, "invalid seed \"%s\"", argv[1]); 239bf215546Sopenharmony_ci 240bf215546Sopenharmony_ci arg_end = NULL; 241bf215546Sopenharmony_ci count = strtoul(argv[2], &arg_end, 0); 242bf215546Sopenharmony_ci if (!arg_end || *arg_end || count == ULONG_MAX) 243bf215546Sopenharmony_ci errx(1, "invalid count \"%s\"", argv[2]); 244bf215546Sopenharmony_ci } else if (argc == 1) { 245bf215546Sopenharmony_ci /* importantly chosen prime numbers. */ 246bf215546Sopenharmony_ci seed = 8675309; 247bf215546Sopenharmony_ci count = 2459; 248bf215546Sopenharmony_ci } else { 249bf215546Sopenharmony_ci errx(1, "USAGE: %s seed iter_count\n", argv[0]); 250bf215546Sopenharmony_ci } 251bf215546Sopenharmony_ci 252bf215546Sopenharmony_ci random_test r{(uint_fast32_t)seed}; 253bf215546Sopenharmony_ci r.test(count); 254bf215546Sopenharmony_ci 255bf215546Sopenharmony_ci printf("ok\n"); 256bf215546Sopenharmony_ci return 0; 257bf215546Sopenharmony_ci} 258