Serene Runtime 1.0.0-dev
C runtime for the Serene programming language
Loading...
Searching...
No Matches
mm_tests.h File Reference
#include <signal.h>
#include <stddef.h>
#include <sys/wait.h>
#include <unistd.h>
#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_over_aligned_request_near_capacity (void)
static void test_mm_get_block_out_of_range (void)

Macro Definition Documentation

◆ MM_TEST_LOG

#define MM_TEST_LOG ( ...)

Definition at line 46 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), \
X("mm::over_aligned_request_near_capacity", test_mm_over_aligned_request_near_capacity)
static void test_mm_allocation()
Definition mm_tests.h:62
static void test_mm_immortal_multiblock(void)
Definition mm_tests.h:222
static void test_mm_block_bitmap(void)
Definition mm_tests.h:143
static void test_mm_allocation_in_block()
Definition mm_tests.h:78
static void test_mm_block_size_properties(void)
Definition mm_tests.h:175
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:255
static void test_mm_aligned_block_allocation(void)
Definition mm_tests.h:187
static void test_mm_over_aligned_allocation(void)
Alignments above DEFAULT_BLOCK_ALIGNMENT must be honored on the returned pointer.
Definition mm_tests.h:288
static void test_mm_get_block_after_release(void)
A released id must not resolve to a block anymore.
Definition mm_tests.h:271
static void test_mm_over_aligned_request_near_capacity(void)
Definition mm_tests.h:315
static void test_mm_allocation_multiblock()
Definition mm_tests.h:98
static void test_mm_get_block_out_of_range(void)
Definition mm_tests.h:350

Definition at line 30 of file mm_tests.h.

30#define MM_TESTS(X) \
31 X("mm::allocation", test_mm_allocation), X("mm::block_allocation", test_mm_allocation_in_block), \
32 X("mm::multiblock_allocation", test_mm_allocation_multiblock), \
33 X("mm::block_bitmap", test_mm_block_bitmap), \
34 X("mm::block_size_properties", test_mm_block_size_properties), \
35 X("mm::aligned_block_allocation", test_mm_aligned_block_allocation), \
36 X("mm::immortal_multiblock", test_mm_immortal_multiblock), \
37 X("mm::get_block_out_of_range", test_mm_get_block_out_of_range), \
38 X("mm::block_release_reuse", test_mm_block_release_reuse), \
39 X("mm::get_block_after_release", test_mm_get_block_after_release), \
40 X("mm::over_aligned_allocation", test_mm_over_aligned_allocation), \
41 X("mm::over_aligned_request_near_capacity", test_mm_over_aligned_request_near_capacity)

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 58 of file mm_tests.h.

58 {
59 return (align - (size & (align - 1U))) & (align - 1);
60}
Here is the caller graph for this function:

◆ test_mm_aligned_block_allocation()

void test_mm_aligned_block_allocation ( void )
static

Definition at line 187 of file mm_tests.h.

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

◆ test_mm_allocation()

void test_mm_allocation ( )
static

Definition at line 62 of file mm_tests.h.

62 {
63 srn_mm_t *mm = srn_mm_init(nullptr);
65
67
68 for (int i = 0; i < 200; i++) {
69 d->foo[i] = 'A';
70 d->bar[i] = 'B';
71 }
72
73 // DBG_HEX(mm->immortal_block, 600);
74
76}
#define srn_mm_immortal_allocate(mm, T)
Definition interface.h:188
char foo[200]
Definition mm_tests.h:50
char bar[200]
Definition mm_tests.h:51
Here is the call graph for this function:

◆ test_mm_allocation_in_block()

void test_mm_allocation_in_block ( )
static

Definition at line 78 of file mm_tests.h.

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

◆ test_mm_allocation_multiblock()

void test_mm_allocation_multiblock ( )
static

Definition at line 98 of file mm_tests.h.

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

◆ test_mm_block_bitmap()

void test_mm_block_bitmap ( void )
static

Definition at line 143 of file mm_tests.h.

143 {
144 srn_mm_t *mm = srn_mm_init(nullptr);
145 ASSERT_NOT_NULL(mm);
146
147 // Initially all bits must be 0 (no user blocks allocated)
148 for (int i = 0; i < 4; i++) {
149 TEST_CHECK(mm->block_bitmap[i] == 0);
150 }
151
155
156 MM_TEST_LOG("Allocated block ids: %zu, %zu, %zu", (size_t)b0, (size_t)b1, (size_t)b2);
157
158 TEST_CHECK(b0 == 0);
159 TEST_CHECK(b1 == 1);
160 TEST_CHECK(b2 == 2);
161
162 uint64_t word0 = mm->block_bitmap[0];
163
164 // Bits 0,1,2 must be set
165 TEST_CHECK((word0 & 0x7ULL) == 0x7ULL);
166 // Other bits in the first word are untouched
167 // and other words should still be zero
168 TEST_CHECK(mm->block_bitmap[1] == 0);
169 TEST_CHECK(mm->block_bitmap[2] == 0);
170 TEST_CHECK(mm->block_bitmap[3] == 0);
171
172 srn_mm_shutdown(mm);
173}
uint64_t block_bitmap[4]
This is a 256bit bitmap we treat it as a whole.
Definition interface.h:126
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 255 of file mm_tests.h.

