Serene Runtime 1.0.0-dev
C runtime for the Serene programming language
Loading...
Searching...
No Matches
mm_tests.h File Reference
#include <serene/rt/mm/interface.h>
#include "base.h"
#include "serene/utils.h"
Include dependency graph for mm_tests.h:
This graph shows which files directly or indirectly include this file:

Go to the source code of this file.

Data Structures

struct  mm_dummy
struct  mm_64k

Macros

#define MM_TESTS(X)
#define MM_TEST_LOG(...)

Typedefs

typedef struct mm_dummy mm_dummy
typedef struct mm_64k mm_64k

Functions

static size_t padding (size_t size, size_t align)
static void test_mm_allocation ()
static void test_mm_allocation_in_block ()
static void test_mm_allocation_multiblock ()
static void test_mm_block_bitmap (void)
static void test_mm_block_size_properties (void)
static void test_mm_aligned_block_allocation (void)
static void test_mm_immortal_multiblock (void)
static void test_mm_block_release_reuse (void)
 Block ids are reused after release, so a release must also retire every piece of allocation bookkeeping.
static void test_mm_get_block_after_release (void)
 A released id must not resolve to a block anymore.
static void test_mm_over_aligned_allocation (void)
 Alignments above DEFAULT_BLOCK_ALIGNMENT must be honored on the returned pointer.
static void test_mm_get_block_out_of_range (void)

Macro Definition Documentation

◆ MM_TEST_LOG

#define MM_TEST_LOG ( ...)

Definition at line 40 of file mm_tests.h.

◆ MM_TESTS

#define MM_TESTS ( X)
Value:
X("mm::allocation", test_mm_allocation), X("mm::block_allocation", test_mm_allocation_in_block), \
X("mm::multiblock_allocation", test_mm_allocation_multiblock), \
X("mm::block_bitmap", test_mm_block_bitmap), \
X("mm::block_size_properties", test_mm_block_size_properties), \
X("mm::aligned_block_allocation", test_mm_aligned_block_allocation), \
X("mm::immortal_multiblock", test_mm_immortal_multiblock), \
X("mm::get_block_out_of_range", test_mm_get_block_out_of_range), \
X("mm::block_release_reuse", test_mm_block_release_reuse), \
X("mm::get_block_after_release", test_mm_get_block_after_release), \
X("mm::over_aligned_allocation", test_mm_over_aligned_allocation)
static void test_mm_allocation()
Definition mm_tests.h:56
static void test_mm_immortal_multiblock(void)
Definition mm_tests.h:216
static void test_mm_block_bitmap(void)
Definition mm_tests.h:137
static void test_mm_allocation_in_block()
Definition mm_tests.h:72
static void test_mm_block_size_properties(void)
Definition mm_tests.h:169
static void test_mm_block_release_reuse(void)
Block ids are reused after release, so a release must also retire every piece of allocation bookkeepi...
Definition mm_tests.h:249
static void test_mm_aligned_block_allocation(void)
Definition mm_tests.h:181
static void test_mm_over_aligned_allocation(void)
Alignments above DEFAULT_BLOCK_ALIGNMENT must be honored on the returned pointer.
Definition mm_tests.h:282
static void test_mm_get_block_after_release(void)
A released id must not resolve to a block anymore.
Definition mm_tests.h:265
static void test_mm_allocation_multiblock()
Definition mm_tests.h:92
static void test_mm_get_block_out_of_range(void)
Definition mm_tests.h:302

Definition at line 25 of file mm_tests.h.

25#define MM_TESTS(X) \
26 X("mm::allocation", test_mm_allocation), X("mm::block_allocation", test_mm_allocation_in_block), \
27 X("mm::multiblock_allocation", test_mm_allocation_multiblock), \
28 X("mm::block_bitmap", test_mm_block_bitmap), \
29 X("mm::block_size_properties", test_mm_block_size_properties), \
30 X("mm::aligned_block_allocation", test_mm_aligned_block_allocation), \
31 X("mm::immortal_multiblock", test_mm_immortal_multiblock), \
32 X("mm::get_block_out_of_range", test_mm_get_block_out_of_range), \
33 X("mm::block_release_reuse", test_mm_block_release_reuse), \
34 X("mm::get_block_after_release", test_mm_get_block_after_release), \
35 X("mm::over_aligned_allocation", test_mm_over_aligned_allocation)

Typedef Documentation

