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