Serene Runtime 1.0.0
C runtime for the Serene programming language
Loading...
Searching...
No Matches
mm_tests.h
Go to the documentation of this file.
1/* -*- C -*-
2 * Serene programming language
3 * Copyright (C) 2019-2026 Sameer Rahmani <[email protected]>
4 *
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <https://www.gnu.org/licenses/>.
17 */
18#pragma once
19
21
22#include "base.h"
23#include "serene/utils.h"
24
25#define MM_TESTS(X) \
26 X("mm::allocation", test_mm_allocation), \
27 X("mm::block_allocation", test_mm_allocation_in_block), \
28 X("mm::multiblock_allocation", test_mm_allocation_multiblock), \
29 X("mm::block_bitmap", test_mm_block_bitmap), \
30 X("mm::block_size_properties", test_mm_block_size_properties), \
31 X("mm::aligned_block_allocation", test_mm_aligned_block_allocation), \
32 X("mm::immortal_multiblock", test_mm_immortal_multiblock), \
33 X("mm::get_block_out_of_range", test_mm_get_block_out_of_range)
34
35#if defined(MM_DEBUG)
36# define MM_TEST_LOG(...) DBG("MM_TEST", __VA_ARGS__)
37#else
38# define MM_TEST_LOG(...)
39#endif
40
41typedef struct mm_dummy {
42 char foo[200];
43 char bar[200];
45
46typedef struct mm_64k {
47 char foo[0xffff];
49
50static inline size_t padding(size_t size, size_t align) {
51 return (align - (size & (align - 1U))) & (align - 1);
52}
53
54static void test_mm_allocation() {
55 srn_mm_t *mm = srn_mm_init();
57
59
60 for (int i = 0; i < 200; i++) {
61 d->foo[i] = 'A';
62 d->bar[i] = 'B';
63 }
64
65 // DBG_HEX(mm->immortal_block, 600);
66
68}
69
71 srn_mm_t *mm = srn_mm_init();
73
76
77 for (int i = 0; i < 200; i++) {
78 d->foo[i] = 'A';
79 d->bar[i] = 'B';
80 }
81
82 srn_block_t *block = srn_mm_get_block(mm, b);
83
84 TEST_CHECK(block != nullptr);
85 // DBG_HEX(block, 600);
86
88}
89
91 srn_mm_t *mm = srn_mm_init();
93
95 MM_TEST_LOG("Allocated block 'b' with id %zu", b);
96 MM_TEST_LOG("Allocating 'd1'");
98
99 for (int i = 0; i < 0xffff; i++) {
100 d1->foo[i] = 'A';
101 }
102 MM_TEST_LOG("Allocating 'd2'");
104 for (int i = 0; i < 0xffff; i++) {
105 d2->foo[i] = 'B';
106 }
107
108 MM_TEST_LOG("Allocating 'd3'");
109 // this should end up on the root block
111 for (int i = 0; i < 200; i++) {
112 d3->foo[i] = 'C';
113 d3->bar[i] = 'D';
114 }
115
116 srn_block_t *block = srn_mm_get_block(mm, b);
117
118 TEST_CHECK(block != nullptr);
119 TEST_CHECK(block->next != nullptr);
120 TEST_CHECK(block->next->next == nullptr);
121 TEST_CHECK((uintptr_t)&block->next->base == (uintptr_t)d2);
122
123 TEST_CHECK((uintptr_t)&block->base + 0xffff +
124 padding(sizeof(mm_64k), alignof(mm_64k)) ==
125 (uintptr_t)d3);
126
127 /* DBG_HEX(block, 600); */
128 /* DBG_HEX(((char *)(uintptr_t)&block->base + (uintptr_t)0xfff0), 600); */
129
130 /* DBG_HEX(block->next, 600); */
131
132 srn_mm_shutdown(mm);
133}
134
135static void test_mm_block_bitmap(void) {
136 srn_mm_t *mm = srn_mm_init();
137 ASSERT_NOT_NULL(mm);
138
139 // Initially all bits must be 0 (no user blocks allocated)
140 for (int i = 0; i < 4; i++) {
141 TEST_CHECK(mm->block_bitmap[i] == 0);
142 }
143
147
148 MM_TEST_LOG("Allocated block ids: %zu, %zu, %zu", (size_t)b0, (size_t)b1,
149 (size_t)b2);
150
151 TEST_CHECK(b0 == 0);
152 TEST_CHECK(b1 == 1);
153 TEST_CHECK(b2 == 2);
154
155 uint64_t word0 = mm->block_bitmap[0];
156
157 // Bits 0,1,2 must be set
158 TEST_CHECK((word0 & 0x7ULL) == 0x7ULL);
159 // Other bits in the first word are untouched
160 // and other words should still be zero
161 TEST_CHECK(mm->block_bitmap[1] == 0);
162 TEST_CHECK(mm->block_bitmap[2] == 0);
163 TEST_CHECK(mm->block_bitmap[3] == 0);
164
165 srn_mm_shutdown(mm);
166}
167
169 size_t page_size = srn_mm_get_os_page_size();
170 size_t block_size = srn_mm_get_block_size();
171
172 MM_TEST_LOG("OS page size: %zu, block size: %zu", page_size, block_size);
173
174 TEST_CHECK(page_size > 0);
175 // block size must be a multiple of the OS page size
176 TEST_CHECK(block_size % page_size == 0);
177
178 // Block size must not be smaller than the desired size
179 TEST_CHECK(block_size >= DESIRED_BLOCK_SIZE);
180
181 // And it should be at most one page larger than DESIRED_BLOCK_SIZE
182 // Note: checkout closest_multiple() implementation.
183 TEST_CHECK(block_size < DESIRED_BLOCK_SIZE + page_size);
184}
185
187 srn_mm_t *mm = srn_mm_init();
188 ASSERT_NOT_NULL(mm);
189
191 MM_TEST_LOG("Allocated block id for aligned test: %zu", (size_t)b);
192
193 size_t alignment = 32;
194 size_t size = 13;
195
196 void *p1 = srn_mm_allocate_in_block_aligned(mm, b, size, alignment);
197 void *p2 = srn_mm_allocate_in_block_aligned(mm, b, size, alignment);
198
199 MM_TEST_LOG("Aligned allocations: p1=%p, p2=%p", p1, p2);
200
201 TEST_CHECK(p1 != NULL);
202 TEST_CHECK(p2 != NULL);
203 // Basically we should ignore the provided alignment and just use
204 // DEFAULT_BLOCK_ALIGNMENT
205 TEST_CHECK(((uintptr_t)p1 % DEFAULT_BLOCK_ALIGNMENT) == 0);
206 TEST_CHECK(((uintptr_t)p2 % DEFAULT_BLOCK_ALIGNMENT) == 0);
207
208 // Quick sanity: both allocations must belong to the same root block
209 srn_block_t *block = srn_mm_get_block(mm, b);
210 TEST_CHECK(block != nullptr);
211
212 uintptr_t base = (uintptr_t)&block->base;
213 uintptr_t end = base + (block->size - offsetof(srn_block_t, base));
214
215 TEST_CHECK((uintptr_t)p1 >= base && (uintptr_t)p1 + size <= end);
216 TEST_CHECK((uintptr_t)p2 >= base && (uintptr_t)p2 + size <= end);
217
218 srn_mm_shutdown(mm);
219}
220
222 srn_mm_t *mm = srn_mm_init();
223 ASSERT_NOT_NULL(mm);
224
225 // Same trick as test_mm_allocation_multiblock but on immortal_block
226 MM_TEST_LOG("Allocating mm_64k instances in immortal block");
227
231
232 ASSERT_NOT_NULL(i1);
233 ASSERT_NOT_NULL(i2);
234 ASSERT_NOT_NULL(i3);
235
236 for (int i = 0; i < 0xffff; i++) {
237 i1->foo[i] = 'X';
238 i2->foo[i] = 'Y';
239 i3->foo[i] = 'Z';
240 }
241
242 srn_block_t *root = mm->immortal_block;
243 TEST_CHECK(root != nullptr);
244 TEST_CHECK(root->next != nullptr);
245
246 MM_TEST_LOG("immortal root = %p, next = %p", (void *)root,
247 (void *)root->next);
248
249 srn_mm_shutdown(mm);
250}
251
253 srn_mm_t *mm = srn_mm_init();
254 ASSERT_NOT_NULL(mm);
255
257 MM_TEST_LOG("Allocated block id: %zu", (size_t)b);
258
259 srn_block_t *block = srn_mm_get_block(mm, b);
260 TEST_CHECK(block != nullptr);
261
262 // This ID is guaranteed to be out of range
263 srn_block_id_t invalid = b + 100;
264 srn_block_t *null_block = srn_mm_get_block(mm, invalid);
265 TEST_CHECK(null_block == nullptr);
266
267 srn_mm_shutdown(mm);
268}
#define TEST_CHECK(cond)
Definition acutest.h:96
#define ASSERT_NOT_NULL(x)
Definition base.h:30
size_t srn_block_id_t
The block id is effectively just an index in the blocks array in srn_mm_t.
Definition context.h:38
void * srn_mm_allocate_in_block_aligned(srn_mm_t *mm, srn_block_id_t block_id, size_t size, size_t alignment)
Allocate memory on a block with the given block_id.
Definition default.c:347
srn_block_t * srn_mm_get_block(srn_mm_t *mm, srn_block_id_t block_id)
Return the block object associated by the given block_id
Definition default.c:290
srn_block_id_t srn_mm_allocate_block(srn_mm_t *mm)
Allocate a new block in the memory manager and return its ID.
Definition default.c:364
size_t srn_mm_get_os_page_size(void)
Retutrns the OS page size.
Definition default.c:270
srn_mm_t * srn_mm_init()
Initialize the memory manager, this function will panic on error.
Definition default.c:294
void srn_mm_shutdown(srn_mm_t *mm)
Shut down the memory manager and release the resources.
Definition default.c:325
size_t srn_mm_get_block_size(void)
Calculates and return the block size based on our desired size and the OS page size.
Definition default.c:285
#define DESIRED_BLOCK_SIZE
This is the value that we want for our blocks but we want it to be a multiple of the page size on the...
Definition interface.h:53
#define srn_mm_immortal_allocate(mm, T)
Definition interface.h:169
#define srn_mm_allocate_in_block(mm, id, T)
Definition interface.h:166
#define DEFAULT_BLOCK_ALIGNMENT
We strictly use 16 bytes alignment for blocks.
Definition interface.h:47
static void test_mm_allocation()
Definition mm_tests.h:54
static void test_mm_immortal_multiblock(void)
Definition mm_tests.h:221
static void test_mm_block_bitmap(void)
Definition mm_tests.h:135
static void test_mm_allocation_in_block()
Definition mm_tests.h:70
static void test_mm_block_size_properties(void)
Definition mm_tests.h:168
static void test_mm_aligned_block_allocation(void)
Definition mm_tests.h:186
static size_t padding(size_t size, size_t align)
Definition mm_tests.h:50
#define MM_TEST_LOG(...)
Definition mm_tests.h:38
static void test_mm_allocation_multiblock()
Definition mm_tests.h:90
static void test_mm_get_block_out_of_range(void)
Definition mm_tests.h:252
char foo[0xffff]
Definition mm_tests.h:47
char foo[200]
Definition mm_tests.h:42
char bar[200]
Definition mm_tests.h:43
uint8_t base[]
Where the data area starts.
Definition interface.h:93
size_t size
This is the TOTAL size of the block, header + payloud.
Definition interface.h:88
struct srn_block_t * next
when the block does not have space to allocate a request, we will allocate a new block and point to i...
Definition interface.h:83
Main memory manager structure that will own all the allocated blocks and data.
Definition interface.h:112
srn_block_t * immortal_block
Immortal block is a chain of blocks which will never die.
Definition interface.h:134
uint64_t block_bitmap[4]
This is a 256bit bitmap we treat it as a whole.
Definition interface.h:126