Add hooking functionality

This allows us to hook chosen functions and do interesting things there (in
particular: reentrancy checking).
This commit is contained in:
David Goldblatt
2017-03-28 17:30:54 -07:00
committed by David Goldblatt
parent 36bd90b962
commit 0a0fcd3e6a
19 changed files with 183 additions and 11 deletions

View File

@@ -45,6 +45,7 @@ extern "C" {
# define JEMALLOC_MANGLE
# include "jemalloc/internal/jemalloc_internal.h"
/******************************************************************************/
/*
* For integration tests, expose the public jemalloc interfaces, but only
@@ -68,6 +69,7 @@ static const bool config_debug =
# define JEMALLOC_N(n) @private_namespace@##n
# include "jemalloc/internal/private_namespace.h"
# include "jemalloc/internal/hooks.h"
/* Hermetic headers. */
# include "jemalloc/internal/assert.h"

View File

@@ -310,6 +310,9 @@ label_test_end: \
#define test(...) \
p_test(__VA_ARGS__, NULL)
#define test_no_reentrancy(...) \
p_test_no_reentrancy(__VA_ARGS__, NULL)
#define test_no_malloc_init(...) \
p_test_no_malloc_init(__VA_ARGS__, NULL)
@@ -321,11 +324,14 @@ label_test_end: \
} \
} while (0)
bool test_is_reentrant();
void test_skip(const char *format, ...) JEMALLOC_FORMAT_PRINTF(1, 2);
void test_fail(const char *format, ...) JEMALLOC_FORMAT_PRINTF(1, 2);
/* For private use by macros. */
test_status_t p_test(test_t *t, ...);
test_status_t p_test_no_reentrancy(test_t *t, ...);
test_status_t p_test_no_malloc_init(test_t *t, ...);
void p_test_init(const char *name);
void p_test_fini(void);

View File

@@ -1,10 +1,42 @@
#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 = "";
/* Reentrancy testing helpers. */
#define NUM_REENTRANT_ALLOCS 20
static bool reentrant = false;
static bool hook_ran = false;
static void *to_free[NUM_REENTRANT_ALLOCS];
static void
reentrancy_hook() {
hook_ran = true;
hooks_libc_hook = NULL;
void *to_free_local[NUM_REENTRANT_ALLOCS];
size_t alloc_size = 1;
for (int i = 0; i < NUM_REENTRANT_ALLOCS; i++) {
to_free[i] = malloc(alloc_size);
to_free_local[i] = malloc(alloc_size);
alloc_size *= 2;
}
for (int i = 0; i < NUM_REENTRANT_ALLOCS; i++) {
free(to_free_local[i]);
}
}
/* Actual test infrastructure. */
bool
test_is_reentrant() {
return reentrant;
}
JEMALLOC_FORMAT_PRINTF(1, 2)
void
test_skip(const char *format, ...) {
@@ -49,11 +81,13 @@ p_test_init(const char *name) {
void
p_test_fini(void) {
test_counts[test_status]++;
malloc_printf("%s: %s\n", test_name, test_status_string(test_status));
malloc_printf("%s: %s (%s)\n", test_name,
test_status_string(test_status),
reentrant ? "reentrant" : "non-reentrant");
}
static test_status_t
p_test_impl(bool do_malloc_init, test_t *t, va_list ap) {
p_test_impl(bool do_malloc_init, bool do_reentrant, test_t *t, va_list ap) {
test_status_t ret;
if (do_malloc_init) {
@@ -71,10 +105,27 @@ p_test_impl(bool do_malloc_init, test_t *t, va_list ap) {
ret = test_status_pass;
for (; t != NULL; t = va_arg(ap, test_t *)) {
/* Non-reentrant run. */
reentrant = false;
t();
if (test_status > ret) {
ret = test_status;
}
/* Reentrant run. */
if (do_reentrant) {
reentrant = true;
hooks_libc_hook = &reentrancy_hook;
t();
if (test_status > ret) {
ret = test_status;
}
if (hook_ran) {
hook_ran = false;
for (int i = 0; i < NUM_REENTRANT_ALLOCS; i++) {
free(to_free[i]);
}
}
}
}
malloc_printf("--- %s: %u/%u, %s: %u/%u, %s: %u/%u ---\n",
@@ -95,7 +146,20 @@ p_test(test_t *t, ...) {
ret = test_status_pass;
va_start(ap, t);
ret = p_test_impl(true, t, ap);
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;
@@ -108,7 +172,11 @@ p_test_no_malloc_init(test_t *t, ...) {
ret = test_status_pass;
va_start(ap, t);
ret = p_test_impl(false, t, ap);
/*
* 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;

38
test/unit/hooks.c Normal file
View File

@@ -0,0 +1,38 @@
#include "test/jemalloc_test.h"
static bool hook_called = false;
static void
hook() {
hook_called = true;
}
static int
func_to_hook(int arg1, int arg2) {
return arg1 + arg2;
}
#define func_to_hook JEMALLOC_HOOK(func_to_hook, hooks_libc_hook)
TEST_BEGIN(unhooked_call) {
hooks_libc_hook = NULL;
hook_called = false;
assert_d_eq(3, func_to_hook(1, 2), "Hooking changed return value.");
assert_false(hook_called, "Nulling out hook didn't take.");
}
TEST_END
TEST_BEGIN(hooked_call) {
hooks_libc_hook = &hook;
hook_called = false;
assert_d_eq(3, func_to_hook(1, 2), "Hooking changed return value.");
assert_true(hook_called, "Hook should have executed.");
}
TEST_END
int
main(void) {
return test(
unhooked_call,
hooked_call);
}

View File

@@ -76,6 +76,6 @@ TEST_END
int
main(void) {
return test(
return test_no_reentrancy(
test_idump);
}

View File

@@ -112,6 +112,6 @@ TEST_END
int
main(void) {
return test(
return test_no_reentrancy(
test_prof_active);
}

View File

@@ -69,6 +69,6 @@ TEST_END
int
main(void) {
return test(
return test_no_reentrancy(
test_gdump);
}

View File

@@ -278,7 +278,7 @@ main(void) {
/* Intercept dumping prior to running any tests. */
prof_dump_open = prof_dump_open_intercept;
return test(
return test_no_reentrancy(
test_prof_reset_basic,
test_prof_reset_cleanup,
test_prof_reset,

View File

@@ -41,6 +41,6 @@ TEST_END
int
main(void) {
return test(
return test_no_reentrancy(
test_prof_realloc);
}

View File

@@ -79,6 +79,7 @@ thd_start(void *arg) {
}
TEST_BEGIN(test_tsd_main_thread) {
test_skip_if(test_is_reentrant());
thd_start((void *)(uintptr_t)0xa5f3e329);
}
TEST_END