1/* ----------------------------------------------------------------------------
2Copyright (c) 2018-2020, Microsoft Research, Daan Leijen
3This is free software; you can redistribute it and/or modify it under the
4terms of the MIT license. A copy of the license can be found in the file
5"LICENSE" at the root of this distribution.
6-----------------------------------------------------------------------------*/
7#if defined(__GNUC__) && !defined(__clang__)
8#pragma GCC diagnostic ignored "-Walloc-size-larger-than="
9#endif
10
11/*
12Testing allocators is difficult as bugs may only surface after particular
13allocation patterns. The main approach to testing _mimalloc_ is therefore
14to have extensive internal invariant checking (see `page_is_valid` in `page.c`
15for example), which is enabled in debug mode with `-DMI_DEBUG_FULL=ON`.
16The main testing is then to run `mimalloc-bench` [1] using full invariant checking
17to catch any potential problems over a wide range of intensive allocation bench
18marks.
19
20However, this does not test well for the entire API surface. In this test file
21we therefore test the API over various inputs. Please add more tests :-)
22
23[1] https://github.com/daanx/mimalloc-bench
24*/
25
26#include <assert.h>
27#include <stdbool.h>
28#include <stdint.h>
29#include <errno.h>
30
31#ifdef __cplusplus
32#include <vector>
33#endif
34
35#include "mimalloc.h"
36// #include "mimalloc-internal.h"
37#include "mimalloc-types.h" // for MI_DEBUG
38
39#include "testhelper.h"
40
41// ---------------------------------------------------------------------------
42// Test functions
43// ---------------------------------------------------------------------------
44bool test_heap1(void);
45bool test_heap2(void);
46bool test_stl_allocator1(void);
47bool test_stl_allocator2(void);
48
49// ---------------------------------------------------------------------------
50// Main testing
51// ---------------------------------------------------------------------------
52int main(void) {
53 mi_option_disable(mi_option_verbose);
54
55 // ---------------------------------------------------
56 // Malloc
57 // ---------------------------------------------------
58
59 CHECK_BODY("malloc-zero") {
60 void* p = mi_malloc(0);
61 result = (p != NULL);
62 mi_free(p);
63 };
64 CHECK_BODY("malloc-nomem1") {
65 result = (mi_malloc((size_t)PTRDIFF_MAX + (size_t)1) == NULL);
66 };
67 CHECK_BODY("malloc-null") {
68 mi_free(NULL);
69 };
70 CHECK_BODY("calloc-overflow") {
71 // use (size_t)&mi_calloc to get some number without triggering compiler warnings
72 result = (mi_calloc((size_t)&mi_calloc,SIZE_MAX/1000) == NULL);
73 };
74 CHECK_BODY("calloc0") {
75 void* p = mi_calloc(0,1000);
76 result = (mi_usable_size(p) <= 16);
77 mi_free(p);
78 };
79 CHECK_BODY("malloc-large") { // see PR #544.
80 void* p = mi_malloc(67108872);
81 mi_free(p);
82 };
83
84 // ---------------------------------------------------
85 // Extended
86 // ---------------------------------------------------
87 CHECK_BODY("posix_memalign1") {
88 void* p = &p;
89 int err = mi_posix_memalign(&p, sizeof(void*), 32);
90 result = ((err==0 && (uintptr_t)p % sizeof(void*) == 0) || p==&p);
91 mi_free(p);
92 };
93 CHECK_BODY("posix_memalign_no_align") {
94 void* p = &p;
95 int err = mi_posix_memalign(&p, 3, 32);
96 result = (err==EINVAL && p==&p);
97 };
98 CHECK_BODY("posix_memalign_zero") {
99 void* p = &p;
100 int err = mi_posix_memalign(&p, sizeof(void*), 0);
101 mi_free(p);
102 result = (err==0);
103 };
104 CHECK_BODY("posix_memalign_nopow2") {
105 void* p = &p;
106 int err = mi_posix_memalign(&p, 3*sizeof(void*), 32);
107 result = (err==EINVAL && p==&p);
108 };
109 CHECK_BODY("posix_memalign_nomem") {
110 void* p = &p;
111 int err = mi_posix_memalign(&p, sizeof(void*), SIZE_MAX);
112 result = (err==ENOMEM && p==&p);
113 };
114
115 // ---------------------------------------------------
116 // Aligned API
117 // ---------------------------------------------------
118 CHECK_BODY("malloc-aligned1") {
119 void* p = mi_malloc_aligned(32,32); result = (p != NULL && (uintptr_t)(p) % 32 == 0); mi_free(p);
120 };
121 CHECK_BODY("malloc-aligned2") {
122 void* p = mi_malloc_aligned(48,32); result = (p != NULL && (uintptr_t)(p) % 32 == 0); mi_free(p);
123 };
124 CHECK_BODY("malloc-aligned3") {
125 void* p1 = mi_malloc_aligned(48,32); bool result1 = (p1 != NULL && (uintptr_t)(p1) % 32 == 0);
126 void* p2 = mi_malloc_aligned(48,32); bool result2 = (p2 != NULL && (uintptr_t)(p2) % 32 == 0);
127 mi_free(p2);
128 mi_free(p1);
129 result = (result1&&result2);
130 };
131 CHECK_BODY("malloc-aligned4") {
132 void* p;
133 bool ok = true;
134 for (int i = 0; i < 8 && ok; i++) {
135 p = mi_malloc_aligned(8, 16);
136 ok = (p != NULL && (uintptr_t)(p) % 16 == 0); mi_free(p);
137 }
138 result = ok;
139 };
140 CHECK_BODY("malloc-aligned5") {
141 void* p = mi_malloc_aligned(4097,4096);
142 size_t usable = mi_usable_size(p);
143 result = (usable >= 4097 && usable < 16000);
144 printf("malloc_aligned5: usable size: %zi\n", usable);
145 mi_free(p);
146 };
147 CHECK_BODY("malloc-aligned6") {
148 bool ok = true;
149 for (size_t align = 1; align <= MI_ALIGNMENT_MAX && ok; align *= 2) {
150 void* ps[8];
151 for (int i = 0; i < 8 && ok; i++) {
152 ps[i] = mi_malloc_aligned(align*13 /*size*/, align);
153 if (ps[i] == NULL || (uintptr_t)(ps[i]) % align != 0) {
154 ok = false;
155 }
156 }
157 for (int i = 0; i < 8 && ok; i++) {
158 mi_free(ps[i]);
159 }
160 }
161 result = ok;
162 };
163 CHECK_BODY("malloc-aligned7") {
164 void* p = mi_malloc_aligned(1024,MI_ALIGNMENT_MAX); mi_free(p);
165 };
166 CHECK_BODY("malloc-aligned8") {
167 void* p = mi_malloc_aligned(1024,2*MI_ALIGNMENT_MAX); mi_free(p);
168 };
169 CHECK_BODY("malloc-aligned-at1") {
170 void* p = mi_malloc_aligned_at(48,32,0); result = (p != NULL && ((uintptr_t)(p) + 0) % 32 == 0); mi_free(p);
171 };
172 CHECK_BODY("malloc-aligned-at2") {
173 void* p = mi_malloc_aligned_at(50,32,8); result = (p != NULL && ((uintptr_t)(p) + 8) % 32 == 0); mi_free(p);
174 };
175 CHECK_BODY("memalign1") {
176 void* p;
177 bool ok = true;
178 for (int i = 0; i < 8 && ok; i++) {
179 p = mi_memalign(16,8);
180 ok = (p != NULL && (uintptr_t)(p) % 16 == 0); mi_free(p);
181 }
182 result = ok;
183 };
184
185 // ---------------------------------------------------
186 // Reallocation
187 // ---------------------------------------------------
188 CHECK_BODY("realloc-null") {
189 void* p = mi_realloc(NULL,4);
190 result = (p != NULL);
191 mi_free(p);
192 };
193
194 CHECK_BODY("realloc-null-sizezero") {
195 void* p = mi_realloc(NULL,0); // <https://en.cppreference.com/w/c/memory/realloc> "If ptr is NULL, the behavior is the same as calling malloc(new_size)."
196 result = (p != NULL);
197 mi_free(p);
198 };
199
200 CHECK_BODY("realloc-sizezero") {
201 void* p = mi_malloc(4);
202 void* q = mi_realloc(p, 0);
203 result = (q != NULL);
204 mi_free(q);
205 };
206
207 CHECK_BODY("reallocarray-null-sizezero") {
208 void* p = mi_reallocarray(NULL,0,16); // issue #574
209 result = (p != NULL && errno == 0);
210 mi_free(p);
211 };
212
213 // ---------------------------------------------------
214 // Heaps
215 // ---------------------------------------------------
216 CHECK("heap_destroy", test_heap1());
217 CHECK("heap_delete", test_heap2());
218
219 //mi_stats_print(NULL);
220
221 // ---------------------------------------------------
222 // various
223 // ---------------------------------------------------
224 CHECK_BODY("realpath") {
225 char* s = mi_realpath( ".", NULL );
226 // printf("realpath: %s\n",s);
227 mi_free(s);
228 };
229
230 CHECK("stl_allocator1", test_stl_allocator1());
231 CHECK("stl_allocator2", test_stl_allocator2());
232
233 // ---------------------------------------------------
234 // Done
235 // ---------------------------------------------------[]
236 return print_test_summary();
237}
238
239// ---------------------------------------------------
240// Larger test functions
241// ---------------------------------------------------
242
243bool test_heap1() {
244 mi_heap_t* heap = mi_heap_new();
245 int* p1 = mi_heap_malloc_tp(heap,int);
246 int* p2 = mi_heap_malloc_tp(heap,int);
247 *p1 = *p2 = 43;
248 mi_heap_destroy(heap);
249 return true;
250}
251
252bool test_heap2() {
253 mi_heap_t* heap = mi_heap_new();
254 int* p1 = mi_heap_malloc_tp(heap,int);
255 int* p2 = mi_heap_malloc_tp(heap,int);
256 mi_heap_delete(heap);
257 *p1 = 42;
258 mi_free(p1);
259 mi_free(p2);
260 return true;
261}
262
263bool test_stl_allocator1() {
264#ifdef __cplusplus
265 std::vector<int, mi_stl_allocator<int> > vec;
266 vec.push_back(1);
267 vec.pop_back();
268 return vec.size() == 0;
269#else
270 return true;
271#endif
272}
273
274struct some_struct { int i; int j; double z; };
275
276bool test_stl_allocator2() {
277#ifdef __cplusplus
278 std::vector<some_struct, mi_stl_allocator<some_struct> > vec;
279 vec.push_back(some_struct());
280 vec.pop_back();
281 return vec.size() == 0;
282#else
283 return true;
284#endif
285}
286