1#include "test/jemalloc_test.h"
2
3/* Test status state. */
4
5static unsigned		test_count = 0;
6static test_status_t	test_counts[test_status_count] = {0, 0, 0};
7static test_status_t	test_status = test_status_pass;
8static const char *	test_name = "";
9
10/* Reentrancy testing helpers. */
11
12#define NUM_REENTRANT_ALLOCS 20
13typedef enum {
14	non_reentrant = 0,
15	libc_reentrant = 1,
16	arena_new_reentrant = 2
17} reentrancy_t;
18static reentrancy_t reentrancy;
19
20static bool libc_hook_ran = false;
21static bool arena_new_hook_ran = false;
22
23static const char *
24reentrancy_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
37static void
38do_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
49static void
50libc_reentrancy_hook() {
51	do_hook(&libc_hook_ran, &hooks_libc_hook);
52}
53
54static void
55arena_new_reentrancy_hook() {
56	do_hook(&arena_new_hook_ran, &hooks_arena_new_hook);
57}
58
59/* Actual test infrastructure. */
60bool
61test_is_reentrant() {
62	return reentrancy != non_reentrant;
63}
64
65JEMALLOC_FORMAT_PRINTF(1, 2)
66void
67test_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
77JEMALLOC_FORMAT_PRINTF(1, 2)
78void
79test_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
89static const char *
90test_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
99void
100p_test_init(const char *name) {
101	test_count++;
102	test_status = test_status_pass;
103	test_name = name;
104}
105
106void
107p_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
113static test_status_t
114p_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
170test_status_t
171p_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
183test_status_t
184p_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
196test_status_t
197p_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
213void
214p_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