1 #include "test/jemalloc_test.h"
2
3 /* Test status state. */
4
5 static unsigned test_count = 0;
6 static test_status_t test_counts[test_status_count] = {0, 0, 0};
7 static test_status_t test_status = test_status_pass;
8 static const char * test_name = "";
9
10 /* Reentrancy testing helpers. */
11
12 #define NUM_REENTRANT_ALLOCS 20
13 typedef enum {
14 non_reentrant = 0,
15 libc_reentrant = 1,
16 arena_new_reentrant = 2
17 } reentrancy_t;
18 static reentrancy_t reentrancy;
19
20 static bool libc_hook_ran = false;
21 static bool arena_new_hook_ran = false;
22
23 static const char *
reentrancy_t_str(reentrancy_t r)24 reentrancy_t_str(reentrancy_t r) {
25 switch (r) {
26 case non_reentrant:
27 return "non-reentrant";
28 case libc_reentrant:
29 return "libc-reentrant";
30 case arena_new_reentrant:
31 return "arena_new-reentrant";
32 default:
33 unreachable();
34 }
35 }
36
37 static void
do_hook(bool * hook_ran,void (** hook)())38 do_hook(bool *hook_ran, void (**hook)()) {
39 *hook_ran = true;
40 *hook = NULL;
41
42 size_t alloc_size = 1;
43 for (int i = 0; i < NUM_REENTRANT_ALLOCS; i++) {
44 free(malloc(alloc_size));
45 alloc_size *= 2;
46 }
47 }
48
49 static void
libc_reentrancy_hook()50 libc_reentrancy_hook() {
51 do_hook(&libc_hook_ran, &hooks_libc_hook);
52 }
53
54 static void
arena_new_reentrancy_hook()55 arena_new_reentrancy_hook() {
56 do_hook(&arena_new_hook_ran, &hooks_arena_new_hook);
57 }
58
59 /* Actual test infrastructure. */
60 bool
test_is_reentrant()61 test_is_reentrant() {
62 return reentrancy != non_reentrant;
63 }
64
65 JEMALLOC_FORMAT_PRINTF(1, 2)
66 void
test_skip(const char * format,...)67 test_skip(const char *format, ...) {
68 va_list ap;
69
70 va_start(ap, format);
71 malloc_vcprintf(NULL, NULL, format, ap);
72 va_end(ap);
73 malloc_printf("\n");
74 test_status = test_status_skip;
75 }
76
77 JEMALLOC_FORMAT_PRINTF(1, 2)
78 void
test_fail(const char * format,...)79 test_fail(const char *format, ...) {
80 va_list ap;
81
82 va_start(ap, format);
83 malloc_vcprintf(NULL, NULL, format, ap);
84 va_end(ap);
85 malloc_printf("\n");
86 test_status = test_status_fail;
87 }
88
89 static const char *
test_status_string(test_status_t test_status)90 test_status_string(test_status_t test_status) {
91 switch (test_status) {
92 case test_status_pass: return "pass";
93 case test_status_skip: return "skip";
94 case test_status_fail: return "fail";
95 default: not_reached();
96 }
97 }
98
99 void
p_test_init(const char * name)100 p_test_init(const char *name) {
101 test_count++;
102 test_status = test_status_pass;
103 test_name = name;
104 }
105
106 void
p_test_fini(void)107 p_test_fini(void) {
108 test_counts[test_status]++;
109 malloc_printf("%s (%s): %s\n", test_name, reentrancy_t_str(reentrancy),
110 test_status_string(test_status));
111 }
112
113 static test_status_t
p_test_impl(bool do_malloc_init,bool do_reentrant,test_t * t,va_list ap)114 p_test_impl(bool do_malloc_init, bool do_reentrant, test_t *t, va_list ap) {
115 test_status_t ret;
116
117 if (do_malloc_init) {
118 /*
119 * Make sure initialization occurs prior to running tests.
120 * Tests are special because they may use internal facilities
121 * prior to triggering initialization as a side effect of
122 * calling into the public API.
123 */
124 if (nallocx(1, 0) == 0) {
125 malloc_printf("Initialization error");
126 return test_status_fail;
127 }
128 }
129
130 ret = test_status_pass;
131 for (; t != NULL; t = va_arg(ap, test_t *)) {
132 /* Non-reentrant run. */
133 reentrancy = non_reentrant;
134 hooks_arena_new_hook = hooks_libc_hook = NULL;
135 t();
136 if (test_status > ret) {
137 ret = test_status;
138 }
139 /* Reentrant run. */
140 if (do_reentrant) {
141 reentrancy = libc_reentrant;
142 hooks_arena_new_hook = NULL;
143 hooks_libc_hook = &libc_reentrancy_hook;
144 t();
145 if (test_status > ret) {
146 ret = test_status;
147 }
148
149 reentrancy = arena_new_reentrant;
150 hooks_libc_hook = NULL;
151 hooks_arena_new_hook = &arena_new_reentrancy_hook;
152 t();
153 if (test_status > ret) {
154 ret = test_status;
155 }
156 }
157 }
158
159 malloc_printf("--- %s: %u/%u, %s: %u/%u, %s: %u/%u ---\n",
160 test_status_string(test_status_pass),
161 test_counts[test_status_pass], test_count,
162 test_status_string(test_status_skip),
163 test_counts[test_status_skip], test_count,
164 test_status_string(test_status_fail),
165 test_counts[test_status_fail], test_count);
166
167 return ret;
168 }
169
170 test_status_t
p_test(test_t * t,...)171 p_test(test_t *t, ...) {
172 test_status_t ret;
173 va_list ap;
174
175 ret = test_status_pass;
176 va_start(ap, t);
177 ret = p_test_impl(true, true, t, ap);
178 va_end(ap);
179
180 return ret;
181 }
182
183 test_status_t
p_test_no_reentrancy(test_t * t,...)184 p_test_no_reentrancy(test_t *t, ...) {
185 test_status_t ret;
186 va_list ap;
187
188 ret = test_status_pass;
189 va_start(ap, t);
190 ret = p_test_impl(true, false, t, ap);
191 va_end(ap);
192
193 return ret;
194 }
195
196 test_status_t
p_test_no_malloc_init(test_t * t,...)197 p_test_no_malloc_init(test_t *t, ...) {
198 test_status_t ret;
199 va_list ap;
200
201 ret = test_status_pass;
202 va_start(ap, t);
203 /*
204 * We also omit reentrancy from bootstrapping tests, since we don't
205 * (yet) care about general reentrancy during bootstrapping.
206 */
207 ret = p_test_impl(false, false, t, ap);
208 va_end(ap);
209
210 return ret;
211 }
212
213 void
p_test_fail(const char * prefix,const char * message)214 p_test_fail(const char *prefix, const char *message) {
215 malloc_cprintf(NULL, NULL, "%s%s\n", prefix, message);
216 test_status = test_status_fail;
217 }
218