◆ mm_64k

typedef struct mm_64k mm_64k

◆ mm_dummy

typedef struct mm_dummy mm_dummy

Function Documentation

◆ padding()

size_t padding ( size_t size,
size_t align )
inlinestatic

Definition at line 52 of file mm_tests.h.

52 {
53 return (align - (size & (align - 1U))) & (align - 1);
54}
Here is the caller graph for this function:

◆ test_mm_aligned_block_allocation()

void test_mm_aligned_block_allocation ( void )
static

Definition at line 181 of file mm_tests.h.

181 {
182 srn_mm_t *mm = srn_mm_init(nullptr);
183 ASSERT_NOT_NULL(mm);
184
186 MM_TEST_LOG("Allocated block id for aligned test: %zu", (size_t)b);
187
188 size_t alignment = 32;
189 size_t size = 13;
190
191 void *p1 = srn_mm_allocate_in_block_aligned(mm, b, size, alignment);
192 void *p2 = srn_mm_allocate_in_block_aligned(mm, b, size, alignment);
193
194 MM_TEST_LOG("Aligned allocations: p1=%p, p2=%p", p1, p2);
195
196 TEST_CHECK(p1 != NULL);
197 TEST_CHECK(p2 != NULL);
198 // The requested alignment is honored, which implies the default block
199 // alignment as well.
200 TEST_CHECK(((uintptr_t)p1 % alignment) == 0);
201 TEST_CHECK(((uintptr_t)p2 % alignment) == 0);
202
203 // Quick sanity, both allocations must belong to the same root block
204 srn_block_t *block = srn_mm_get_block(mm, b);
205 TEST_CHECK(block != nullptr);
206
207 uintptr_t base = (uintptr_t)&block->base;
208 uintptr_t end = base + (block->size - offsetof(srn_block_t, base));
209
210 TEST_CHECK((uintptr_t)p1 >= base && (uintptr_t)p1 + size <= end);
211 TEST_CHECK((uintptr_t)p2 >= base && (uintptr_t)p2 + size <= end);
212
213 srn_mm_shutdown(mm);
214}
#define TEST_CHECK(cond)
Definition acutest.h:95
#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:396
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:318
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:426
void srn_mm_shutdown(srn_mm_t *mm)
Shut down the memory manager and release the resources.
Definition default.c:374
srn_mm_t * srn_mm_init(const srn_configuration_t *config)
Initialize the memory manager, this function will panic on error.
Definition default.c:322
#define MM_TEST_LOG(...)
Definition mm_tests.h:40
uint8_t base[]
Where the data area starts.
Definition interface.h:89
Main memory manager structure that will own all the allocated blocks and data.
Definition interface.h:107
Here is the call graph for this function:

◆ test_mm_allocation()

void test_mm_allocation ( )
static

Definition at line 56 of file mm_tests.h.

56 {
57 srn_mm_t *mm = srn_mm_init(nullptr);
59
61
62 for (int i = 0; i < 200; i++) {
63 d->foo[i] = 'A';
64 d->bar[i] = 'B';
65 }
66
67 // DBG_HEX(mm->immortal_block, 600);
68
70}
#define srn_mm_immortal_allocate(mm, T)
Definition interface.h:183
char foo[200]
Definition mm_tests.h:44
char bar[200]
Definition mm_tests.h:45
Here is the call graph for this function:

◆ test_mm_allocation_in_block()

void test_mm_allocation_in_block ( )
static

Definition at line 72 of file mm_tests.h.

72 {
73 srn_mm_t *mm = srn_mm_init(nullptr);
75
78
79 for (int i = 0; i < 200; i++) {
80 d->foo[i] = 'A';
81 d->bar[i] = 'B';
82 }
83
84 srn_block_t *block = srn_mm_get_block(mm, b);
85
86 TEST_CHECK(block != nullptr);
87 // DBG_HEX(block, 600);
88
90}
#define srn_mm_allocate_in_block(mm, id, T)
Definition interface.h:180
Here is the call graph for this function:

◆ test_mm_allocation_multiblock()

void test_mm_allocation_multiblock ( )
static

Definition at line 92 of file mm_tests.h.

