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