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 * 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 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 50 libc_reentrancy_hook() { 51 do_hook(&libc_hook_ran, &hooks_libc_hook); 52 } 53 54 static void 55 arena_new_reentrancy_hook() { 56 do_hook(&arena_new_hook_ran, &hooks_arena_new_hook); 57 } 58 59 /* Actual test infrastructure. */ 60 bool 61 test_is_reentrant() { 62 return reentrancy != non_reentrant; 63 } 64 65 JEMALLOC_FORMAT_PRINTF(1, 2) 66 void 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 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 * 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 100 p_test_init(const char *name) { 101 test_count++; 102 test_status = test_status_pass; 103 test_name = name; 104 } 105 106 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 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 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 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 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 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