92 {
93 srn_mm_t *mm = srn_mm_init(nullptr);
95
97 MM_TEST_LOG("Allocated block 'b' with id %zu", b);
98 MM_TEST_LOG("Allocating 'd1'");
100
101 for (int i = 0; i < 0xffff; i++) {
102 d1->foo[i] = 'A';
103 }
104 MM_TEST_LOG("Allocating 'd2'");
106 for (int i = 0; i < 0xffff; i++) {
107 d2->foo[i] = 'B';
108 }
109
110 MM_TEST_LOG("Allocating 'd3'");
111 // this should end up on the root block
113 for (int i = 0; i < 200; i++) {
114 d3->foo[i] = 'C';
115 d3->bar[i] = 'D';
116 }
117
118 srn_block_t *block = srn_mm_get_block(mm, b);
119
120 TEST_CHECK(block != nullptr);
121 TEST_CHECK(block->next != nullptr);
122 TEST_CHECK(block->next->next == nullptr);
123 TEST_CHECK((uintptr_t)&block->next->base == (uintptr_t)d2);
124
126 (uintptr_t)&block->base + 0xffff + padding(sizeof(mm_64k), alignof(mm_64k)) == (uintptr_t)d3
127 );
128
129 /* DBG_HEX(block, 600); */
130 /* DBG_HEX(((char *)(uintptr_t)&block->base + (uintptr_t)0xfff0), 600); */
131
132 /* DBG_HEX(block->next, 600); */
133
134 srn_mm_shutdown(mm);
135}
static size_t padding(size_t size, size_t align)
Definition mm_tests.h:52
char foo[0xffff]
Definition mm_tests.h:49
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:77
Here is the call graph for this function:

◆ test_mm_block_bitmap()

void test_mm_block_bitmap ( void )
static

Definition at line 137 of file mm_tests.h.

137 {
138 srn_mm_t *mm = srn_mm_init(nullptr);
139 ASSERT_NOT_NULL(mm);
140
141 // Initially all bits must be 0 (no user blocks allocated)
142 for (int i = 0; i < 4; i++) {
143 TEST_CHECK(mm->block_bitmap[i] == 0);
144 }
145
149
150 MM_TEST_LOG("Allocated block ids: %zu, %zu, %zu", (size_t)b0, (size_t)b1, (size_t)b2);
151
152 TEST_CHECK(b0 == 0);
153 TEST_CHECK(b1 == 1);
154 TEST_CHECK(b2 == 2);
155
156 uint64_t word0 = mm->block_bitmap[0];
157
158 // Bits 0,1,2 must be set
159 TEST_CHECK((word0 & 0x7ULL) == 0x7ULL);
160 // Other bits in the first word are untouched
161 // and other words should still be zero
162 TEST_CHECK(mm->block_bitmap[1] == 0);
163 TEST_CHECK(mm->block_bitmap[2] == 0);
164 TEST_CHECK(mm->block_bitmap[3] == 0);
165
166 srn_mm_shutdown(mm);
167}
uint64_t block_bitmap[4]
This is a 256bit bitmap we treat it as a whole.
Definition interface.h:121
Here is the call graph for this function:

◆ test_mm_block_release_reuse()

void test_mm_block_release_reuse ( void )
static

Block ids are reused after release, so a release must also retire every piece of allocation bookkeeping.

These cycles exceed MAX_NUMBER_OF_BLOCKS; only live blocks may count against the limit.

Definition at line 249 of file mm_tests.h.

249 {
250 srn_mm_t *mm = srn_mm_init(nullptr);
251 ASSERT_NOT_NULL(mm);
252
253 for (size_t i = 0; i < MAX_NUMBER_OF_BLOCKS + 44; i++) {
255 void *p = srn_mm_allocate_in_block_aligned(mm, b, 64, 16);
256 TEST_CHECK(p != nullptr);
258 }
259
260 srn_mm_shutdown(mm);
261}
void srn_mm_release_block(srn_mm_t *mm, srn_block_id_t id)
Release the given block id and free the memory for later allocations.
Definition default.c:451
#define MAX_NUMBER_OF_BLOCKS
array of blocks is enough for us, we can tweak the size as we see fit.
Definition interface.h:50
Here is the call graph for this function:

◆ test_mm_block_size_properties()

void test_mm_block_size_properties ( void )
static

Definition at line 169 of file mm_tests.h.