255 {
256 srn_mm_t *mm = srn_mm_init(nullptr);
257 ASSERT_NOT_NULL(mm);
258
259 for (size_t i = 0; i < MAX_NUMBER_OF_BLOCKS + 44; i++) {
261 void *p = srn_mm_allocate_in_block_aligned(mm, b, 64, 16);
262 TEST_CHECK(p != nullptr);
264 }
265
266 srn_mm_shutdown(mm);
267}
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:468
#define MAX_NUMBER_OF_BLOCKS
array of blocks is enough for us, we can tweak the size as we see fit.
Definition interface.h:55
Here is the call graph for this function:

◆ test_mm_block_size_properties()

void test_mm_block_size_properties ( void )
static

Definition at line 175 of file mm_tests.h.

175 {
176 size_t page_size = srn_mm_get_os_page_size();
177
178 TEST_CHECK(page_size > 0);
179 // The block size is a power of two derived from the magnitude, so it is a
180 // whole number of pages on this platform by construction.
183 );
185}
#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:318
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 271 of file mm_tests.h.

271 {
272 srn_mm_t *mm = srn_mm_init(nullptr);
273 ASSERT_NOT_NULL(mm);
274
276 TEST_CHECK(srn_mm_get_block(mm, b) != nullptr);
277
279 TEST_CHECK(srn_mm_get_block(mm, b) == nullptr);
280
281 srn_mm_shutdown(mm);
282}
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 350 of file mm_tests.h.

350 {
351 srn_mm_t *mm = srn_mm_init(nullptr);
352 ASSERT_NOT_NULL(mm);
353
355 MM_TEST_LOG("Allocated block id: %zu", (size_t)b);
356
357 srn_block_t *block = srn_mm_get_block(mm, b);
358 TEST_CHECK(block != nullptr);
359
360 // This ID is guaranteed to be out of range
361 srn_block_id_t invalid = b + 100;
362 srn_block_t *null_block = srn_mm_get_block(mm, invalid);
363 TEST_CHECK(null_block == nullptr);
364
365 srn_mm_shutdown(mm);
366}
Here is the call graph for this function:

◆ test_mm_immortal_multiblock()

void test_mm_immortal_multiblock ( void )
static

Definition at line 222 of file mm_tests.h.

222 {
223 srn_mm_t *mm = srn_mm_init(nullptr);
224 ASSERT_NOT_NULL(mm);
225
226 // Same trick as test_mm_allocation_multiblock but on immortal_block
227 MM_TEST_LOG("Allocating mm_64k instances in immortal block");
228
232
233 ASSERT_NOT_NULL(i1);
234 ASSERT_NOT_NULL(i2);
235 ASSERT_NOT_NULL(i3);
236
237 for (int i = 0; i < 0xffff; i++) {
238 i1->foo[i] = 'X';
239 i2->foo[i] = 'Y';
240 i3->foo[i] = 'Z';
241 }
242
243 srn_block_t *root = mm->immortal_block;
244 TEST_CHECK(root != nullptr);
245 TEST_CHECK(root->next != nullptr);
246
247 MM_TEST_LOG("immortal root = %p, next = %p", (void *)root, (void *)root->next);
248
249 srn_mm_shutdown(mm);
250}
srn_block_t * immortal_block
Immortal block is a chain of blocks which will never die.
Definition interface.h:144
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 288 of file mm_tests.h.

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

◆ test_mm_over_aligned_request_near_capacity()

void test_mm_over_aligned_request_near_capacity ( void )
static

Definition at line 315 of file mm_tests.h.

315 {
316 pid_t pid = fork();
317 TEST_ASSERT(pid >= 0);
318
319 if (pid == 0) {
320 alarm(2);
321 srn_mm_t *mm = srn_mm_init(nullptr);
323 srn_block_t *block = srn_mm_get_block(mm, b);
324 size_t capacity = block->size - offsetof(srn_block_t, base);
325
326 // The base only promises DEFAULT_BLOCK_ALIGNMENT, but the provider may
327 // hand out something better aligned by chance, so pick the smallest
328 // alignment this base actually misses. Every block of the chain comes
329 // from the same provider with the same layout, so a fresh one misses it
330 // too.
331 size_t alignment = 2 * DEFAULT_BLOCK_ALIGNMENT;
332 while (alignment <= 4096 && ((uintptr_t)block->base % alignment) == 0) {
333 alignment *= 2;
334 }
335 if (alignment > 4096) {
336 // Nothing to show against a page aligned base, count it as refused.
337 raise(SIGABRT);
338 }
339
340 (void)srn_mm_allocate_in_block_aligned(mm, b, capacity - 8, alignment);
341 _exit(0);
342 }
343
344 int status = 0;
345 TEST_ASSERT(waitpid(pid, &status, 0) == pid);
346 TEST_CHECK(WIFSIGNALED(status));
347 TEST_CHECK_(WTERMSIG(status) != SIGALRM, "the chain grew until the alarm fired");
348}
#define TEST_CHECK_(cond,...)
Definition acutest.h:94
#define TEST_ASSERT(cond)
Definition acutest.h:117
#define DEFAULT_BLOCK_ALIGNMENT
We strictly use 16 bytes alignment for blocks.
Definition interface.h:58
size_t size
This is the TOTAL size of the block, header + payloud.
Definition interface.h:87
Here is the call graph for this function: