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, &test_hooks_libc_hook); 52 } 53 54 static void 55 arena_new_reentrancy_hook() { 56 do_hook(&arena_new_hook_ran, &test_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 current_status) { 91 switch (current_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 void 114 check_global_slow(test_status_t *status) { 115 #ifdef JEMALLOC_UNIT_TEST 116 /* 117 * This check needs to peek into tsd internals, which is why it's only 118 * exposed in unit tests. 119 */ 120 if (tsd_global_slow()) { 121 malloc_printf("Testing increased global slow count\n"); 122 *status = test_status_fail; 123 } 124 #endif 125 } 126 127 static test_status_t 128 p_test_impl(bool do_malloc_init, bool do_reentrant, test_t *t, va_list ap) { 129 test_status_t ret; 130 131 if (do_malloc_init) { 132 /* 133 * Make sure initialization occurs prior to running tests. 134 * Tests are special because they may use internal facilities 135 * prior to triggering initialization as a side effect of 136 * calling into the public API. 137 */ 138 if (nallocx(1, 0) == 0) { 139 malloc_printf("Initialization error"); 140 return test_status_fail; 141 } 142 } 143 144 ret = test_status_pass; 145 for (; t != NULL; t = va_arg(ap, test_t *)) { 146 /* Non-reentrant run. */ 147 reentrancy = non_reentrant; 148 test_hooks_arena_new_hook = test_hooks_libc_hook = NULL; 149 t(); 150 if (test_status > ret) { 151 ret = test_status; 152 } 153 check_global_slow(&ret); 154 /* Reentrant run. */ 155 if (do_reentrant) { 156 reentrancy = libc_reentrant; 157 test_hooks_arena_new_hook = NULL; 158 test_hooks_libc_hook = &libc_reentrancy_hook; 159 t(); 160 if (test_status > ret) { 161 ret = test_status; 162 } 163 check_global_slow(&ret); 164 165 reentrancy = arena_new_reentrant; 166 test_hooks_libc_hook = NULL; 167 test_hooks_arena_new_hook = &arena_new_reentrancy_hook; 168 t(); 169 if (test_status > ret) { 170 ret = test_status; 171 } 172 check_global_slow(&ret); 173 } 174 } 175 176 malloc_printf("--- %s: %u/%u, %s: %u/%u, %s: %u/%u ---\n", 177 test_status_string(test_status_pass), 178 test_counts[test_status_pass], test_count, 179 test_status_string(test_status_skip), 180 test_counts[test_status_skip], test_count, 181 test_status_string(test_status_fail), 182 test_counts[test_status_fail], test_count); 183 184 return ret; 185 } 186 187 test_status_t 188 p_test(test_t *t, ...) { 189 test_status_t ret; 190 va_list ap; 191 192 ret = test_status_pass; 193 va_start(ap, t); 194 ret = p_test_impl(true, true, t, ap); 195 va_end(ap); 196 197 return ret; 198 } 199 200 test_status_t 201 p_test_no_reentrancy(test_t *t, ...) { 202 test_status_t ret; 203 va_list ap; 204 205 ret = test_status_pass; 206 va_start(ap, t); 207 ret = p_test_impl(true, false, t, ap); 208 va_end(ap); 209 210 return ret; 211 } 212 213 test_status_t 214 p_test_no_malloc_init(test_t *t, ...) { 215 test_status_t ret; 216 va_list ap; 217 218 ret = test_status_pass; 219 va_start(ap, t); 220 /* 221 * We also omit reentrancy from bootstrapping tests, since we don't 222 * (yet) care about general reentrancy during bootstrapping. 223 */ 224 ret = p_test_impl(false, false, t, ap); 225 va_end(ap); 226 227 return ret; 228 } 229 230 void 231 p_test_fail(const char *prefix, const char *message) { 232 malloc_cprintf(NULL, NULL, "%s%s\n", prefix, message); 233 test_status = test_status_fail; 234 } 235