169 {
170 size_t page_size = srn_mm_get_os_page_size();
171
172 TEST_CHECK(page_size > 0);
173 // The block size is a power of two derived from the magnitude, so it is a
174 // whole number of pages on this platform by construction.
177 );
179}
#define SRN_CONFIG_DEFAULT_BLOCK_SIZE
The default block size in bytes, derived from the magnitude.
#define SRN_CONFIG_DEFAULT_BLOCK_SIZE_MAGNITUDE
Magnitude of one memory-manager block.
size_t srn_mm_get_os_page_size(void)
Retutrns the OS page size.
Definition default.c:303
Here is the call graph for this function:

◆ test_mm_get_block_after_release()

void test_mm_get_block_after_release ( void )
static

A released id must not resolve to a block anymore.

Handing back the stale pointer lets callers bump allocate into freed memory.

Definition at line 265 of file mm_tests.h.

265 {
266 srn_mm_t *mm = srn_mm_init(nullptr);
267 ASSERT_NOT_NULL(mm);
268
270 TEST_CHECK(srn_mm_get_block(mm, b) != nullptr);
271
273 TEST_CHECK(srn_mm_get_block(mm, b) == nullptr);
274
275 srn_mm_shutdown(mm);
276}
Here is the call graph for this function:

◆ test_mm_get_block_out_of_range()

void test_mm_get_block_out_of_range ( void )
static

Definition at line 302 of file mm_tests.h.

302 {
303 srn_mm_t *mm = srn_mm_init(nullptr);
304 ASSERT_NOT_NULL(mm);
305
307 MM_TEST_LOG("Allocated block id: %zu", (size_t)b);
308
309 srn_block_t *block = srn_mm_get_block(mm, b);
310 TEST_CHECK(block != nullptr);
311
312 // This ID is guaranteed to be out of range
313 srn_block_id_t invalid = b + 100;
314 srn_block_t *null_block = srn_mm_get_block(mm, invalid);
315 TEST_CHECK(null_block == nullptr);
316
317 srn_mm_shutdown(mm);
318}
Here is the call graph for this function:

◆ test_mm_immortal_multiblock()

void test_mm_immortal_multiblock ( void )
static

Definition at line 216 of file mm_tests.h.

216 {
217 srn_mm_t *mm = srn_mm_init(nullptr);
218 ASSERT_NOT_NULL(mm);
219
220 // Same trick as test_mm_allocation_multiblock but on immortal_block
221 MM_TEST_LOG("Allocating mm_64k instances in immortal block");
222
226
227 ASSERT_NOT_NULL(i1);
228 ASSERT_NOT_NULL(i2);
229 ASSERT_NOT_NULL(i3);
230
231 for (int i = 0; i < 0xffff; i++) {
232 i1->foo[i] = 'X';
233 i2->foo[i] = 'Y';
234 i3->foo[i] = 'Z';
235 }
236
237 srn_block_t *root = mm->immortal_block;
238 TEST_CHECK(root != nullptr);
239 TEST_CHECK(root->next != nullptr);
240
241 MM_TEST_LOG("immortal root = %p, next = %p", (void *)root, (void *)root->next);
242
243 srn_mm_shutdown(mm);
244}
srn_block_t * immortal_block
Immortal block is a chain of blocks which will never die.
Definition interface.h:139
Here is the call graph for this function:

◆ test_mm_over_aligned_allocation()

void test_mm_over_aligned_allocation ( void )
static

Alignments above DEFAULT_BLOCK_ALIGNMENT must be honored on the returned pointer.

Aligning the bump offset alone leaves the result at base modulo the requested alignment, and base is only guaranteed to be 16 byte aligned.

Definition at line 282 of file mm_tests.h.

282 {
283 srn_mm_t *mm = srn_mm_init(nullptr);
284 ASSERT_NOT_NULL(mm);
285
287
288 // Skew the bump offset first so the aligned request cannot start at zero.
289 void *skew = srn_mm_allocate_in_block_aligned(mm, b, 1, 1);
290 TEST_CHECK(skew != nullptr);
291
292 for (size_t alignment = 32; alignment <= 256; alignment *= 2) {
293 void *p = srn_mm_allocate_in_block_aligned(mm, b, 24, alignment);
294 TEST_CHECK(p != nullptr);
295 TEST_MSG("alignment %zu, remainder %zu", alignment, (size_t)((uintptr_t)p % alignment));
296 TEST_CHECK(((uintptr_t)p % alignment) == 0);
297 }
298
299 srn_mm_shutdown(mm);
300}
#define TEST_MSG(...)
Definition acutest.h:223
Here is the call graph for this function: