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