1 #include <err.h>
2 #include <kernel/thread.h>
3 #include <lib/unittest/unittest.h>
4 #include <malloc.h>
5 #include <stdatomic.h>
6 
expected_malloc_alignment(size_t size)7 static uintptr_t expected_malloc_alignment(size_t size) {
8     /* TODO use ffs? */
9     if (size >= 16) {
10         return sizeof(void*) * 2;
11     } else if (size >= 8) {
12         return 8;
13     } else if (size >= 4) {
14         return 4;
15     } else if (size >= 2) {
16         return 2;
17     } else {
18         return 1;
19     }
20 }
21 
TEST(memorytest,malloc_alignment)22 TEST(memorytest, malloc_alignment) {
23     for (int size = 2; size < 256; size++) {
24         const uintptr_t alignment_mask = expected_malloc_alignment(size) - 1;
25         void* ptr1 = malloc(size);
26         void* ptr2 = malloc(size / 2); /* Try to shake up the alignment. */
27         void* ptr3 = malloc(size);
28 
29         ASSERT_EQ(0, (uintptr_t)ptr1 & alignment_mask, "size %d / align %ld\n",
30                   size, alignment_mask + 1);
31         ASSERT_EQ(0, (uintptr_t)ptr3 & alignment_mask, "size %d / align %ld\n",
32                   size, alignment_mask + 1);
33 
34         free(ptr3);
35         free(ptr2);
36         free(ptr1);
37     }
38 test_abort:;
39 }
40 
41 #define MEMORYTEST_CONCURRENT_ALLOCATION_THREADS 64
42 
43 struct memorytest_alloc_thread_arg {
44     size_t alloc_size;
45     int count;
46     atomic_int threads_done;
47     atomic_int chunks_allocated;
48     atomic_bool go;
49 };
50 
memorytest_alloc_thread(void * _arg)51 int memorytest_alloc_thread(void* _arg) {
52     struct memorytest_alloc_thread_arg* arg = _arg;
53     void** ptrs;
54     int i;
55     int ret;
56 
57     ptrs = calloc(arg->count, sizeof(*ptrs));
58     if (!ptrs) {
59         return ERR_NO_MEMORY;
60     }
61 
62     /* Busy-wait until control thread says go. */
63     while (!atomic_load(&arg->go)) {
64     }
65 
66     for (i = 0; i < arg->count; i++) {
67         ptrs[i] = malloc(arg->alloc_size);
68         if (!ptrs[i]) {
69             ret = ERR_NO_MEMORY;
70             goto err_malloc;
71         }
72         atomic_fetch_add(&arg->chunks_allocated, 1);
73     }
74     ret = 0;
75 
76 err_malloc:
77     atomic_fetch_add(&arg->threads_done, 1);
78     while (atomic_load(&arg->threads_done) !=
79            MEMORYTEST_CONCURRENT_ALLOCATION_THREADS) {
80         thread_sleep(10);
81     }
82 
83     while (i-- > 0) {
84         free(ptrs[i]);
85     }
86     free(ptrs);
87     return ret;
88 }
89 
TEST(memorytest,concurrent_allocation)90 TEST(memorytest, concurrent_allocation) {
91     /*
92      * Test concurrent allocation by creating many threads. If this test is
93      * as the first test after boot, it will test the behavior while growing
94      * the heap. Assuming the heap implemention never shrinks the heap,
95      * additional test runs will not exercise this path.
96      */
97     struct memorytest_alloc_thread_arg thread_arg = {
98             .alloc_size = PAGE_SIZE / 4 * 3, /* ~1 page after heap overhead */
99             .count = 8,
100             .threads_done = 0,
101             .chunks_allocated = 0,
102             .go = false,
103     };
104     struct thread* thread[MEMORYTEST_CONCURRENT_ALLOCATION_THREADS];
105     for (size_t i = 0; i < countof(thread); i++) {
106         thread[i] = thread_create("memorytest", memorytest_alloc_thread,
107                                   &thread_arg, HIGH_PRIORITY - 1,
108                                   DEFAULT_STACK_SIZE);
109         ASSERT_NE(0, thread[i]);
110     }
111     for (size_t i = 0; i < countof(thread); i++) {
112         ASSERT_EQ(0, thread_resume(thread[i]));
113     }
114 
115     /* Wait for test threads to start and migrate to other cpus */
116     thread_sleep(100);
117     atomic_store(&thread_arg.go, true);
118 
119     for (size_t i = 0; i < countof(thread); i++) {
120         int retcode;
121         ASSERT_EQ(0, thread_join(thread[i], &retcode, INFINITE_TIME));
122         EXPECT_EQ(0, retcode, "Chunks allocated: %d\n",
123                   atomic_load(&thread_arg.chunks_allocated));
124     }
125 test_abort:;
126 }
127 
128 PORT_TEST(memorytest, "com.android.kernel.memorytest");
129