mirror of
https://github.com/jemalloc/jemalloc.git
synced 2026-07-23 13:13:06 +00:00
Compare commits
8 Commits
716bda4394
...
c823cf2f3c
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c823cf2f3c | ||
|
|
0bce098955 | ||
|
|
bcdd23d44c | ||
|
|
206e3cb1a2 | ||
|
|
cc7be7fbe2 | ||
|
|
820065072b | ||
|
|
901365d473 | ||
|
|
9e143468a2 |
@@ -11,8 +11,7 @@
|
||||
*/
|
||||
|
||||
/*
|
||||
* The following two structs are for experimental purposes. See
|
||||
* experimental_utilization_query_ctl and
|
||||
* The following struct is for experimental purposes. See
|
||||
* experimental_utilization_batch_query_ctl in src/ctl.c.
|
||||
*/
|
||||
typedef struct inspect_extent_util_stats_s inspect_extent_util_stats_t;
|
||||
@@ -22,22 +21,7 @@ struct inspect_extent_util_stats_s {
|
||||
size_t size;
|
||||
};
|
||||
|
||||
typedef struct inspect_extent_util_stats_verbose_s
|
||||
inspect_extent_util_stats_verbose_t;
|
||||
|
||||
struct inspect_extent_util_stats_verbose_s {
|
||||
void *slabcur_addr;
|
||||
size_t nfree;
|
||||
size_t nregs;
|
||||
size_t size;
|
||||
size_t bin_nfree;
|
||||
size_t bin_nregs;
|
||||
};
|
||||
|
||||
void inspect_extent_util_stats_get(
|
||||
tsdn_t *tsdn, const void *ptr, size_t *nfree, size_t *nregs, size_t *size);
|
||||
void inspect_extent_util_stats_verbose_get(tsdn_t *tsdn, const void *ptr,
|
||||
size_t *nfree, size_t *nregs, size_t *size, size_t *bin_nfree,
|
||||
size_t *bin_nregs, void **slabcur_addr);
|
||||
|
||||
#endif /* JEMALLOC_INTERNAL_INSPECT_H */
|
||||
|
||||
@@ -46,6 +46,7 @@ extern bool opt_disable_large_size_classes;
|
||||
|
||||
extern const char *opt_malloc_conf_symlink;
|
||||
extern const char *opt_malloc_conf_env_var;
|
||||
extern const char *je_malloc_conf_2_conf_harder;
|
||||
|
||||
/* Escape free-fastpath when ptr & mask == 0 (for sanitization purpose). */
|
||||
extern uintptr_t san_cache_bin_nonfast_mask;
|
||||
|
||||
@@ -11,11 +11,6 @@ void safety_check_fail_sized_dealloc(
|
||||
bool current_dealloc, const void *ptr, size_t true_size, size_t input_size);
|
||||
void safety_check_fail(const char *format, ...);
|
||||
|
||||
typedef void (*safety_check_abort_hook_t)(const char *message);
|
||||
|
||||
/* Can set to NULL for a default. */
|
||||
void safety_check_set_abort(safety_check_abort_hook_t abort_fn);
|
||||
|
||||
#define REDZONE_SIZE ((size_t)32)
|
||||
#define REDZONE_FILL_VALUE 0xBC
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
|
||||
extern JEMALLOC_EXPORT void (*test_hooks_arena_new_hook)(void);
|
||||
extern JEMALLOC_EXPORT void (*test_hooks_libc_hook)(void);
|
||||
extern JEMALLOC_EXPORT void (*test_hooks_safety_check_abort)(const char *);
|
||||
|
||||
#if defined(JEMALLOC_JET) || defined(JEMALLOC_UNIT_TEST)
|
||||
# define JEMALLOC_TEST_HOOK(fn, hook) \
|
||||
|
||||
371
src/ctl.c
371
src/ctl.c
@@ -173,7 +173,6 @@ CTL_PROTO(opt_process_madvise_max_batch)
|
||||
CTL_PROTO(opt_malloc_conf_symlink)
|
||||
CTL_PROTO(opt_malloc_conf_env_var)
|
||||
CTL_PROTO(opt_malloc_conf_global_var)
|
||||
CTL_PROTO(opt_malloc_conf_global_var_2_conf_harder)
|
||||
CTL_PROTO(tcache_create)
|
||||
CTL_PROTO(tcache_flush)
|
||||
CTL_PROTO(tcache_destroy)
|
||||
@@ -373,11 +372,7 @@ CTL_PROTO(experimental_hooks_prof_dump)
|
||||
CTL_PROTO(experimental_hooks_prof_sample)
|
||||
CTL_PROTO(experimental_hooks_prof_sample_free)
|
||||
CTL_PROTO(experimental_hooks_thread_event)
|
||||
CTL_PROTO(experimental_hooks_safety_check_abort)
|
||||
CTL_PROTO(experimental_utilization_query)
|
||||
CTL_PROTO(experimental_utilization_batch_query)
|
||||
CTL_PROTO(experimental_arenas_i_pactivep)
|
||||
INDEX_PROTO(experimental_arenas_i)
|
||||
CTL_PROTO(experimental_prof_recent_alloc_max)
|
||||
CTL_PROTO(experimental_prof_recent_alloc_dump)
|
||||
CTL_PROTO(experimental_arenas_create_ext)
|
||||
@@ -468,9 +463,7 @@ static const ctl_named_node_t config_node[] = {
|
||||
static const ctl_named_node_t opt_malloc_conf_node[] = {
|
||||
{NAME("symlink"), CTL(opt_malloc_conf_symlink)},
|
||||
{NAME("env_var"), CTL(opt_malloc_conf_env_var)},
|
||||
{NAME("global_var"), CTL(opt_malloc_conf_global_var)},
|
||||
{NAME("global_var_2_conf_harder"),
|
||||
CTL(opt_malloc_conf_global_var_2_conf_harder)}};
|
||||
{NAME("global_var"), CTL(opt_malloc_conf_global_var)}};
|
||||
|
||||
static const ctl_named_node_t opt_node[] = {{NAME("abort"), CTL(opt_abort)},
|
||||
{NAME("abort_conf"), CTL(opt_abort_conf)},
|
||||
@@ -913,22 +906,12 @@ static const ctl_named_node_t experimental_hooks_node[] = {
|
||||
{NAME("prof_dump"), CTL(experimental_hooks_prof_dump)},
|
||||
{NAME("prof_sample"), CTL(experimental_hooks_prof_sample)},
|
||||
{NAME("prof_sample_free"), CTL(experimental_hooks_prof_sample_free)},
|
||||
{NAME("safety_check_abort"), CTL(experimental_hooks_safety_check_abort)},
|
||||
{NAME("thread_event"), CTL(experimental_hooks_thread_event)},
|
||||
};
|
||||
|
||||
static const ctl_named_node_t experimental_utilization_node[] = {
|
||||
{NAME("query"), CTL(experimental_utilization_query)},
|
||||
{NAME("batch_query"), CTL(experimental_utilization_batch_query)}};
|
||||
|
||||
static const ctl_named_node_t experimental_arenas_i_node[] = {
|
||||
{NAME("pactivep"), CTL(experimental_arenas_i_pactivep)}};
|
||||
static const ctl_named_node_t super_experimental_arenas_i_node[] = {
|
||||
{NAME(""), CHILD(named, experimental_arenas_i)}};
|
||||
|
||||
static const ctl_indexed_node_t experimental_arenas_node[] = {
|
||||
{INDEX(experimental_arenas_i)}};
|
||||
|
||||
static const ctl_named_node_t experimental_prof_recent_node[] = {
|
||||
{NAME("alloc_max"), CTL(experimental_prof_recent_alloc_max)},
|
||||
{NAME("alloc_dump"), CTL(experimental_prof_recent_alloc_dump)},
|
||||
@@ -937,7 +920,6 @@ static const ctl_named_node_t experimental_prof_recent_node[] = {
|
||||
static const ctl_named_node_t experimental_node[] = {
|
||||
{NAME("hooks"), CHILD(named, experimental_hooks)},
|
||||
{NAME("utilization"), CHILD(named, experimental_utilization)},
|
||||
{NAME("arenas"), CHILD(indexed, experimental_arenas)},
|
||||
{NAME("arenas_create_ext"), CTL(experimental_arenas_create_ext)},
|
||||
{NAME("prof_recent"), CHILD(named, experimental_prof_recent)}};
|
||||
|
||||
@@ -981,8 +963,41 @@ ctl_accum_atomic_zu(atomic_zu_t *dst, atomic_zu_t *src) {
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
/*
|
||||
* Historical compatibility for treating arena.<narenas> as the merged
|
||||
* all-arenas entry. New code should use MALLCTL_ARENAS_ALL.
|
||||
*
|
||||
* `narenas` must be a snapshot of ctl_arenas->narenas taken while holding
|
||||
* ctl_mtx; see ctl_narenas_get.
|
||||
*/
|
||||
static bool
|
||||
ctl_arena_ind_is_deprecated_all(size_t i, unsigned narenas) {
|
||||
return i == narenas;
|
||||
}
|
||||
|
||||
/*
|
||||
* `narenas` must be a snapshot of ctl_arenas->narenas taken while holding
|
||||
* ctl_mtx; see ctl_narenas_get.
|
||||
*/
|
||||
static bool
|
||||
ctl_arena_ind_is_all(size_t i, unsigned narenas) {
|
||||
return i == MALLCTL_ARENAS_ALL
|
||||
|| ctl_arena_ind_is_deprecated_all(i, narenas);
|
||||
}
|
||||
|
||||
/*
|
||||
* ctl_arenas->narenas may grow concurrently (arena creation in
|
||||
* ctl_arena_init); all reads must happen while ctl_mtx is held. This
|
||||
* helper centralizes that requirement.
|
||||
*/
|
||||
static unsigned
|
||||
arenas_i2a_impl(size_t i, bool compat, bool validate) {
|
||||
ctl_narenas_get(tsdn_t *tsdn) {
|
||||
malloc_mutex_assert_owner(tsdn, &ctl_mtx);
|
||||
return ctl_arenas->narenas;
|
||||
}
|
||||
|
||||
static unsigned
|
||||
arenas_i2a_impl(size_t i, unsigned narenas, bool compat, bool validate) {
|
||||
unsigned a;
|
||||
|
||||
switch (i) {
|
||||
@@ -993,7 +1008,7 @@ arenas_i2a_impl(size_t i, bool compat, bool validate) {
|
||||
a = 1;
|
||||
break;
|
||||
default:
|
||||
if (compat && i == ctl_arenas->narenas) {
|
||||
if (compat && ctl_arena_ind_is_deprecated_all(i, narenas)) {
|
||||
/*
|
||||
* Provide deprecated backward compatibility for
|
||||
* accessing the merged stats at index narenas rather
|
||||
@@ -1001,7 +1016,7 @@ arenas_i2a_impl(size_t i, bool compat, bool validate) {
|
||||
* removal in 6.0.0.
|
||||
*/
|
||||
a = 0;
|
||||
} else if (validate && i >= ctl_arenas->narenas) {
|
||||
} else if (validate && i >= narenas) {
|
||||
a = UINT_MAX;
|
||||
} else {
|
||||
/*
|
||||
@@ -1009,8 +1024,7 @@ arenas_i2a_impl(size_t i, bool compat, bool validate) {
|
||||
* more than one past the range of indices that have
|
||||
* initialized ctl data.
|
||||
*/
|
||||
assert(i < ctl_arenas->narenas
|
||||
|| (!validate && i == ctl_arenas->narenas));
|
||||
assert(i < narenas || (!validate && i == narenas));
|
||||
a = (unsigned)i + 2;
|
||||
}
|
||||
break;
|
||||
@@ -1020,8 +1034,8 @@ arenas_i2a_impl(size_t i, bool compat, bool validate) {
|
||||
}
|
||||
|
||||
static unsigned
|
||||
arenas_i2a(size_t i) {
|
||||
return arenas_i2a_impl(i, true, false);
|
||||
arenas_i2a(size_t i, unsigned narenas) {
|
||||
return arenas_i2a_impl(i, narenas, true, false);
|
||||
}
|
||||
|
||||
static ctl_arena_t *
|
||||
@@ -1029,8 +1043,9 @@ arenas_i_impl(tsd_t *tsd, size_t i, bool compat, bool init) {
|
||||
ctl_arena_t *ret;
|
||||
|
||||
assert(!compat || !init);
|
||||
unsigned narenas = ctl_narenas_get(tsd_tsdn(tsd));
|
||||
|
||||
ret = ctl_arenas->arenas[arenas_i2a_impl(i, compat, false)];
|
||||
ret = ctl_arenas->arenas[arenas_i2a_impl(i, narenas, compat, false)];
|
||||
if (init && ret == NULL) {
|
||||
if (config_stats) {
|
||||
struct container_s {
|
||||
@@ -1053,10 +1068,12 @@ arenas_i_impl(tsd_t *tsd, size_t i, bool compat, bool init) {
|
||||
}
|
||||
}
|
||||
ret->arena_ind = (unsigned)i;
|
||||
ctl_arenas->arenas[arenas_i2a_impl(i, compat, false)] = ret;
|
||||
ctl_arenas->arenas[arenas_i2a_impl(i, narenas, compat, false)]
|
||||
= ret;
|
||||
}
|
||||
|
||||
assert(ret == NULL || arenas_i2a(ret->arena_ind) == arenas_i2a(i));
|
||||
assert(ret == NULL ||
|
||||
arenas_i2a(ret->arena_ind, narenas) == arenas_i2a(i, narenas));
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -1309,6 +1326,8 @@ ctl_arena_init(tsd_t *tsd, const arena_config_t *config) {
|
||||
unsigned arena_ind;
|
||||
ctl_arena_t *ctl_arena;
|
||||
|
||||
malloc_mutex_assert_owner(tsd_tsdn(tsd), &ctl_mtx);
|
||||
|
||||
if ((ctl_arena = ql_last(&ctl_arenas->destroyed, destroyed_link))
|
||||
!= NULL) {
|
||||
ql_remove(&ctl_arenas->destroyed, ctl_arena, destroyed_link);
|
||||
@@ -2288,9 +2307,6 @@ CTL_RO_NL_CGEN(opt_malloc_conf_env_var, opt_malloc_conf_env_var,
|
||||
opt_malloc_conf_env_var, const char *)
|
||||
CTL_RO_NL_CGEN(
|
||||
je_malloc_conf, opt_malloc_conf_global_var, je_malloc_conf, const char *)
|
||||
CTL_RO_NL_CGEN(je_malloc_conf_2_conf_harder,
|
||||
opt_malloc_conf_global_var_2_conf_harder, je_malloc_conf_2_conf_harder,
|
||||
const char *)
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
@@ -2696,46 +2712,29 @@ label_return:
|
||||
static void
|
||||
arena_i_decay(tsdn_t *tsdn, unsigned arena_ind, bool all) {
|
||||
malloc_mutex_lock(tsdn, &ctl_mtx);
|
||||
{
|
||||
unsigned narenas = ctl_arenas->narenas;
|
||||
unsigned narenas = ctl_narenas_get(tsdn);
|
||||
|
||||
/*
|
||||
* Access via index narenas is deprecated, and scheduled for
|
||||
* removal in 6.0.0.
|
||||
*/
|
||||
if (arena_ind == MALLCTL_ARENAS_ALL || arena_ind == narenas) {
|
||||
unsigned i;
|
||||
VARIABLE_ARRAY_UNSAFE(arena_t *, tarenas, narenas);
|
||||
/*
|
||||
* Access via index narenas is deprecated, and scheduled for
|
||||
* removal in 6.0.0.
|
||||
*/
|
||||
bool decay_all = ctl_arena_ind_is_all(arena_ind, narenas);
|
||||
unsigned count = decay_all ? narenas : 1;
|
||||
VARIABLE_ARRAY_UNSAFE(arena_t *, tarenas, count);
|
||||
|
||||
for (i = 0; i < narenas; i++) {
|
||||
tarenas[i] = arena_get(tsdn, i, false);
|
||||
}
|
||||
if (decay_all) {
|
||||
for (unsigned i = 0; i < narenas; i++) {
|
||||
tarenas[i] = arena_get(tsdn, i, false);
|
||||
}
|
||||
} else {
|
||||
assert(arena_ind < narenas);
|
||||
tarenas[0] = arena_get(tsdn, arena_ind, false);
|
||||
}
|
||||
malloc_mutex_unlock(tsdn, &ctl_mtx);
|
||||
|
||||
/*
|
||||
* No further need to hold ctl_mtx, since narenas and
|
||||
* tarenas contain everything needed below.
|
||||
*/
|
||||
malloc_mutex_unlock(tsdn, &ctl_mtx);
|
||||
|
||||
for (i = 0; i < narenas; i++) {
|
||||
if (tarenas[i] != NULL) {
|
||||
arena_decay(
|
||||
tsdn, tarenas[i], false, all);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
arena_t *tarena;
|
||||
|
||||
assert(arena_ind < narenas);
|
||||
|
||||
tarena = arena_get(tsdn, arena_ind, false);
|
||||
|
||||
/* No further need to hold ctl_mtx. */
|
||||
malloc_mutex_unlock(tsdn, &ctl_mtx);
|
||||
|
||||
if (tarena != NULL) {
|
||||
arena_decay(tsdn, tarena, false, all);
|
||||
}
|
||||
for (unsigned i = 0; i < count; i++) {
|
||||
if (tarenas[i] != NULL) {
|
||||
arena_decay(tsdn, tarenas[i], false, all);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2920,8 +2919,8 @@ arena_i_dss_ctl(tsd_t *tsd, const size_t *mib, size_t miblen, void *oldp,
|
||||
* 6.0.0.
|
||||
*/
|
||||
dss_prec_t dss_prec_old;
|
||||
if (arena_ind == MALLCTL_ARENAS_ALL
|
||||
|| arena_ind == ctl_arenas->narenas) {
|
||||
unsigned narenas = ctl_narenas_get(tsd_tsdn(tsd));
|
||||
if (ctl_arena_ind_is_all(arena_ind, narenas)) {
|
||||
if (dss_prec != dss_prec_limit
|
||||
&& extent_dss_prec_set(dss_prec)) {
|
||||
ret = EFAULT;
|
||||
@@ -2996,7 +2995,7 @@ arena_i_decay_ms_ctl_impl(tsd_t *tsd, const size_t *mib, size_t miblen,
|
||||
extent_state_t state = dirty ? extent_state_dirty : extent_state_muzzy;
|
||||
|
||||
if (oldp != NULL && oldlenp != NULL) {
|
||||
size_t oldval = arena_decay_ms_get(arena, state);
|
||||
ssize_t oldval = arena_decay_ms_get(arena, state);
|
||||
READ(oldval, ssize_t);
|
||||
}
|
||||
if (newp != NULL) {
|
||||
@@ -3141,8 +3140,9 @@ arena_i_name_ctl(tsd_t *tsd, const size_t *mib, size_t miblen, void *oldp,
|
||||
|
||||
malloc_mutex_lock(tsd_tsdn(tsd), &ctl_mtx);
|
||||
MIB_UNSIGNED(arena_ind, 1);
|
||||
if (arena_ind == MALLCTL_ARENAS_ALL
|
||||
|| arena_ind >= ctl_arenas->narenas) {
|
||||
unsigned narenas = ctl_narenas_get(tsd_tsdn(tsd));
|
||||
if (ctl_arena_ind_is_all(arena_ind, narenas)
|
||||
|| arena_ind > narenas) {
|
||||
ret = EINVAL;
|
||||
goto label_return;
|
||||
}
|
||||
@@ -3191,7 +3191,7 @@ arena_i_index(tsdn_t *tsdn, const size_t *mib, size_t miblen, size_t i) {
|
||||
case MALLCTL_ARENAS_DESTROYED:
|
||||
break;
|
||||
default:
|
||||
if (i > ctl_arenas->narenas) {
|
||||
if (i > ctl_narenas_get(tsdn)) {
|
||||
ret = NULL;
|
||||
goto label_return;
|
||||
}
|
||||
@@ -3214,7 +3214,7 @@ arenas_narenas_ctl(tsd_t *tsd, const size_t *mib, size_t miblen, void *oldp,
|
||||
|
||||
malloc_mutex_lock(tsd_tsdn(tsd), &ctl_mtx);
|
||||
READONLY();
|
||||
narenas = ctl_arenas->narenas;
|
||||
narenas = ctl_narenas_get(tsd_tsdn(tsd));
|
||||
READ(narenas, unsigned);
|
||||
|
||||
ret = 0;
|
||||
@@ -3229,8 +3229,8 @@ arenas_decay_ms_ctl_impl(tsd_t *tsd, const size_t *mib, size_t miblen,
|
||||
int ret;
|
||||
|
||||
if (oldp != NULL && oldlenp != NULL) {
|
||||
size_t oldval = (dirty ? arena_dirty_decay_ms_default_get()
|
||||
: arena_muzzy_decay_ms_default_get());
|
||||
ssize_t oldval = (dirty ? arena_dirty_decay_ms_default_get()
|
||||
: arena_muzzy_decay_ms_default_get());
|
||||
READ(oldval, ssize_t);
|
||||
}
|
||||
if (newp != NULL) {
|
||||
@@ -3296,23 +3296,34 @@ arenas_lextent_i_index(
|
||||
}
|
||||
|
||||
static int
|
||||
arenas_create_ctl(tsd_t *tsd, const size_t *mib, size_t miblen, void *oldp,
|
||||
size_t *oldlenp, void *newp, size_t newlen) {
|
||||
int ret;
|
||||
ctl_arena_create(tsd_t *tsd, void *oldp, size_t *oldlenp,
|
||||
const arena_config_t *config) {
|
||||
int ret;
|
||||
unsigned arena_ind;
|
||||
|
||||
malloc_mutex_lock(tsd_tsdn(tsd), &ctl_mtx);
|
||||
|
||||
VERIFY_READ(unsigned);
|
||||
arena_config_t config = arena_config_default;
|
||||
WRITE(config.extent_hooks, extent_hooks_t *);
|
||||
if ((arena_ind = ctl_arena_init(tsd, &config)) == UINT_MAX) {
|
||||
if ((arena_ind = ctl_arena_init(tsd, config)) == UINT_MAX) {
|
||||
ret = EAGAIN;
|
||||
goto label_return;
|
||||
}
|
||||
READ(arena_ind, unsigned);
|
||||
|
||||
ret = 0;
|
||||
label_return:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int
|
||||
arenas_create_ctl(tsd_t *tsd, const size_t *mib, size_t miblen, void *oldp,
|
||||
size_t *oldlenp, void *newp, size_t newlen) {
|
||||
int ret;
|
||||
|
||||
malloc_mutex_lock(tsd_tsdn(tsd), &ctl_mtx);
|
||||
|
||||
VERIFY_READ(unsigned);
|
||||
arena_config_t config = arena_config_default;
|
||||
WRITE(config.extent_hooks, extent_hooks_t *);
|
||||
|
||||
ret = ctl_arena_create(tsd, oldp, oldlenp, &config);
|
||||
label_return:
|
||||
malloc_mutex_unlock(tsd_tsdn(tsd), &ctl_mtx);
|
||||
return ret;
|
||||
@@ -3321,8 +3332,7 @@ label_return:
|
||||
static int
|
||||
experimental_arenas_create_ext_ctl(tsd_t *tsd, const size_t *mib, size_t miblen,
|
||||
void *oldp, size_t *oldlenp, void *newp, size_t newlen) {
|
||||
int ret;
|
||||
unsigned arena_ind;
|
||||
int ret;
|
||||
|
||||
malloc_mutex_lock(tsd_tsdn(tsd), &ctl_mtx);
|
||||
|
||||
@@ -3330,12 +3340,7 @@ experimental_arenas_create_ext_ctl(tsd_t *tsd, const size_t *mib, size_t miblen,
|
||||
VERIFY_READ(unsigned);
|
||||
WRITE(config, arena_config_t);
|
||||
|
||||
if ((arena_ind = ctl_arena_init(tsd, &config)) == UINT_MAX) {
|
||||
ret = EAGAIN;
|
||||
goto label_return;
|
||||
}
|
||||
READ(arena_ind, unsigned);
|
||||
ret = 0;
|
||||
ret = ctl_arena_create(tsd, oldp, oldlenp, &config);
|
||||
label_return:
|
||||
malloc_mutex_unlock(tsd_tsdn(tsd), &ctl_mtx);
|
||||
return ret;
|
||||
@@ -3714,27 +3719,6 @@ label_return:
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* For integration test purpose only. No plan to move out of experimental. */
|
||||
static int
|
||||
experimental_hooks_safety_check_abort_ctl(tsd_t *tsd, const size_t *mib,
|
||||
size_t miblen, void *oldp, size_t *oldlenp, void *newp, size_t newlen) {
|
||||
int ret;
|
||||
|
||||
WRITEONLY();
|
||||
if (newp != NULL) {
|
||||
if (newlen != sizeof(safety_check_abort_hook_t)) {
|
||||
ret = EINVAL;
|
||||
goto label_return;
|
||||
}
|
||||
safety_check_abort_hook_t hook JEMALLOC_CC_SILENCE_INIT(NULL);
|
||||
WRITE(hook, safety_check_abort_hook_t);
|
||||
safety_check_set_abort(hook);
|
||||
}
|
||||
ret = 0;
|
||||
label_return:
|
||||
return ret;
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
CTL_RO_CGEN(config_stats, stats_allocated, ctl_stats->allocated, size_t)
|
||||
@@ -4254,8 +4238,8 @@ stats_arenas_i_hpa_shard_alloc_j_index(
|
||||
}
|
||||
|
||||
static bool
|
||||
ctl_arenas_i_verify(size_t i) {
|
||||
size_t a = arenas_i2a_impl(i, true, true);
|
||||
ctl_arenas_i_verify(size_t i, unsigned narenas) {
|
||||
size_t a = arenas_i2a_impl(i, narenas, true, true);
|
||||
if (a == UINT_MAX || !ctl_arenas->arenas[a]->initialized) {
|
||||
return true;
|
||||
}
|
||||
@@ -4268,7 +4252,7 @@ stats_arenas_i_index(tsdn_t *tsdn, const size_t *mib, size_t miblen, size_t i) {
|
||||
const ctl_named_node_t *ret;
|
||||
|
||||
malloc_mutex_lock(tsdn, &ctl_mtx);
|
||||
if (ctl_arenas_i_verify(i)) {
|
||||
if (ctl_arenas_i_verify(i, ctl_narenas_get(tsdn))) {
|
||||
ret = NULL;
|
||||
goto label_return;
|
||||
}
|
||||
@@ -4279,102 +4263,6 @@ label_return:
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Output six memory utilization entries for an input pointer, the first one of
|
||||
* type (void *) and the remaining five of type size_t, describing the following
|
||||
* (in the same order):
|
||||
*
|
||||
* (a) memory address of the extent a potential reallocation would go into,
|
||||
* == the five fields below describe about the extent the pointer resides in ==
|
||||
* (b) number of free regions in the extent,
|
||||
* (c) number of regions in the extent,
|
||||
* (d) size of the extent in terms of bytes,
|
||||
* (e) total number of free regions in the bin the extent belongs to, and
|
||||
* (f) total number of regions in the bin the extent belongs to.
|
||||
*
|
||||
* Note that "(e)" and "(f)" are only available when stats are enabled;
|
||||
* otherwise their values are undefined.
|
||||
*
|
||||
* This API is mainly intended for small class allocations, where extents are
|
||||
* used as slab. Note that if the bin the extent belongs to is completely
|
||||
* full, "(a)" will be NULL.
|
||||
*
|
||||
* In case of large class allocations, "(a)" will be NULL, and "(e)" and "(f)"
|
||||
* will be zero (if stats are enabled; otherwise undefined). The other three
|
||||
* fields will be properly set though the values are trivial: "(b)" will be 0,
|
||||
* "(c)" will be 1, and "(d)" will be the usable size.
|
||||
*
|
||||
* The input pointer and size are respectively passed in by newp and newlen,
|
||||
* and the output fields and size are respectively oldp and *oldlenp.
|
||||
*
|
||||
* It can be beneficial to define the following macros to make it easier to
|
||||
* access the output:
|
||||
*
|
||||
* #define SLABCUR_READ(out) (*(void **)out)
|
||||
* #define COUNTS(out) ((size_t *)((void **)out + 1))
|
||||
* #define NFREE_READ(out) COUNTS(out)[0]
|
||||
* #define NREGS_READ(out) COUNTS(out)[1]
|
||||
* #define SIZE_READ(out) COUNTS(out)[2]
|
||||
* #define BIN_NFREE_READ(out) COUNTS(out)[3]
|
||||
* #define BIN_NREGS_READ(out) COUNTS(out)[4]
|
||||
*
|
||||
* and then write e.g. NFREE_READ(oldp) to fetch the output. See the unit test
|
||||
* test_query in test/unit/extent_util.c for an example.
|
||||
*
|
||||
* For a typical defragmentation workflow making use of this API for
|
||||
* understanding the fragmentation level, please refer to the comment for
|
||||
* experimental_utilization_batch_query_ctl.
|
||||
*
|
||||
* It's up to the application how to determine the significance of
|
||||
* fragmentation relying on the outputs returned. Possible choices are:
|
||||
*
|
||||
* (a) if extent utilization ratio is below certain threshold,
|
||||
* (b) if extent memory consumption is above certain threshold,
|
||||
* (c) if extent utilization ratio is significantly below bin utilization ratio,
|
||||
* (d) if input pointer deviates a lot from potential reallocation address, or
|
||||
* (e) some selection/combination of the above.
|
||||
*
|
||||
* The caller needs to make sure that the input/output arguments are valid,
|
||||
* in particular, that the size of the output is correct, i.e.:
|
||||
*
|
||||
* *oldlenp = sizeof(void *) + sizeof(size_t) * 5
|
||||
*
|
||||
* Otherwise, the function immediately returns EINVAL without touching anything.
|
||||
*
|
||||
* In the rare case where there's no associated extent found for the input
|
||||
* pointer, the function zeros out all output fields and return. Please refer
|
||||
* to the comment for experimental_utilization_batch_query_ctl to understand the
|
||||
* motivation from C++.
|
||||
*/
|
||||
static int
|
||||
experimental_utilization_query_ctl(tsd_t *tsd, const size_t *mib, size_t miblen,
|
||||
void *oldp, size_t *oldlenp, void *newp, size_t newlen) {
|
||||
int ret;
|
||||
|
||||
assert(sizeof(inspect_extent_util_stats_verbose_t)
|
||||
== sizeof(void *) + sizeof(size_t) * 5);
|
||||
|
||||
if (oldp == NULL || oldlenp == NULL
|
||||
|| *oldlenp != sizeof(inspect_extent_util_stats_verbose_t)
|
||||
|| newp == NULL) {
|
||||
ret = EINVAL;
|
||||
goto label_return;
|
||||
}
|
||||
|
||||
void *ptr = NULL;
|
||||
WRITE(ptr, void *);
|
||||
inspect_extent_util_stats_verbose_t *util_stats =
|
||||
(inspect_extent_util_stats_verbose_t *)oldp;
|
||||
inspect_extent_util_stats_verbose_get(tsd_tsdn(tsd), ptr,
|
||||
&util_stats->nfree, &util_stats->nregs, &util_stats->size,
|
||||
&util_stats->bin_nfree, &util_stats->bin_nregs,
|
||||
&util_stats->slabcur_addr);
|
||||
ret = 0;
|
||||
|
||||
label_return:
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Given an input array of pointers, output three memory utilization entries of
|
||||
* type size_t for each input pointer about the extent it resides in:
|
||||
@@ -4501,59 +4389,6 @@ label_return:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const ctl_named_node_t *
|
||||
experimental_arenas_i_index(
|
||||
tsdn_t *tsdn, const size_t *mib, size_t miblen, size_t i) {
|
||||
const ctl_named_node_t *ret;
|
||||
|
||||
malloc_mutex_lock(tsdn, &ctl_mtx);
|
||||
if (ctl_arenas_i_verify(i)) {
|
||||
ret = NULL;
|
||||
goto label_return;
|
||||
}
|
||||
ret = super_experimental_arenas_i_node;
|
||||
label_return:
|
||||
malloc_mutex_unlock(tsdn, &ctl_mtx);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int
|
||||
experimental_arenas_i_pactivep_ctl(tsd_t *tsd, const size_t *mib, size_t miblen,
|
||||
void *oldp, size_t *oldlenp, void *newp, size_t newlen) {
|
||||
if (!config_stats) {
|
||||
return ENOENT;
|
||||
}
|
||||
if (oldp == NULL || oldlenp == NULL || *oldlenp != sizeof(size_t *)) {
|
||||
return EINVAL;
|
||||
}
|
||||
|
||||
unsigned arena_ind;
|
||||
arena_t *arena;
|
||||
int ret;
|
||||
size_t *pactivep;
|
||||
|
||||
malloc_mutex_lock(tsd_tsdn(tsd), &ctl_mtx);
|
||||
READONLY();
|
||||
MIB_UNSIGNED(arena_ind, 2);
|
||||
if (arena_ind < narenas_total_get()
|
||||
&& (arena = arena_get(tsd_tsdn(tsd), arena_ind, false)) != NULL) {
|
||||
#if defined(JEMALLOC_GCC_ATOMIC_ATOMICS) || defined(JEMALLOC_GCC_SYNC_ATOMICS) \
|
||||
|| defined(_MSC_VER)
|
||||
/* Expose the underlying counter for fast read. */
|
||||
pactivep = (size_t *)&(arena->pa_shard.nactive.repr);
|
||||
READ(pactivep, size_t *);
|
||||
ret = 0;
|
||||
#else
|
||||
ret = EFAULT;
|
||||
#endif
|
||||
} else {
|
||||
ret = EFAULT;
|
||||
}
|
||||
label_return:
|
||||
malloc_mutex_unlock(tsd_tsdn(tsd), &ctl_mtx);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int
|
||||
experimental_prof_recent_alloc_max_ctl(tsd_t *tsd, const size_t *mib,
|
||||
size_t miblen, void *oldp, size_t *oldlenp, void *newp, size_t newlen) {
|
||||
|
||||
@@ -24,54 +24,3 @@ inspect_extent_util_stats_get(
|
||||
assert(*nfree * edata_usize_get(edata) <= *size);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
inspect_extent_util_stats_verbose_get(tsdn_t *tsdn, const void *ptr,
|
||||
size_t *nfree, size_t *nregs, size_t *size, size_t *bin_nfree,
|
||||
size_t *bin_nregs, void **slabcur_addr) {
|
||||
assert(ptr != NULL && nfree != NULL && nregs != NULL && size != NULL
|
||||
&& bin_nfree != NULL && bin_nregs != NULL && slabcur_addr != NULL);
|
||||
|
||||
const edata_t *edata = emap_edata_lookup(tsdn, &arena_emap_global, ptr);
|
||||
if (unlikely(edata == NULL)) {
|
||||
*nfree = *nregs = *size = *bin_nfree = *bin_nregs = 0;
|
||||
*slabcur_addr = NULL;
|
||||
return;
|
||||
}
|
||||
|
||||
*size = edata_size_get(edata);
|
||||
if (!edata_slab_get(edata)) {
|
||||
*nfree = *bin_nfree = *bin_nregs = 0;
|
||||
*nregs = 1;
|
||||
*slabcur_addr = NULL;
|
||||
return;
|
||||
}
|
||||
|
||||
*nfree = edata_nfree_get(edata);
|
||||
const szind_t szind = edata_szind_get(edata);
|
||||
*nregs = bin_infos[szind].nregs;
|
||||
assert(*nfree <= *nregs);
|
||||
assert(*nfree * edata_usize_get(edata) <= *size);
|
||||
|
||||
arena_t *arena = arena_get_from_edata(edata);
|
||||
assert(arena != NULL);
|
||||
const unsigned binshard = edata_binshard_get(edata);
|
||||
bin_t *bin = arena_get_bin(arena, szind, binshard);
|
||||
|
||||
malloc_mutex_lock(tsdn, &bin->lock);
|
||||
if (config_stats) {
|
||||
*bin_nregs = *nregs * bin->stats.curslabs;
|
||||
assert(*bin_nregs >= bin->stats.curregs);
|
||||
*bin_nfree = *bin_nregs - bin->stats.curregs;
|
||||
} else {
|
||||
*bin_nfree = *bin_nregs = 0;
|
||||
}
|
||||
edata_t *slab;
|
||||
if (bin->slabcur != NULL) {
|
||||
slab = bin->slabcur;
|
||||
} else {
|
||||
slab = edata_heap_first(&bin->slabs_nonfull);
|
||||
}
|
||||
*slabcur_addr = slab != NULL ? edata_addr_get(slab) : NULL;
|
||||
malloc_mutex_unlock(tsdn, &bin->lock);
|
||||
}
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
#include "jemalloc/internal/jemalloc_preamble.h"
|
||||
#include "jemalloc/internal/jemalloc_internal_includes.h"
|
||||
|
||||
static safety_check_abort_hook_t safety_check_abort;
|
||||
|
||||
void
|
||||
safety_check_fail_sized_dealloc(bool current_dealloc, const void *ptr,
|
||||
size_t true_size, size_t input_size) {
|
||||
@@ -19,23 +17,18 @@ safety_check_fail_sized_dealloc(bool current_dealloc, const void *ptr,
|
||||
true_size, input_size, ptr, src, suggest_debug_build);
|
||||
}
|
||||
|
||||
void
|
||||
safety_check_set_abort(safety_check_abort_hook_t abort_fn) {
|
||||
safety_check_abort = abort_fn;
|
||||
}
|
||||
|
||||
/*
|
||||
* In addition to malloc_write, also embed hint msg in the abort function name
|
||||
* because there are cases only logging crash stack traces.
|
||||
*/
|
||||
static void
|
||||
safety_check_detected_heap_corruption___run_address_sanitizer_build_to_debug(
|
||||
const char *buf) {
|
||||
if (safety_check_abort == NULL) {
|
||||
const char *buf) {
|
||||
if (test_hooks_safety_check_abort == NULL) {
|
||||
malloc_write(buf);
|
||||
abort();
|
||||
} else {
|
||||
safety_check_abort(buf);
|
||||
test_hooks_safety_check_abort(buf);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1708,10 +1708,9 @@ stats_general_print(emitter_t *emitter) {
|
||||
MALLOC_CONF_WRITE("global_var", "Global variable malloc_conf");
|
||||
MALLOC_CONF_WRITE("symlink", "Symbolic link malloc.conf");
|
||||
MALLOC_CONF_WRITE("env_var", "Environment variable MALLOC_CONF");
|
||||
/* As this config is unofficial, skip the output if it's NULL */
|
||||
if (je_mallctl("opt.malloc_conf.global_var_2_conf_harder", (void *)&cpv,
|
||||
&cpsz, NULL, 0)
|
||||
== 0) {
|
||||
/* As this config is unofficial, skip the output if it's NULL. */
|
||||
if (je_malloc_conf_2_conf_harder != NULL) {
|
||||
cpv = je_malloc_conf_2_conf_harder;
|
||||
emitter_kv(emitter, "global_var_2_conf_harder",
|
||||
"Global "
|
||||
"variable malloc_conf_2_conf_harder",
|
||||
|
||||
@@ -10,3 +10,6 @@ void (*test_hooks_arena_new_hook)(void) = NULL;
|
||||
|
||||
JEMALLOC_EXPORT
|
||||
void (*test_hooks_libc_hook)(void) = NULL;
|
||||
|
||||
JEMALLOC_EXPORT
|
||||
void (*test_hooks_safety_check_abort)(const char *) = NULL;
|
||||
|
||||
@@ -3,10 +3,9 @@
|
||||
#include "test/jemalloc_test.h"
|
||||
|
||||
/*
|
||||
* We can't test C++ in unit tests. In order to intercept abort, use a secret
|
||||
* safety check abort hook in integration tests.
|
||||
* We can't test C++ in unit tests. In order to intercept abort, use the
|
||||
* internal test hook in integration tests.
|
||||
*/
|
||||
typedef void (*abort_hook_t)(const char *message);
|
||||
bool fake_abort_called;
|
||||
void
|
||||
fake_abort(const char *message) {
|
||||
@@ -34,10 +33,7 @@ own_operator_new(void) {
|
||||
}
|
||||
|
||||
TEST_BEGIN(test_failing_alloc) {
|
||||
abort_hook_t abort_hook = &fake_abort;
|
||||
expect_d_eq(mallctl("experimental.hooks.safety_check_abort", NULL, NULL,
|
||||
(void *)&abort_hook, sizeof(abort_hook)),
|
||||
0, "Unexpected mallctl failure setting abort hook");
|
||||
test_hooks_safety_check_abort = &fake_abort;
|
||||
|
||||
/*
|
||||
* Not owning operator new is only expected to happen on MinGW which
|
||||
@@ -57,6 +53,7 @@ TEST_BEGIN(test_failing_alloc) {
|
||||
}
|
||||
expect_ptr_null(ptr, "Allocation should have failed");
|
||||
expect_b_eq(fake_abort_called, true, "Abort hook not invoked");
|
||||
test_hooks_safety_check_abort = NULL;
|
||||
}
|
||||
TEST_END
|
||||
|
||||
|
||||
@@ -150,6 +150,7 @@ p_test_impl(bool do_malloc_init, bool do_reentrant, test_t *t, va_list ap) {
|
||||
/* Non-reentrant run. */
|
||||
reentrancy = non_reentrant;
|
||||
test_hooks_arena_new_hook = test_hooks_libc_hook = NULL;
|
||||
test_hooks_safety_check_abort = NULL;
|
||||
t();
|
||||
if (test_status > ret) {
|
||||
ret = test_status;
|
||||
@@ -158,6 +159,7 @@ p_test_impl(bool do_malloc_init, bool do_reentrant, test_t *t, va_list ap) {
|
||||
if (do_reentrant) {
|
||||
reentrancy = libc_reentrant;
|
||||
test_hooks_arena_new_hook = NULL;
|
||||
test_hooks_safety_check_abort = NULL;
|
||||
test_hooks_libc_hook = &libc_reentrancy_hook;
|
||||
t();
|
||||
if (test_status > ret) {
|
||||
@@ -166,6 +168,7 @@ p_test_impl(bool do_malloc_init, bool do_reentrant, test_t *t, va_list ap) {
|
||||
|
||||
reentrancy = arena_new_reentrant;
|
||||
test_hooks_libc_hook = NULL;
|
||||
test_hooks_safety_check_abort = NULL;
|
||||
test_hooks_arena_new_hook = &arena_new_reentrancy_hook;
|
||||
t();
|
||||
if (test_status > ret) {
|
||||
|
||||
@@ -2,15 +2,30 @@
|
||||
|
||||
case @abi@ in
|
||||
macho)
|
||||
export DYLD_FALLBACK_LIBRARY_PATH="@objroot@lib"
|
||||
export DYLD_FALLBACK_LIBRARY_PATH="@abs_objroot@lib"
|
||||
;;
|
||||
pecoff)
|
||||
export PATH="${PATH}:@objroot@lib"
|
||||
export PATH="@abs_objroot@lib:${PATH}"
|
||||
;;
|
||||
*)
|
||||
;;
|
||||
esac
|
||||
|
||||
prepare_test_exec() {
|
||||
case @abi@ in
|
||||
pecoff)
|
||||
test_dir=`dirname "$1"`
|
||||
for dll in @abs_objroot@lib/*.dll ; do
|
||||
if [ -f "${dll}" ] ; then
|
||||
cp -f "${dll}" "${test_dir}/"
|
||||
fi
|
||||
done
|
||||
;;
|
||||
*)
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
# Make a copy of the @JEMALLOC_CPREFIX@MALLOC_CONF passed in to this script, so
|
||||
# it can be repeatedly concatenated with per test settings.
|
||||
export MALLOC_CONF_ALL=${@JEMALLOC_CPREFIX@MALLOC_CONF}
|
||||
@@ -45,10 +60,12 @@ for t in $@; do
|
||||
enable_prof=@enable_prof@ \
|
||||
disable_large_size_classes=@disable_large_size_classes@ \
|
||||
. @srcroot@${t}.sh && \
|
||||
prepare_test_exec ${t}@exe@ && \
|
||||
export_malloc_conf && \
|
||||
$JEMALLOC_TEST_PREFIX ${t}@exe@ @abs_srcroot@ @abs_objroot@
|
||||
else
|
||||
export MALLOC_CONF= && \
|
||||
prepare_test_exec ${t}@exe@ && \
|
||||
export_malloc_conf && \
|
||||
$JEMALLOC_TEST_PREFIX ${t}@exe@ @abs_srcroot@ @abs_objroot@
|
||||
fi
|
||||
|
||||
@@ -12,14 +12,14 @@ fake_abort(const char *message) {
|
||||
|
||||
static void
|
||||
test_double_free_pre(void) {
|
||||
safety_check_set_abort(&fake_abort);
|
||||
test_hooks_safety_check_abort = &fake_abort;
|
||||
fake_abort_called = false;
|
||||
}
|
||||
|
||||
static void
|
||||
test_double_free_post(void) {
|
||||
expect_b_eq(fake_abort_called, true, "Double-free check didn't fire.");
|
||||
safety_check_set_abort(NULL);
|
||||
test_hooks_safety_check_abort = NULL;
|
||||
}
|
||||
|
||||
static bool
|
||||
|
||||
@@ -11,8 +11,6 @@
|
||||
"Output content touched when given invalid arguments"); \
|
||||
} while (0)
|
||||
|
||||
#define TEST_UTIL_QUERY_EINVAL(a, b, c, d, why_inval) \
|
||||
TEST_UTIL_EINVAL("query", a, b, c, d, why_inval)
|
||||
#define TEST_UTIL_BATCH_EINVAL(a, b, c, d, why_inval) \
|
||||
TEST_UTIL_EINVAL("batch_query", a, b, c, d, why_inval)
|
||||
|
||||
@@ -30,139 +28,6 @@
|
||||
|
||||
#define TEST_MAX_SIZE (1 << 20)
|
||||
|
||||
TEST_BEGIN(test_query) {
|
||||
size_t sz;
|
||||
/*
|
||||
* Select some sizes that can span both small and large sizes, and are
|
||||
* numerically unrelated to any size boundaries.
|
||||
*/
|
||||
for (sz = 7; sz <= TEST_MAX_SIZE && sz <= SC_LARGE_MAXCLASS;
|
||||
sz += (sz <= SC_SMALL_MAXCLASS ? 1009 : 99989)) {
|
||||
void *p = mallocx(sz, 0);
|
||||
void **in = &p;
|
||||
size_t in_sz = sizeof(const void *);
|
||||
size_t out_sz = sizeof(void *) + sizeof(size_t) * 5;
|
||||
void *out = mallocx(out_sz, 0);
|
||||
void *out_ref = mallocx(out_sz, 0);
|
||||
size_t out_sz_ref = out_sz;
|
||||
|
||||
assert_ptr_not_null(p, "test pointer allocation failed");
|
||||
assert_ptr_not_null(out, "test output allocation failed");
|
||||
assert_ptr_not_null(
|
||||
out_ref, "test reference output allocation failed");
|
||||
|
||||
#define SLABCUR_READ(out) (*(void **)out)
|
||||
#define COUNTS(out) ((size_t *)((void **)out + 1))
|
||||
#define NFREE_READ(out) COUNTS(out)[0]
|
||||
#define NREGS_READ(out) COUNTS(out)[1]
|
||||
#define SIZE_READ(out) COUNTS(out)[2]
|
||||
#define BIN_NFREE_READ(out) COUNTS(out)[3]
|
||||
#define BIN_NREGS_READ(out) COUNTS(out)[4]
|
||||
|
||||
SLABCUR_READ(out) = NULL;
|
||||
NFREE_READ(out) = NREGS_READ(out) = SIZE_READ(out) = -1;
|
||||
BIN_NFREE_READ(out) = BIN_NREGS_READ(out) = -1;
|
||||
memcpy(out_ref, out, out_sz);
|
||||
|
||||
/* Test invalid argument(s) errors */
|
||||
TEST_UTIL_QUERY_EINVAL(NULL, &out_sz, in, in_sz, "old is NULL");
|
||||
TEST_UTIL_QUERY_EINVAL(out, NULL, in, in_sz, "oldlenp is NULL");
|
||||
TEST_UTIL_QUERY_EINVAL(
|
||||
out, &out_sz, NULL, in_sz, "newp is NULL");
|
||||
TEST_UTIL_QUERY_EINVAL(out, &out_sz, in, 0, "newlen is zero");
|
||||
in_sz -= 1;
|
||||
TEST_UTIL_QUERY_EINVAL(
|
||||
out, &out_sz, in, in_sz, "invalid newlen");
|
||||
in_sz += 1;
|
||||
out_sz_ref = out_sz -= 2 * sizeof(size_t);
|
||||
TEST_UTIL_QUERY_EINVAL(
|
||||
out, &out_sz, in, in_sz, "invalid *oldlenp");
|
||||
out_sz_ref = out_sz += 2 * sizeof(size_t);
|
||||
|
||||
/* Examine output for valid call */
|
||||
TEST_UTIL_VALID("query");
|
||||
expect_zu_le(sz, SIZE_READ(out),
|
||||
"Extent size should be at least allocation size");
|
||||
expect_zu_eq(SIZE_READ(out) & (PAGE - 1), 0,
|
||||
"Extent size should be a multiple of page size");
|
||||
|
||||
/*
|
||||
* We don't do much bin checking if prof is on, since profiling
|
||||
* can produce extents that are for small size classes but not
|
||||
* slabs, which interferes with things like region counts.
|
||||
*/
|
||||
if (!opt_prof && sz <= SC_SMALL_MAXCLASS) {
|
||||
expect_zu_le(NFREE_READ(out), NREGS_READ(out),
|
||||
"Extent free count exceeded region count");
|
||||
expect_zu_le(NREGS_READ(out), SIZE_READ(out),
|
||||
"Extent region count exceeded size");
|
||||
expect_zu_ne(NREGS_READ(out), 0,
|
||||
"Extent region count must be positive");
|
||||
expect_true(NFREE_READ(out) == 0
|
||||
|| (SLABCUR_READ(out) != NULL
|
||||
&& SLABCUR_READ(out) <= p),
|
||||
"Allocation should follow first fit principle");
|
||||
|
||||
if (config_stats) {
|
||||
expect_zu_le(BIN_NFREE_READ(out),
|
||||
BIN_NREGS_READ(out),
|
||||
"Bin free count exceeded region count");
|
||||
expect_zu_ne(BIN_NREGS_READ(out), 0,
|
||||
"Bin region count must be positive");
|
||||
expect_zu_le(NFREE_READ(out),
|
||||
BIN_NFREE_READ(out),
|
||||
"Extent free count exceeded bin free count");
|
||||
expect_zu_le(NREGS_READ(out),
|
||||
BIN_NREGS_READ(out),
|
||||
"Extent region count exceeded "
|
||||
"bin region count");
|
||||
expect_zu_eq(
|
||||
BIN_NREGS_READ(out) % NREGS_READ(out), 0,
|
||||
"Bin region count isn't a multiple of "
|
||||
"extent region count");
|
||||
expect_zu_le(
|
||||
BIN_NFREE_READ(out) - NFREE_READ(out),
|
||||
BIN_NREGS_READ(out) - NREGS_READ(out),
|
||||
"Free count in other extents in the bin "
|
||||
"exceeded region count in other extents "
|
||||
"in the bin");
|
||||
expect_zu_le(NREGS_READ(out) - NFREE_READ(out),
|
||||
BIN_NREGS_READ(out) - BIN_NFREE_READ(out),
|
||||
"Extent utilized count exceeded "
|
||||
"bin utilized count");
|
||||
}
|
||||
} else if (sz > SC_SMALL_MAXCLASS) {
|
||||
expect_zu_eq(NFREE_READ(out), 0,
|
||||
"Extent free count should be zero");
|
||||
expect_zu_eq(NREGS_READ(out), 1,
|
||||
"Extent region count should be one");
|
||||
expect_ptr_null(SLABCUR_READ(out),
|
||||
"Current slab must be null for large size classes");
|
||||
if (config_stats) {
|
||||
expect_zu_eq(BIN_NFREE_READ(out), 0,
|
||||
"Bin free count must be zero for "
|
||||
"large sizes");
|
||||
expect_zu_eq(BIN_NREGS_READ(out), 0,
|
||||
"Bin region count must be zero for "
|
||||
"large sizes");
|
||||
}
|
||||
}
|
||||
|
||||
#undef BIN_NREGS_READ
|
||||
#undef BIN_NFREE_READ
|
||||
#undef SIZE_READ
|
||||
#undef NREGS_READ
|
||||
#undef NFREE_READ
|
||||
#undef COUNTS
|
||||
#undef SLABCUR_READ
|
||||
|
||||
free(out_ref);
|
||||
free(out);
|
||||
free(p);
|
||||
}
|
||||
}
|
||||
TEST_END
|
||||
|
||||
TEST_BEGIN(test_batch) {
|
||||
size_t sz;
|
||||
/*
|
||||
@@ -217,10 +82,7 @@ TEST_BEGIN(test_batch) {
|
||||
"Extent size should be at least allocation size");
|
||||
expect_zu_eq(SIZE_READ(out, 0) & (PAGE - 1), 0,
|
||||
"Extent size should be a multiple of page size");
|
||||
/*
|
||||
* See the corresponding comment in test_query; profiling breaks
|
||||
* our slab count expectations.
|
||||
*/
|
||||
/* Profiling breaks our slab count expectations. */
|
||||
if (sz <= SC_SMALL_MAXCLASS && !opt_prof) {
|
||||
expect_zu_le(NFREE_READ(out, 0), NREGS_READ(out, 0),
|
||||
"Extent free count exceeded region count");
|
||||
@@ -270,5 +132,5 @@ int
|
||||
main(void) {
|
||||
assert_zu_lt(SC_SMALL_MAXCLASS + 100000, TEST_MAX_SIZE,
|
||||
"Test case cannot cover large classes");
|
||||
return test(test_query, test_batch);
|
||||
return test(test_batch);
|
||||
}
|
||||
|
||||
@@ -39,13 +39,6 @@ TEST_BEGIN(test_mallctl_global_var) {
|
||||
expect_str_eq(mc, malloc_conf,
|
||||
"Unexpected value for the global variable "
|
||||
"malloc_conf");
|
||||
|
||||
expect_d_eq(mallctl("opt.malloc_conf.global_var_2_conf_harder",
|
||||
(void *)&mc, &sz, NULL, 0),
|
||||
0, "Unexpected mallctl() failure");
|
||||
expect_str_eq(mc, malloc_conf_2_conf_harder,
|
||||
"Unexpected value for the "
|
||||
"global variable malloc_conf_2_conf_harder");
|
||||
}
|
||||
TEST_END
|
||||
|
||||
|
||||
@@ -334,9 +334,50 @@ TEST_BEGIN(test_prof_sample_hooks) {
|
||||
}
|
||||
TEST_END
|
||||
|
||||
TEST_BEGIN(test_prof_hook_noop) {
|
||||
test_skip_if(!config_prof);
|
||||
|
||||
const char *hooks[] = {"experimental.hooks.prof_backtrace",
|
||||
"experimental.hooks.prof_dump", "experimental.hooks.prof_sample",
|
||||
"experimental.hooks.prof_sample_free"};
|
||||
|
||||
/* Passing NULL for both oldp and newp must always return EINVAL. */
|
||||
for (unsigned i = 0; i < sizeof(hooks) / sizeof(hooks[0]); i++) {
|
||||
expect_d_eq(mallctl(hooks[i], NULL, NULL, NULL, 0), EINVAL,
|
||||
"Unexpected noop hook mallctl result");
|
||||
}
|
||||
|
||||
/*
|
||||
* Passing a pointer to a NULL function pointer:
|
||||
* prof_backtrace requires a non-NULL hook -> EINVAL (unchanged)
|
||||
* prof_dump / prof_sample / prof_sample_free allow unsetting -> 0
|
||||
*
|
||||
* For the latter, save and restore the prior value so this test
|
||||
* doesn't leak NULL hook state to any future test.
|
||||
*/
|
||||
prof_backtrace_hook_t bt_null = NULL;
|
||||
expect_d_eq(mallctl(hooks[0], NULL, NULL, &bt_null, sizeof(bt_null)),
|
||||
EINVAL, "Incorrectly allowed NULL backtrace hook");
|
||||
|
||||
for (unsigned i = 1; i < sizeof(hooks) / sizeof(hooks[0]); i++) {
|
||||
void *saved = NULL;
|
||||
size_t saved_sz = sizeof(saved);
|
||||
void *new_hook = NULL;
|
||||
/* Read current value into `saved`, write NULL in same call. */
|
||||
expect_d_eq(mallctl(hooks[i], &saved, &saved_sz,
|
||||
&new_hook, sizeof(new_hook)),
|
||||
0, "Unexpected null-hook mallctl result");
|
||||
/* Restore. */
|
||||
expect_d_eq(mallctl(hooks[i], NULL, NULL,
|
||||
&saved, sizeof(saved)),
|
||||
0, "Failed to restore hook");
|
||||
}
|
||||
}
|
||||
TEST_END
|
||||
|
||||
int
|
||||
main(void) {
|
||||
return test(test_prof_backtrace_hook_replace,
|
||||
test_prof_backtrace_hook_augment, test_prof_dump_hook,
|
||||
test_prof_sample_hooks);
|
||||
test_prof_sample_hooks, test_prof_hook_noop);
|
||||
}
|
||||
|
||||
@@ -25,12 +25,12 @@ TEST_BEGIN(test_malloc_free_overflow) {
|
||||
test_skip_if(!config_prof);
|
||||
test_skip_if(!config_opt_safety_checks);
|
||||
|
||||
safety_check_set_abort(&fake_abort);
|
||||
test_hooks_safety_check_abort = &fake_abort;
|
||||
/* Buffer overflow! */
|
||||
char *ptr = malloc(128);
|
||||
buffer_overflow_write(ptr, 128);
|
||||
free(ptr);
|
||||
safety_check_set_abort(NULL);
|
||||
test_hooks_safety_check_abort = NULL;
|
||||
|
||||
expect_b_eq(fake_abort_called, true, "Redzone check didn't fire.");
|
||||
fake_abort_called = false;
|
||||
@@ -41,12 +41,12 @@ TEST_BEGIN(test_mallocx_dallocx_overflow) {
|
||||
test_skip_if(!config_prof);
|
||||
test_skip_if(!config_opt_safety_checks);
|
||||
|
||||
safety_check_set_abort(&fake_abort);
|
||||
test_hooks_safety_check_abort = &fake_abort;
|
||||
/* Buffer overflow! */
|
||||
char *ptr = mallocx(128, 0);
|
||||
buffer_overflow_write(ptr, 128);
|
||||
dallocx(ptr, 0);
|
||||
safety_check_set_abort(NULL);
|
||||
test_hooks_safety_check_abort = NULL;
|
||||
|
||||
expect_b_eq(fake_abort_called, true, "Redzone check didn't fire.");
|
||||
fake_abort_called = false;
|
||||
@@ -57,12 +57,12 @@ TEST_BEGIN(test_malloc_sdallocx_overflow) {
|
||||
test_skip_if(!config_prof);
|
||||
test_skip_if(!config_opt_safety_checks);
|
||||
|
||||
safety_check_set_abort(&fake_abort);
|
||||
test_hooks_safety_check_abort = &fake_abort;
|
||||
/* Buffer overflow! */
|
||||
char *ptr = malloc(128);
|
||||
buffer_overflow_write(ptr, 128);
|
||||
sdallocx(ptr, 128, 0);
|
||||
safety_check_set_abort(NULL);
|
||||
test_hooks_safety_check_abort = NULL;
|
||||
|
||||
expect_b_eq(fake_abort_called, true, "Redzone check didn't fire.");
|
||||
fake_abort_called = false;
|
||||
@@ -73,12 +73,12 @@ TEST_BEGIN(test_realloc_overflow) {
|
||||
test_skip_if(!config_prof);
|
||||
test_skip_if(!config_opt_safety_checks);
|
||||
|
||||
safety_check_set_abort(&fake_abort);
|
||||
test_hooks_safety_check_abort = &fake_abort;
|
||||
/* Buffer overflow! */
|
||||
char *ptr = malloc(128);
|
||||
buffer_overflow_write(ptr, 128);
|
||||
ptr = realloc(ptr, 129);
|
||||
safety_check_set_abort(NULL);
|
||||
test_hooks_safety_check_abort = NULL;
|
||||
free(ptr);
|
||||
|
||||
expect_b_eq(fake_abort_called, true, "Redzone check didn't fire.");
|
||||
@@ -90,12 +90,12 @@ TEST_BEGIN(test_rallocx_overflow) {
|
||||
test_skip_if(!config_prof);
|
||||
test_skip_if(!config_opt_safety_checks);
|
||||
|
||||
safety_check_set_abort(&fake_abort);
|
||||
test_hooks_safety_check_abort = &fake_abort;
|
||||
/* Buffer overflow! */
|
||||
char *ptr = malloc(128);
|
||||
buffer_overflow_write(ptr, 128);
|
||||
ptr = rallocx(ptr, 129, 0);
|
||||
safety_check_set_abort(NULL);
|
||||
test_hooks_safety_check_abort = NULL;
|
||||
free(ptr);
|
||||
|
||||
expect_b_eq(fake_abort_called, true, "Redzone check didn't fire.");
|
||||
@@ -107,7 +107,7 @@ TEST_BEGIN(test_xallocx_overflow) {
|
||||
test_skip_if(!config_prof);
|
||||
test_skip_if(!config_opt_safety_checks);
|
||||
|
||||
safety_check_set_abort(&fake_abort);
|
||||
test_hooks_safety_check_abort = &fake_abort;
|
||||
/* Buffer overflow! */
|
||||
char *ptr = malloc(128);
|
||||
buffer_overflow_write(ptr, 128);
|
||||
@@ -116,7 +116,7 @@ TEST_BEGIN(test_xallocx_overflow) {
|
||||
free(ptr);
|
||||
expect_b_eq(fake_abort_called, true, "Redzone check didn't fire.");
|
||||
fake_abort_called = false;
|
||||
safety_check_set_abort(NULL);
|
||||
test_hooks_safety_check_abort = NULL;
|
||||
}
|
||||
TEST_END
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@ fake_abort(const char *message) {
|
||||
|
||||
static void *
|
||||
test_invalid_size_pre(size_t sz) {
|
||||
safety_check_set_abort(&fake_abort);
|
||||
test_hooks_safety_check_abort = &fake_abort;
|
||||
|
||||
fake_abort_called = false;
|
||||
void *ptr = malloc(sz);
|
||||
@@ -29,7 +29,7 @@ test_invalid_size_pre(size_t sz) {
|
||||
static void
|
||||
test_invalid_size_post(void) {
|
||||
expect_true(fake_abort_called, "Safety check didn't fire");
|
||||
safety_check_set_abort(NULL);
|
||||
test_hooks_safety_check_abort = NULL;
|
||||
}
|
||||
|
||||
TEST_BEGIN(test_invalid_size_sdallocx) {
|
||||
|
||||
@@ -19,7 +19,7 @@ fake_abort(const char *message) {
|
||||
|
||||
static void
|
||||
test_write_after_free_pre(void) {
|
||||
safety_check_set_abort(&fake_abort);
|
||||
test_hooks_safety_check_abort = &fake_abort;
|
||||
fake_abort_called = false;
|
||||
}
|
||||
|
||||
@@ -28,7 +28,7 @@ test_write_after_free_post(void) {
|
||||
assert_d_eq(mallctl("thread.tcache.flush", NULL, NULL, NULL, 0), 0,
|
||||
"Unexpected tcache flush failure");
|
||||
expect_true(fake_abort_called, "Use-after-free check didn't fire.");
|
||||
safety_check_set_abort(NULL);
|
||||
test_hooks_safety_check_abort = NULL;
|
||||
}
|
||||
|
||||
static bool
|
||||
|
||||
@@ -12,7 +12,7 @@ set_abort_called(const char *message) {
|
||||
|
||||
TEST_BEGIN(test_realloc_abort) {
|
||||
abort_called = false;
|
||||
safety_check_set_abort(&set_abort_called);
|
||||
test_hooks_safety_check_abort = &set_abort_called;
|
||||
void *ptr = mallocx(42, 0);
|
||||
expect_ptr_not_null(ptr, "Unexpected mallocx error");
|
||||
ptr = realloc(ptr, 0);
|
||||
|
||||
Reference in New Issue
Block a user