mirror of
https://github.com/jemalloc/jemalloc.git
synced 2026-04-14 22:51:50 +03:00
Add mechanism to be able to select a test to run from a test file. The test harness will read the JEMALLOC_TEST_NAME env and, if set, it will only run subtests with that name.
263 lines
5.7 KiB
C
263 lines
5.7 KiB
C
#include "test/jemalloc_test.h"
|
|
|
|
/* Test status state. */
|
|
|
|
static unsigned test_count = 0;
|
|
static test_status_t test_counts[test_status_count] = {0, 0, 0};
|
|
static test_status_t test_status = test_status_pass;
|
|
static const char *test_name = "";
|
|
static const char *selected_test_name = NULL;
|
|
|
|
/* Reentrancy testing helpers. */
|
|
|
|
#define NUM_REENTRANT_ALLOCS 20
|
|
typedef enum {
|
|
non_reentrant = 0,
|
|
libc_reentrant = 1,
|
|
arena_new_reentrant = 2
|
|
} reentrancy_t;
|
|
static reentrancy_t reentrancy;
|
|
|
|
static bool libc_hook_ran = false;
|
|
static bool arena_new_hook_ran = false;
|
|
|
|
static const char *
|
|
reentrancy_t_str(reentrancy_t r) {
|
|
switch (r) {
|
|
case non_reentrant:
|
|
return "non-reentrant";
|
|
case libc_reentrant:
|
|
return "libc-reentrant";
|
|
case arena_new_reentrant:
|
|
return "arena_new-reentrant";
|
|
default:
|
|
unreachable();
|
|
}
|
|
}
|
|
|
|
static void
|
|
do_hook(bool *hook_ran, void (**hook)(void)) {
|
|
*hook_ran = true;
|
|
*hook = NULL;
|
|
|
|
size_t alloc_size = 1;
|
|
for (int i = 0; i < NUM_REENTRANT_ALLOCS; i++) {
|
|
free(malloc(alloc_size));
|
|
alloc_size *= 2;
|
|
}
|
|
}
|
|
|
|
static void
|
|
libc_reentrancy_hook(void) {
|
|
do_hook(&libc_hook_ran, &test_hooks_libc_hook);
|
|
}
|
|
|
|
static void
|
|
arena_new_reentrancy_hook(void) {
|
|
do_hook(&arena_new_hook_ran, &test_hooks_arena_new_hook);
|
|
}
|
|
|
|
/* Actual test infrastructure. */
|
|
bool
|
|
test_is_reentrant() {
|
|
return reentrancy != non_reentrant;
|
|
}
|
|
|
|
JEMALLOC_FORMAT_PRINTF(1, 2)
|
|
void
|
|
test_skip(const char *format, ...) {
|
|
va_list ap;
|
|
|
|
va_start(ap, format);
|
|
malloc_vcprintf(NULL, NULL, format, ap);
|
|
va_end(ap);
|
|
malloc_printf("\n");
|
|
test_status = test_status_skip;
|
|
}
|
|
|
|
JEMALLOC_FORMAT_PRINTF(1, 2)
|
|
void
|
|
test_fail(const char *format, ...) {
|
|
va_list ap;
|
|
|
|
va_start(ap, format);
|
|
malloc_vcprintf(NULL, NULL, format, ap);
|
|
va_end(ap);
|
|
malloc_printf("\n");
|
|
test_status = test_status_fail;
|
|
}
|
|
|
|
static const char *
|
|
test_status_string(test_status_t current_status) {
|
|
switch (current_status) {
|
|
case test_status_pass:
|
|
return "pass";
|
|
case test_status_skip:
|
|
return "skip";
|
|
case test_status_fail:
|
|
return "fail";
|
|
default:
|
|
not_reached();
|
|
}
|
|
}
|
|
|
|
bool
|
|
p_test_init(const char *name) {
|
|
if (selected_test_name != NULL && strcmp(selected_test_name, name)) {
|
|
/* skip test */
|
|
return true;
|
|
}
|
|
|
|
test_count++;
|
|
test_status = test_status_pass;
|
|
test_name = name;
|
|
|
|
return false;
|
|
}
|
|
|
|
void
|
|
p_test_fini(bool skip_test) {
|
|
if (skip_test) {
|
|
return;
|
|
}
|
|
|
|
test_counts[test_status]++;
|
|
malloc_printf("%s (%s): %s\n", test_name, reentrancy_t_str(reentrancy),
|
|
test_status_string(test_status));
|
|
}
|
|
|
|
static void
|
|
check_global_slow(test_status_t *status) {
|
|
#ifdef JEMALLOC_UNIT_TEST
|
|
/*
|
|
* This check needs to peek into tsd internals, which is why it's only
|
|
* exposed in unit tests.
|
|
*/
|
|
if (tsd_global_slow()) {
|
|
malloc_printf("Testing increased global slow count\n");
|
|
*status = test_status_fail;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
static test_status_t
|
|
p_test_impl(bool do_malloc_init, bool do_reentrant, test_t *t, va_list ap) {
|
|
selected_test_name = getenv("JEMALLOC_TEST_NAME");
|
|
|
|
test_status_t ret;
|
|
|
|
if (do_malloc_init) {
|
|
/*
|
|
* Make sure initialization occurs prior to running tests.
|
|
* Tests are special because they may use internal facilities
|
|
* prior to triggering initialization as a side effect of
|
|
* calling into the public API.
|
|
*/
|
|
if (nallocx(1, 0) == 0) {
|
|
malloc_printf("Initialization error");
|
|
return test_status_fail;
|
|
}
|
|
}
|
|
|
|
ret = test_status_pass;
|
|
for (; t != NULL; t = va_arg(ap, test_t *)) {
|
|
/* Non-reentrant run. */
|
|
reentrancy = non_reentrant;
|
|
test_hooks_arena_new_hook = test_hooks_libc_hook = NULL;
|
|
t();
|
|
if (test_status > ret) {
|
|
ret = test_status;
|
|
}
|
|
check_global_slow(&ret);
|
|
/* Reentrant run. */
|
|
if (do_reentrant) {
|
|
reentrancy = libc_reentrant;
|
|
test_hooks_arena_new_hook = NULL;
|
|
test_hooks_libc_hook = &libc_reentrancy_hook;
|
|
t();
|
|
if (test_status > ret) {
|
|
ret = test_status;
|
|
}
|
|
check_global_slow(&ret);
|
|
|
|
reentrancy = arena_new_reentrant;
|
|
test_hooks_libc_hook = NULL;
|
|
test_hooks_arena_new_hook = &arena_new_reentrancy_hook;
|
|
t();
|
|
if (test_status > ret) {
|
|
ret = test_status;
|
|
}
|
|
check_global_slow(&ret);
|
|
}
|
|
}
|
|
|
|
bool colored = test_counts[test_status_fail] != 0
|
|
&& isatty(STDERR_FILENO);
|
|
const char *color_start = colored ? "\033[1;31m" : "";
|
|
const char *color_end = colored ? "\033[0m" : "";
|
|
malloc_printf("%s--- %s: %u/%u, %s: %u/%u, %s: %u/%u ---\n%s",
|
|
color_start, test_status_string(test_status_pass),
|
|
test_counts[test_status_pass], test_count,
|
|
test_status_string(test_status_skip), test_counts[test_status_skip],
|
|
test_count, test_status_string(test_status_fail),
|
|
test_counts[test_status_fail], test_count, color_end);
|
|
|
|
return ret;
|
|
}
|
|
|
|
test_status_t
|
|
p_test(test_t *t, ...) {
|
|
test_status_t ret;
|
|
va_list ap;
|
|
|
|
ret = test_status_pass;
|
|
va_start(ap, t);
|
|
ret = p_test_impl(true, true, t, ap);
|
|
va_end(ap);
|
|
|
|
return ret;
|
|
}
|
|
|
|
test_status_t
|
|
p_test_no_reentrancy(test_t *t, ...) {
|
|
test_status_t ret;
|
|
va_list ap;
|
|
|
|
ret = test_status_pass;
|
|
va_start(ap, t);
|
|
ret = p_test_impl(true, false, t, ap);
|
|
va_end(ap);
|
|
|
|
return ret;
|
|
}
|
|
|
|
test_status_t
|
|
p_test_no_malloc_init(test_t *t, ...) {
|
|
test_status_t ret;
|
|
va_list ap;
|
|
|
|
ret = test_status_pass;
|
|
va_start(ap, t);
|
|
/*
|
|
* We also omit reentrancy from bootstrapping tests, since we don't
|
|
* (yet) care about general reentrancy during bootstrapping.
|
|
*/
|
|
ret = p_test_impl(false, false, t, ap);
|
|
va_end(ap);
|
|
|
|
return ret;
|
|
}
|
|
|
|
void
|
|
p_test_fail(bool may_abort, const char *prefix, const char *message) {
|
|
bool colored = test_counts[test_status_fail] != 0
|
|
&& isatty(STDERR_FILENO);
|
|
const char *color_start = colored ? "\033[1;31m" : "";
|
|
const char *color_end = colored ? "\033[0m" : "";
|
|
malloc_cprintf(
|
|
NULL, NULL, "%s%s%s\n%s", color_start, prefix, message, color_end);
|
|
test_status = test_status_fail;
|
|
if (may_abort) {
|
|
abort();
|
|
}
|
|
}
|