Implement arena.<i>.destroy .
Add MALLCTL_ARENAS_DESTROYED for accessing destroyed arena stats as an analogue to MALLCTL_ARENAS_ALL. This resolves #382.
This commit is contained in:
parent
3f291d59ad
commit
edf1bafb2b
@ -195,6 +195,10 @@ TESTS_UNIT := \
|
||||
$(srcroot)test/unit/util.c \
|
||||
$(srcroot)test/unit/witness.c \
|
||||
$(srcroot)test/unit/zero.c
|
||||
ifeq (@enable_prof@, 1)
|
||||
TESTS_UNIT += \
|
||||
$(srcroot)test/unit/arena_reset_prof.c
|
||||
endif
|
||||
TESTS_INTEGRATION := $(srcroot)test/integration/aligned_alloc.c \
|
||||
$(srcroot)test/integration/allocated.c \
|
||||
$(srcroot)test/integration/extent.c \
|
||||
|
@ -426,13 +426,14 @@ for (i = 0; i < nbins; i++) {
|
||||
<function>mallctl*()</function> functions internally, so inconsistent
|
||||
statistics can be reported if multiple threads use these functions
|
||||
simultaneously. If <option>--enable-stats</option> is specified during
|
||||
configuration, <quote>m</quote> and <quote>a</quote> can be specified to
|
||||
omit merged arena and per arena statistics, respectively; <quote>b</quote>
|
||||
and <quote>l</quote> can be specified to omit per size class statistics
|
||||
for bins and large objects, respectively. Unrecognized characters are
|
||||
silently ignored. Note that thread caching may prevent some statistics
|
||||
from being completely up to date, since extra locking would be required to
|
||||
merge counters that track thread cache operations.</para>
|
||||
configuration, <quote>m</quote>, <quote>d</quote>, and <quote>a</quote>
|
||||
can be specified to omit merged arena, destroyed merged arena, and per
|
||||
arena statistics, respectively; <quote>b</quote> and <quote>l</quote> can
|
||||
be specified to omit per size class statistics for bins and large objects,
|
||||
respectively. Unrecognized characters are silently ignored. Note that
|
||||
thread caching may prevent some statistics from being completely up to
|
||||
date, since extra locking would be required to merge counters that track
|
||||
thread cache operations.</para>
|
||||
|
||||
<para>The <function>malloc_usable_size()</function> function
|
||||
returns the usable size of the allocation pointed to by
|
||||
@ -687,18 +688,21 @@ for (i = 0; i < nbins; i++) {
|
||||
<refsect1 id="mallctl_namespace">
|
||||
<title>MALLCTL NAMESPACE</title>
|
||||
<para>The following names are defined in the namespace accessible via the
|
||||
<function>mallctl*()</function> functions. Value types are
|
||||
specified in parentheses, their readable/writable statuses are encoded as
|
||||
<function>mallctl*()</function> functions. Value types are specified in
|
||||
parentheses, their readable/writable statuses are encoded as
|
||||
<literal>rw</literal>, <literal>r-</literal>, <literal>-w</literal>, or
|
||||
<literal>--</literal>, and required build configuration flags follow, if
|
||||
any. A name element encoded as <literal><i></literal> or
|
||||
<literal><j></literal> indicates an integer component, where the
|
||||
integer varies from 0 to some upper value that must be determined via
|
||||
introspection. In the case of <mallctl>stats.arenas.<i>.*</mallctl>
|
||||
and <mallctl>arena.<i>.{purge,decay,dss}</mallctl>,
|
||||
and <mallctl>arena.<i>.{initialized,purge,decay,dss}</mallctl>,
|
||||
<literal><i></literal> equal to
|
||||
<constant>MALLCTL_ARENAS_ALL</constant> can be used to operate on all arenas
|
||||
or access the summation of statistics from all arenas. This constant can be
|
||||
or access the summation of statistics from all arenas; similarly
|
||||
<literal><i></literal> equal to
|
||||
<constant>MALLCTL_ARENAS_DESTROYED</constant> can be used to access the
|
||||
summation of statistics from all destroyed arenas. These constants can be
|
||||
utilized either via <function>mallctlnametomib()</function> followed by
|
||||
<function>mallctlbymib()</function>, or via code such as the following:
|
||||
<programlisting language="C"><![CDATA[
|
||||
@ -707,9 +711,9 @@ for (i = 0; i < nbins; i++) {
|
||||
|
||||
mallctl("arena." STRINGIFY(MALLCTL_ARENAS_ALL) ".decay",
|
||||
NULL, NULL, NULL, 0);]]></programlisting>
|
||||
Take special note of the
|
||||
<link linkend="epoch"><mallctl>epoch</mallctl></link> mallctl, which
|
||||
controls refreshing of cached dynamic statistics.</para>
|
||||
Take special note of the <link
|
||||
linkend="epoch"><mallctl>epoch</mallctl></link> mallctl, which controls
|
||||
refreshing of cached dynamic statistics.</para>
|
||||
|
||||
<variablelist>
|
||||
<varlistentry id="version">
|
||||
@ -1478,6 +1482,25 @@ malloc_conf = "xmalloc:true";]]></programlisting>
|
||||
beforehand.</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry id="arena.i.destroy">
|
||||
<term>
|
||||
<mallctl>arena.<i>.destroy</mallctl>
|
||||
(<type>void</type>)
|
||||
<literal>--</literal>
|
||||
</term>
|
||||
<listitem><para>Destroy the arena. Discard all of the arena's extant
|
||||
allocations using the same mechanism as for <link
|
||||
linkend="arena.i.reset"><mallctl>arena.<i>.reset</mallctl></link>
|
||||
(with all the same constraints and side effects), merge the arena stats
|
||||
into those accessible at arena index
|
||||
<constant>MALLCTL_ARENAS_DESTROYED</constant>, and then completely
|
||||
discard all metadata associated with the arena. Future calls to <link
|
||||
linkend="arenas.create"><mallctl>arenas.create</mallctl></link> may
|
||||
recycle the arena index. Destruction will fail if any threads are
|
||||
currently associated with the arena as a result of calls to <link
|
||||
linkend="thread.arena"><mallctl>thread.arena</mallctl></link>.</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry id="arena.i.dss">
|
||||
<term>
|
||||
<mallctl>arena.<i>.dss</mallctl>
|
||||
|
@ -290,6 +290,7 @@ bool arena_decay_time_set(tsdn_t *tsdn, arena_t *arena, ssize_t decay_time);
|
||||
void arena_purge(tsdn_t *tsdn, arena_t *arena, bool all);
|
||||
void arena_maybe_purge(tsdn_t *tsdn, arena_t *arena);
|
||||
void arena_reset(tsd_t *tsd, arena_t *arena);
|
||||
void arena_destroy(tsd_t *tsd, arena_t *arena);
|
||||
void arena_tcache_fill_small(tsdn_t *tsdn, arena_t *arena,
|
||||
tcache_bin_t *tbin, szind_t binind, uint64_t prof_accumbytes);
|
||||
void arena_alloc_junk_small(void *ptr, const arena_bin_info_t *bin_info,
|
||||
|
@ -32,7 +32,10 @@ struct ctl_indexed_node_s {
|
||||
};
|
||||
|
||||
struct ctl_arena_stats_s {
|
||||
unsigned arena_ind;
|
||||
bool initialized;
|
||||
ql_elm(ctl_arena_stats_t) destroyed_link;
|
||||
|
||||
unsigned nthreads;
|
||||
const char *dss;
|
||||
ssize_t decay_time;
|
||||
@ -62,7 +65,14 @@ struct ctl_stats_s {
|
||||
size_t mapped;
|
||||
size_t retained;
|
||||
unsigned narenas;
|
||||
ctl_arena_stats_t *arenas[1 << MALLOCX_ARENA_BITS];
|
||||
ql_head(ctl_arena_stats_t) destroyed;
|
||||
/*
|
||||
* Element 0 contains merged stats for extant arenas (accessed via
|
||||
* MALLCTL_ARENAS_ALL), element 1 contains merged stats for destroyed
|
||||
* arenas (accessed via MALLCTL_ARENAS_DESTROYED), and the remaining
|
||||
* MALLOCX_ARENA_MAX+1 elements correspond to arenas.
|
||||
*/
|
||||
ctl_arena_stats_t *arenas[MALLOCX_ARENA_MAX + 3];
|
||||
};
|
||||
|
||||
#endif /* JEMALLOC_H_STRUCTS */
|
||||
|
@ -125,6 +125,8 @@ extent_t *extent_alloc_wrapper(tsdn_t *tsdn, arena_t *arena,
|
||||
void extent_dalloc_gap(tsdn_t *tsdn, arena_t *arena, extent_t *extent);
|
||||
void extent_dalloc_cache(tsdn_t *tsdn, arena_t *arena,
|
||||
extent_hooks_t **r_extent_hooks, extent_t *extent);
|
||||
bool extent_dalloc_wrapper_try(tsdn_t *tsdn, arena_t *arena,
|
||||
extent_hooks_t **r_extent_hooks, extent_t *extent);
|
||||
void extent_dalloc_wrapper(tsdn_t *tsdn, arena_t *arena,
|
||||
extent_hooks_t **r_extent_hooks, extent_t *extent);
|
||||
bool extent_commit_wrapper(tsdn_t *tsdn, arena_t *arena,
|
||||
|
@ -215,6 +215,7 @@ typedef unsigned szind_t;
|
||||
#define MALLOCX_TCACHE_SHIFT 8
|
||||
#define MALLOCX_ARENA_MASK \
|
||||
(((1 << MALLOCX_ARENA_BITS) - 1) << MALLOCX_ARENA_SHIFT)
|
||||
/* NB: Arena index bias decreases the maximum number of arenas by 1. */
|
||||
#define MALLOCX_ARENA_MAX ((1 << MALLOCX_ARENA_BITS) - 2)
|
||||
#define MALLOCX_TCACHE_MASK \
|
||||
(((1 << MALLOCX_TCACHE_BITS) - 1) << MALLOCX_TCACHE_SHIFT)
|
||||
@ -470,6 +471,7 @@ void a0dalloc(void *ptr);
|
||||
void *bootstrap_malloc(size_t size);
|
||||
void *bootstrap_calloc(size_t num, size_t size);
|
||||
void bootstrap_free(void *ptr);
|
||||
void arena_set(unsigned ind, arena_t *arena);
|
||||
unsigned narenas_total_get(void);
|
||||
arena_t *arena_init(tsdn_t *tsdn, unsigned ind, extent_hooks_t *extent_hooks);
|
||||
arena_tdata_t *arena_tdata_get_hard(tsd_t *tsd, unsigned ind);
|
||||
|
@ -21,6 +21,7 @@ arena_decay_time_default_get
|
||||
arena_decay_time_default_set
|
||||
arena_decay_time_get
|
||||
arena_decay_time_set
|
||||
arena_destroy
|
||||
arena_dss_prec_get
|
||||
arena_dss_prec_set
|
||||
arena_extent_alloc_large
|
||||
@ -67,6 +68,7 @@ arena_ralloc_no_move
|
||||
arena_reset
|
||||
arena_salloc
|
||||
arena_sdalloc
|
||||
arena_set
|
||||
arena_slab_regind
|
||||
arena_stats_merge
|
||||
arena_tcache_fill_small
|
||||
@ -164,6 +166,7 @@ extent_dalloc_cache
|
||||
extent_dalloc_gap
|
||||
extent_dalloc_mmap
|
||||
extent_dalloc_wrapper
|
||||
extent_dalloc_wrapper_try
|
||||
extent_decommit_wrapper
|
||||
extent_dss_boot
|
||||
extent_dss_mergeable
|
||||
|
@ -44,6 +44,11 @@
|
||||
* 0);
|
||||
*/
|
||||
#define MALLCTL_ARENAS_ALL 4096
|
||||
/*
|
||||
* Use as arena index in "stats.arenas.<i>.*" mallctl interfaces to select
|
||||
* destroyed arenas.
|
||||
*/
|
||||
#define MALLCTL_ARENAS_DESTROYED 4097
|
||||
|
||||
#if defined(__cplusplus) && defined(JEMALLOC_USE_CXX_THROW)
|
||||
# define JEMALLOC_CXX_THROW throw()
|
||||
|
66
src/arena.c
66
src/arena.c
@ -903,6 +903,72 @@ arena_reset(tsd_t *tsd, arena_t *arena)
|
||||
malloc_mutex_unlock(tsd_tsdn(tsd), &arena->lock);
|
||||
}
|
||||
|
||||
static void
|
||||
arena_destroy_retained(tsdn_t *tsdn, arena_t *arena)
|
||||
{
|
||||
extent_hooks_t *extent_hooks = extent_hooks_get(arena);
|
||||
size_t i;
|
||||
|
||||
/*
|
||||
* Iterate over the retained extents and blindly attempt to deallocate
|
||||
* them. This gives the extent allocator underlying the extent hooks an
|
||||
* opportunity to unmap all retained memory without having to keep its
|
||||
* own metadata structures, but if deallocation fails, that is the
|
||||
* application's decision/problem. In practice, retained extents are
|
||||
* leaked here if !config_munmap unless the application provided custom
|
||||
* extent hooks, so best practice to either enable munmap (and avoid dss
|
||||
* for arenas to be destroyed), or provide custom extent hooks that
|
||||
* either unmap retained extents or track them for later use.
|
||||
*/
|
||||
for (i = 0; i < sizeof(arena->extents_retained)/sizeof(extent_heap_t);
|
||||
i++) {
|
||||
extent_heap_t *extents = &arena->extents_retained[i];
|
||||
extent_t *extent;
|
||||
|
||||
while ((extent = extent_heap_remove_first(extents)) != NULL) {
|
||||
extent_dalloc_wrapper_try(tsdn, arena, &extent_hooks,
|
||||
extent);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
arena_destroy(tsd_t *tsd, arena_t *arena)
|
||||
{
|
||||
|
||||
assert(base_ind_get(arena->base) >= narenas_auto);
|
||||
assert(arena_nthreads_get(arena, false) == 0);
|
||||
assert(arena_nthreads_get(arena, true) == 0);
|
||||
|
||||
/*
|
||||
* No allocations have occurred since arena_reset() was called.
|
||||
* Furthermore, the caller (arena_i_destroy_ctl()) purged all cached
|
||||
* extents, so only retained extents may remain.
|
||||
*/
|
||||
assert(arena->ndirty == 0);
|
||||
|
||||
/* Attempt to deallocate retained memory. */
|
||||
arena_destroy_retained(tsd_tsdn(tsd), arena);
|
||||
|
||||
/*
|
||||
* Remove the arena pointer from the arenas array. We rely on the fact
|
||||
* that there is no way for the application to get a dirty read from the
|
||||
* arenas array unless there is an inherent race in the application
|
||||
* involving access of an arena being concurrently destroyed. The
|
||||
* application must synchronize knowledge of the arena's validity, so as
|
||||
* long as we use an atomic write to update the arenas array, the
|
||||
* application will get a clean read any time after it synchronizes
|
||||
* knowledge that the arena is no longer valid.
|
||||
*/
|
||||
arena_set(base_ind_get(arena->base), NULL);
|
||||
|
||||
/*
|
||||
* Destroy the base allocator, which manages all metadata ever mapped by
|
||||
* this arena.
|
||||
*/
|
||||
base_delete(arena->base);
|
||||
}
|
||||
|
||||
static extent_t *
|
||||
arena_slab_alloc_hard(tsdn_t *tsdn, arena_t *arena,
|
||||
extent_hooks_t **r_extent_hooks, const arena_bin_info_t *bin_info)
|
||||
|
263
src/ctl.c
263
src/ctl.c
@ -48,18 +48,6 @@ static int n##_ctl(tsd_t *tsd, const size_t *mib, size_t miblen, \
|
||||
static const ctl_named_node_t *n##_index(tsdn_t *tsdn, \
|
||||
const size_t *mib, size_t miblen, size_t i);
|
||||
|
||||
static void ctl_arena_clear(ctl_arena_stats_t *astats);
|
||||
static void ctl_arena_stats_amerge(tsdn_t *tsdn, ctl_arena_stats_t *cstats,
|
||||
arena_t *arena);
|
||||
static void ctl_arena_stats_smerge(ctl_arena_stats_t *sstats,
|
||||
ctl_arena_stats_t *astats);
|
||||
static void ctl_arena_refresh(tsdn_t *tsdn, arena_t *arena, unsigned i);
|
||||
static bool ctl_grow(tsdn_t *tsdn, extent_hooks_t *extent_hooks);
|
||||
static void ctl_refresh(tsdn_t *tsdn);
|
||||
static bool ctl_init(tsdn_t *tsdn);
|
||||
static int ctl_lookup(tsdn_t *tsdn, const char *name,
|
||||
ctl_node_t const **nodesp, size_t *mibp, size_t *depthp);
|
||||
|
||||
CTL_PROTO(version)
|
||||
CTL_PROTO(epoch)
|
||||
CTL_PROTO(thread_tcache_enabled)
|
||||
@ -113,6 +101,7 @@ CTL_PROTO(arena_i_initialized)
|
||||
CTL_PROTO(arena_i_purge)
|
||||
CTL_PROTO(arena_i_decay)
|
||||
CTL_PROTO(arena_i_reset)
|
||||
CTL_PROTO(arena_i_destroy)
|
||||
CTL_PROTO(arena_i_dss)
|
||||
CTL_PROTO(arena_i_decay_time)
|
||||
CTL_PROTO(arena_i_extent_hooks)
|
||||
@ -274,6 +263,7 @@ static const ctl_named_node_t arena_i_node[] = {
|
||||
{NAME("purge"), CTL(arena_i_purge)},
|
||||
{NAME("decay"), CTL(arena_i_decay)},
|
||||
{NAME("reset"), CTL(arena_i_reset)},
|
||||
{NAME("destroy"), CTL(arena_i_destroy)},
|
||||
{NAME("dss"), CTL(arena_i_dss)},
|
||||
{NAME("decay_time"), CTL(arena_i_decay_time)},
|
||||
{NAME("extent_hooks"), CTL(arena_i_extent_hooks)}
|
||||
@ -452,6 +442,9 @@ stats_arenas_i2a_impl(size_t i, bool compat, bool validate)
|
||||
case MALLCTL_ARENAS_ALL:
|
||||
a = 0;
|
||||
break;
|
||||
case MALLCTL_ARENAS_DESTROYED:
|
||||
a = 1;
|
||||
break;
|
||||
default:
|
||||
if (compat && i == ctl_stats->narenas) {
|
||||
/*
|
||||
@ -471,7 +464,7 @@ stats_arenas_i2a_impl(size_t i, bool compat, bool validate)
|
||||
*/
|
||||
assert(i < ctl_stats->narenas || (!validate && i ==
|
||||
ctl_stats->narenas));
|
||||
a = (unsigned)i + 1;
|
||||
a = (unsigned)i + 2;
|
||||
}
|
||||
break;
|
||||
}
|
||||
@ -479,6 +472,13 @@ stats_arenas_i2a_impl(size_t i, bool compat, bool validate)
|
||||
return (a);
|
||||
}
|
||||
|
||||
static unsigned
|
||||
stats_arenas_i2a(size_t i)
|
||||
{
|
||||
|
||||
return (stats_arenas_i2a_impl(i, true, false));
|
||||
}
|
||||
|
||||
static ctl_arena_stats_t *
|
||||
stats_arenas_i_impl(tsdn_t *tsdn, size_t i, bool compat, bool init)
|
||||
{
|
||||
@ -492,10 +492,13 @@ stats_arenas_i_impl(tsdn_t *tsdn, size_t i, bool compat, bool init)
|
||||
sizeof(ctl_arena_stats_t), QUANTUM);
|
||||
if (ret == NULL)
|
||||
return (NULL);
|
||||
ret->arena_ind = (unsigned)i;
|
||||
ctl_stats->arenas[stats_arenas_i2a_impl(i, compat, false)] =
|
||||
ret;
|
||||
}
|
||||
|
||||
assert(ret == NULL || stats_arenas_i2a(ret->arena_ind) ==
|
||||
stats_arenas_i2a(i));
|
||||
return (ret);
|
||||
}
|
||||
|
||||
@ -553,92 +556,130 @@ ctl_arena_stats_amerge(tsdn_t *tsdn, ctl_arena_stats_t *cstats, arena_t *arena)
|
||||
}
|
||||
|
||||
static void
|
||||
ctl_arena_stats_smerge(ctl_arena_stats_t *sstats, ctl_arena_stats_t *astats)
|
||||
ctl_arena_stats_sdmerge(ctl_arena_stats_t *sdstats, ctl_arena_stats_t *astats,
|
||||
bool destroyed)
|
||||
{
|
||||
unsigned i;
|
||||
|
||||
sstats->nthreads += astats->nthreads;
|
||||
sstats->pactive += astats->pactive;
|
||||
sstats->pdirty += astats->pdirty;
|
||||
if (!destroyed) {
|
||||
sdstats->nthreads += astats->nthreads;
|
||||
sdstats->pactive += astats->pactive;
|
||||
sdstats->pdirty += astats->pdirty;
|
||||
} else {
|
||||
assert(astats->nthreads == 0);
|
||||
assert(astats->pactive == 0);
|
||||
assert(astats->pdirty == 0);
|
||||
}
|
||||
|
||||
if (config_stats) {
|
||||
sstats->astats.mapped += astats->astats.mapped;
|
||||
sstats->astats.retained += astats->astats.retained;
|
||||
sstats->astats.npurge += astats->astats.npurge;
|
||||
sstats->astats.nmadvise += astats->astats.nmadvise;
|
||||
sstats->astats.purged += astats->astats.purged;
|
||||
if (!destroyed) {
|
||||
sdstats->astats.mapped += astats->astats.mapped;
|
||||
sdstats->astats.retained += astats->astats.retained;
|
||||
}
|
||||
sdstats->astats.npurge += astats->astats.npurge;
|
||||
sdstats->astats.nmadvise += astats->astats.nmadvise;
|
||||
sdstats->astats.purged += astats->astats.purged;
|
||||
|
||||
sstats->astats.base += astats->astats.base;
|
||||
sstats->astats.internal += astats->astats.internal;
|
||||
sstats->astats.resident += astats->astats.resident;
|
||||
if (!destroyed) {
|
||||
sdstats->astats.base += astats->astats.base;
|
||||
sdstats->astats.internal += astats->astats.internal;
|
||||
sdstats->astats.resident += astats->astats.resident;
|
||||
} else
|
||||
assert(astats->astats.internal == 0);
|
||||
|
||||
sstats->allocated_small += astats->allocated_small;
|
||||
sstats->nmalloc_small += astats->nmalloc_small;
|
||||
sstats->ndalloc_small += astats->ndalloc_small;
|
||||
sstats->nrequests_small += astats->nrequests_small;
|
||||
if (!destroyed)
|
||||
sdstats->allocated_small += astats->allocated_small;
|
||||
else
|
||||
assert(astats->allocated_small == 0);
|
||||
sdstats->nmalloc_small += astats->nmalloc_small;
|
||||
sdstats->ndalloc_small += astats->ndalloc_small;
|
||||
sdstats->nrequests_small += astats->nrequests_small;
|
||||
|
||||
sstats->astats.allocated_large +=
|
||||
if (!destroyed) {
|
||||
sdstats->astats.allocated_large +=
|
||||
astats->astats.allocated_large;
|
||||
sstats->astats.nmalloc_large += astats->astats.nmalloc_large;
|
||||
sstats->astats.ndalloc_large += astats->astats.ndalloc_large;
|
||||
sstats->astats.nrequests_large +=
|
||||
} else
|
||||
assert(astats->astats.allocated_large == 0);
|
||||
sdstats->astats.nmalloc_large += astats->astats.nmalloc_large;
|
||||
sdstats->astats.ndalloc_large += astats->astats.ndalloc_large;
|
||||
sdstats->astats.nrequests_large +=
|
||||
astats->astats.nrequests_large;
|
||||
|
||||
for (i = 0; i < NBINS; i++) {
|
||||
sstats->bstats[i].nmalloc += astats->bstats[i].nmalloc;
|
||||
sstats->bstats[i].ndalloc += astats->bstats[i].ndalloc;
|
||||
sstats->bstats[i].nrequests +=
|
||||
sdstats->bstats[i].nmalloc += astats->bstats[i].nmalloc;
|
||||
sdstats->bstats[i].ndalloc += astats->bstats[i].ndalloc;
|
||||
sdstats->bstats[i].nrequests +=
|
||||
astats->bstats[i].nrequests;
|
||||
sstats->bstats[i].curregs += astats->bstats[i].curregs;
|
||||
if (!destroyed) {
|
||||
sdstats->bstats[i].curregs +=
|
||||
astats->bstats[i].curregs;
|
||||
} else
|
||||
assert(astats->bstats[i].curregs == 0);
|
||||
if (config_tcache) {
|
||||
sstats->bstats[i].nfills +=
|
||||
sdstats->bstats[i].nfills +=
|
||||
astats->bstats[i].nfills;
|
||||
sstats->bstats[i].nflushes +=
|
||||
sdstats->bstats[i].nflushes +=
|
||||
astats->bstats[i].nflushes;
|
||||
}
|
||||
sstats->bstats[i].nslabs += astats->bstats[i].nslabs;
|
||||
sstats->bstats[i].reslabs += astats->bstats[i].reslabs;
|
||||
sstats->bstats[i].curslabs +=
|
||||
sdstats->bstats[i].nslabs += astats->bstats[i].nslabs;
|
||||
sdstats->bstats[i].reslabs += astats->bstats[i].reslabs;
|
||||
if (!destroyed) {
|
||||
sdstats->bstats[i].curslabs +=
|
||||
astats->bstats[i].curslabs;
|
||||
} else
|
||||
assert(astats->bstats[i].curslabs == 0);
|
||||
}
|
||||
|
||||
for (i = 0; i < NSIZES - NBINS; i++) {
|
||||
sstats->lstats[i].nmalloc += astats->lstats[i].nmalloc;
|
||||
sstats->lstats[i].ndalloc += astats->lstats[i].ndalloc;
|
||||
sstats->lstats[i].nrequests +=
|
||||
sdstats->lstats[i].nmalloc += astats->lstats[i].nmalloc;
|
||||
sdstats->lstats[i].ndalloc += astats->lstats[i].ndalloc;
|
||||
sdstats->lstats[i].nrequests +=
|
||||
astats->lstats[i].nrequests;
|
||||
sstats->lstats[i].curlextents +=
|
||||
if (!destroyed) {
|
||||
sdstats->lstats[i].curlextents +=
|
||||
astats->lstats[i].curlextents;
|
||||
} else
|
||||
assert(astats->lstats[i].curlextents == 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
ctl_arena_refresh(tsdn_t *tsdn, arena_t *arena, unsigned i)
|
||||
ctl_arena_refresh(tsdn_t *tsdn, arena_t *arena, ctl_arena_stats_t *sdstats,
|
||||
unsigned i, bool destroyed)
|
||||
{
|
||||
ctl_arena_stats_t *astats = stats_arenas_i(i);
|
||||
ctl_arena_stats_t *sstats = stats_arenas_i(MALLCTL_ARENAS_ALL);
|
||||
|
||||
ctl_arena_clear(astats);
|
||||
ctl_arena_stats_amerge(tsdn, astats, arena);
|
||||
/* Merge into sum stats as well. */
|
||||
ctl_arena_stats_smerge(sstats, astats);
|
||||
ctl_arena_stats_sdmerge(sdstats, astats, destroyed);
|
||||
}
|
||||
|
||||
static bool
|
||||
ctl_grow(tsdn_t *tsdn, extent_hooks_t *extent_hooks)
|
||||
static unsigned
|
||||
ctl_arena_init(tsdn_t *tsdn, extent_hooks_t *extent_hooks)
|
||||
{
|
||||
unsigned arena_ind;
|
||||
ctl_arena_stats_t *astats;
|
||||
|
||||
if ((astats = ql_last(&ctl_stats->destroyed, destroyed_link)) != NULL) {
|
||||
ql_remove(&ctl_stats->destroyed, astats, destroyed_link);
|
||||
arena_ind = astats->arena_ind;
|
||||
} else
|
||||
arena_ind = ctl_stats->narenas;
|
||||
|
||||
/* Trigger stats allocation. */
|
||||
if (stats_arenas_i_impl(tsdn, ctl_stats->narenas, false, true) == NULL)
|
||||
return (true);
|
||||
if (stats_arenas_i_impl(tsdn, arena_ind, false, true) == NULL)
|
||||
return (UINT_MAX);
|
||||
|
||||
/* Initialize new arena. */
|
||||
if (arena_init(tsdn, ctl_stats->narenas, extent_hooks) == NULL)
|
||||
return (true);
|
||||
if (arena_init(tsdn, arena_ind, extent_hooks) == NULL)
|
||||
return (UINT_MAX);
|
||||
|
||||
if (arena_ind == ctl_stats->narenas)
|
||||
ctl_stats->narenas++;
|
||||
|
||||
return (false);
|
||||
return (arena_ind);
|
||||
}
|
||||
|
||||
static void
|
||||
@ -663,7 +704,7 @@ ctl_refresh(tsdn_t *tsdn)
|
||||
|
||||
astats->initialized = initialized;
|
||||
if (initialized)
|
||||
ctl_arena_refresh(tsdn, tarenas[i], i);
|
||||
ctl_arena_refresh(tsdn, tarenas[i], sstats, i, false);
|
||||
}
|
||||
|
||||
if (config_stats) {
|
||||
@ -687,7 +728,7 @@ ctl_init(tsdn_t *tsdn)
|
||||
|
||||
malloc_mutex_lock(tsdn, &ctl_mtx);
|
||||
if (!ctl_initialized) {
|
||||
ctl_arena_stats_t *sstats;
|
||||
ctl_arena_stats_t *sstats, *dstats;
|
||||
unsigned i;
|
||||
|
||||
/*
|
||||
@ -715,6 +756,19 @@ ctl_init(tsdn_t *tsdn)
|
||||
}
|
||||
sstats->initialized = true;
|
||||
|
||||
if ((dstats = stats_arenas_i_impl(tsdn,
|
||||
MALLCTL_ARENAS_DESTROYED, false, true)) == NULL) {
|
||||
ret = true;
|
||||
goto label_return;
|
||||
}
|
||||
ctl_arena_clear(dstats);
|
||||
/*
|
||||
* Don't toggle stats for MALLCTL_ARENAS_DESTROYED to
|
||||
* initialized until an arena is actually destroyed, so that
|
||||
* arena.<i>.initialized can be used to query whether the stats
|
||||
* are relevant.
|
||||
*/
|
||||
|
||||
ctl_stats->narenas = narenas_total_get();
|
||||
for (i = 0; i < ctl_stats->narenas; i++) {
|
||||
if (stats_arenas_i_impl(tsdn, i, false, true) == NULL) {
|
||||
@ -723,7 +777,7 @@ ctl_init(tsdn_t *tsdn)
|
||||
}
|
||||
}
|
||||
|
||||
ctl_stats->epoch = 0;
|
||||
ql_new(&ctl_stats->destroyed);
|
||||
ctl_refresh(tsdn);
|
||||
ctl_initialized = true;
|
||||
}
|
||||
@ -1562,6 +1616,33 @@ label_return:
|
||||
return (ret);
|
||||
}
|
||||
|
||||
static int
|
||||
arena_i_reset_destroy_helper(tsd_t *tsd, const size_t *mib, size_t miblen,
|
||||
void *oldp, size_t *oldlenp, void *newp, size_t newlen, unsigned *arena_ind,
|
||||
arena_t **arena)
|
||||
{
|
||||
int ret;
|
||||
|
||||
READONLY();
|
||||
WRITEONLY();
|
||||
MIB_UNSIGNED(*arena_ind, 1);
|
||||
|
||||
if (*arena_ind < narenas_auto) {
|
||||
ret = EFAULT;
|
||||
goto label_return;
|
||||
}
|
||||
|
||||
*arena = arena_get(tsd_tsdn(tsd), *arena_ind, false);
|
||||
if (*arena == NULL) {
|
||||
ret = EFAULT;
|
||||
goto label_return;
|
||||
}
|
||||
|
||||
ret = 0;
|
||||
label_return:
|
||||
return (ret);
|
||||
}
|
||||
|
||||
static int
|
||||
arena_i_reset_ctl(tsd_t *tsd, const size_t *mib, size_t miblen, void *oldp,
|
||||
size_t *oldlenp, void *newp, size_t newlen)
|
||||
@ -1570,26 +1651,51 @@ arena_i_reset_ctl(tsd_t *tsd, const size_t *mib, size_t miblen, void *oldp,
|
||||
unsigned arena_ind;
|
||||
arena_t *arena;
|
||||
|
||||
READONLY();
|
||||
WRITEONLY();
|
||||
MIB_UNSIGNED(arena_ind, 1);
|
||||
ret = arena_i_reset_destroy_helper(tsd, mib, miblen, oldp, oldlenp,
|
||||
newp, newlen, &arena_ind, &arena);
|
||||
if (ret != 0)
|
||||
return (ret);
|
||||
|
||||
if (config_debug) {
|
||||
malloc_mutex_lock(tsd_tsdn(tsd), &ctl_mtx);
|
||||
assert(arena_ind < ctl_stats->narenas);
|
||||
malloc_mutex_unlock(tsd_tsdn(tsd), &ctl_mtx);
|
||||
arena_reset(tsd, arena);
|
||||
|
||||
return (ret);
|
||||
}
|
||||
assert(arena_ind >= opt_narenas);
|
||||
|
||||
arena = arena_get(tsd_tsdn(tsd), arena_ind, false);
|
||||
if (arena == NULL) {
|
||||
static int
|
||||
arena_i_destroy_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;
|
||||
arena_t *arena;
|
||||
ctl_arena_stats_t *dstats, *astats;
|
||||
|
||||
ret = arena_i_reset_destroy_helper(tsd, mib, miblen, oldp, oldlenp,
|
||||
newp, newlen, &arena_ind, &arena);
|
||||
if (ret != 0)
|
||||
goto label_return;
|
||||
|
||||
if (arena_nthreads_get(arena, false) != 0 || arena_nthreads_get(arena,
|
||||
true) != 0) {
|
||||
ret = EFAULT;
|
||||
goto label_return;
|
||||
}
|
||||
|
||||
/* Merge stats after resetting and purging arena. */
|
||||
arena_reset(tsd, arena);
|
||||
arena_purge(tsd_tsdn(tsd), arena, true);
|
||||
dstats = stats_arenas_i(MALLCTL_ARENAS_DESTROYED);
|
||||
dstats->initialized = true;
|
||||
ctl_arena_refresh(tsd_tsdn(tsd), arena, dstats, arena_ind, true);
|
||||
/* Destroy arena. */
|
||||
arena_destroy(tsd, arena);
|
||||
astats = stats_arenas_i(arena_ind);
|
||||
astats->initialized = false;
|
||||
/* Record arena index for later recycling via arenas.create. */
|
||||
ql_elm_new(astats, destroyed_link);
|
||||
ql_tail_insert(&ctl_stats->destroyed, astats, destroyed_link);
|
||||
|
||||
ret = 0;
|
||||
assert(ret == 0);
|
||||
label_return:
|
||||
return (ret);
|
||||
}
|
||||
@ -1733,10 +1839,17 @@ arena_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 (i > ctl_stats->narenas && i != MALLCTL_ARENAS_ALL) {
|
||||
switch (i) {
|
||||
case MALLCTL_ARENAS_ALL:
|
||||
case MALLCTL_ARENAS_DESTROYED:
|
||||
break;
|
||||
default:
|
||||
if (i > ctl_stats->narenas) {
|
||||
ret = NULL;
|
||||
goto label_return;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
ret = super_arena_i_node;
|
||||
label_return:
|
||||
@ -1828,18 +1941,18 @@ arenas_create_ctl(tsd_t *tsd, const size_t *mib, size_t miblen, void *oldp,
|
||||
{
|
||||
int ret;
|
||||
extent_hooks_t *extent_hooks;
|
||||
unsigned narenas;
|
||||
unsigned arena_ind;
|
||||
|
||||
malloc_mutex_lock(tsd_tsdn(tsd), &ctl_mtx);
|
||||
|
||||
extent_hooks = (extent_hooks_t *)&extent_hooks_default;
|
||||
WRITE(extent_hooks, extent_hooks_t *);
|
||||
if (ctl_grow(tsd_tsdn(tsd), extent_hooks)) {
|
||||
if ((arena_ind = ctl_arena_init(tsd_tsdn(tsd), extent_hooks)) ==
|
||||
UINT_MAX) {
|
||||
ret = EAGAIN;
|
||||
goto label_return;
|
||||
}
|
||||
narenas = ctl_stats->narenas - 1;
|
||||
READ(narenas, unsigned);
|
||||
READ(arena_ind, unsigned);
|
||||
|
||||
ret = 0;
|
||||
label_return:
|
||||
|
21
src/extent.c
21
src/extent.c
@ -1039,11 +1039,11 @@ extent_dalloc_default(extent_hooks_t *extent_hooks, void *addr, size_t size,
|
||||
return (extent_dalloc_default_impl(addr, size));
|
||||
}
|
||||
|
||||
void
|
||||
extent_dalloc_wrapper(tsdn_t *tsdn, arena_t *arena,
|
||||
bool
|
||||
extent_dalloc_wrapper_try(tsdn_t *tsdn, arena_t *arena,
|
||||
extent_hooks_t **r_extent_hooks, extent_t *extent)
|
||||
{
|
||||
bool err, zeroed;
|
||||
bool err;
|
||||
|
||||
assert(extent_base_get(extent) != NULL);
|
||||
assert(extent_size_get(extent) != 0);
|
||||
@ -1067,10 +1067,21 @@ extent_dalloc_wrapper(tsdn_t *tsdn, arena_t *arena,
|
||||
extent_committed_get(extent), arena_ind_get(arena)));
|
||||
}
|
||||
|
||||
if (!err) {
|
||||
if (!err)
|
||||
extent_dalloc(tsdn, arena, extent);
|
||||
return;
|
||||
|
||||
return (err);
|
||||
}
|
||||
|
||||
void
|
||||
extent_dalloc_wrapper(tsdn_t *tsdn, arena_t *arena,
|
||||
extent_hooks_t **r_extent_hooks, extent_t *extent)
|
||||
{
|
||||
bool zeroed;
|
||||
|
||||
if (!extent_dalloc_wrapper_try(tsdn, arena, r_extent_hooks, extent))
|
||||
return;
|
||||
|
||||
extent_reregister(tsdn, extent);
|
||||
/* Try to decommit; purge if that fails. */
|
||||
if (!extent_committed_get(extent))
|
||||
|
@ -375,7 +375,7 @@ bootstrap_free(void *ptr)
|
||||
a0idalloc(iealloc(NULL, ptr), ptr, false);
|
||||
}
|
||||
|
||||
static void
|
||||
void
|
||||
arena_set(unsigned ind, arena_t *arena)
|
||||
{
|
||||
|
||||
|
37
src/stats.c
37
src/stats.c
@ -772,7 +772,8 @@ stats_general_print(void (*write_cb)(void *, const char *), void *cbopaque,
|
||||
|
||||
static void
|
||||
stats_print_helper(void (*write_cb)(void *, const char *), void *cbopaque,
|
||||
bool json, bool merged, bool unmerged, bool bins, bool large)
|
||||
bool json, bool merged, bool destroyed, bool unmerged, bool bins,
|
||||
bool large)
|
||||
{
|
||||
size_t allocated, active, metadata, resident, mapped, retained;
|
||||
|
||||
@ -808,7 +809,7 @@ stats_print_helper(void (*write_cb)(void *, const char *), void *cbopaque,
|
||||
allocated, active, metadata, resident, mapped, retained);
|
||||
}
|
||||
|
||||
if (merged || unmerged) {
|
||||
if (merged || destroyed || unmerged) {
|
||||
unsigned narenas;
|
||||
|
||||
if (json) {
|
||||
@ -822,6 +823,7 @@ stats_print_helper(void (*write_cb)(void *, const char *), void *cbopaque,
|
||||
size_t miblen = sizeof(mib) / sizeof(size_t);
|
||||
size_t sz;
|
||||
VARIABLE_ARRAY(bool, initialized, narenas);
|
||||
bool destroyed_initialized;
|
||||
unsigned i, j, ninitialized;
|
||||
|
||||
xmallctlnametomib("arena.0.initialized", mib, &miblen);
|
||||
@ -833,6 +835,10 @@ stats_print_helper(void (*write_cb)(void *, const char *), void *cbopaque,
|
||||
if (initialized[i])
|
||||
ninitialized++;
|
||||
}
|
||||
mib[1] = MALLCTL_ARENAS_DESTROYED;
|
||||
sz = sizeof(bool);
|
||||
xmallctlbymib(mib, miblen, &destroyed_initialized, &sz,
|
||||
NULL, 0);
|
||||
|
||||
/* Merged stats. */
|
||||
if (merged && (ninitialized > 1 || !unmerged)) {
|
||||
@ -853,6 +859,25 @@ stats_print_helper(void (*write_cb)(void *, const char *), void *cbopaque,
|
||||
}
|
||||
}
|
||||
|
||||
/* Destroyed stats. */
|
||||
if (destroyed_initialized && destroyed) {
|
||||
/* Print destroyed arena stats. */
|
||||
if (json) {
|
||||
malloc_cprintf(write_cb, cbopaque,
|
||||
"\t\t\t\"destroyed\": {\n");
|
||||
} else {
|
||||
malloc_cprintf(write_cb, cbopaque,
|
||||
"\nDestroyed arenas stats:\n");
|
||||
}
|
||||
stats_arena_print(write_cb, cbopaque, json,
|
||||
MALLCTL_ARENAS_DESTROYED, bins, large);
|
||||
if (json) {
|
||||
malloc_cprintf(write_cb, cbopaque,
|
||||
"\t\t\t}%s\n", (ninitialized > 1) ?
|
||||
"," : "");
|
||||
}
|
||||
}
|
||||
|
||||
/* Unmerged stats. */
|
||||
for (i = j = 0; i < narenas; i++) {
|
||||
if (initialized[i]) {
|
||||
@ -895,6 +920,7 @@ stats_print(void (*write_cb)(void *, const char *), void *cbopaque,
|
||||
bool json = false;
|
||||
bool general = true;
|
||||
bool merged = config_stats;
|
||||
bool destroyed = config_stats;
|
||||
bool unmerged = config_stats;
|
||||
bool bins = true;
|
||||
bool large = true;
|
||||
@ -935,6 +961,9 @@ stats_print(void (*write_cb)(void *, const char *), void *cbopaque,
|
||||
case 'm':
|
||||
merged = false;
|
||||
break;
|
||||
case 'd':
|
||||
destroyed = false;
|
||||
break;
|
||||
case 'a':
|
||||
unmerged = false;
|
||||
break;
|
||||
@ -963,8 +992,8 @@ stats_print(void (*write_cb)(void *, const char *), void *cbopaque,
|
||||
stats_general_print(write_cb, cbopaque, json, more);
|
||||
}
|
||||
if (config_stats) {
|
||||
stats_print_helper(write_cb, cbopaque, json, merged, unmerged,
|
||||
bins, large);
|
||||
stats_print_helper(write_cb, cbopaque, json, merged, destroyed,
|
||||
unmerged, bins, large);
|
||||
}
|
||||
|
||||
if (json) {
|
||||
|
@ -1,9 +1,9 @@
|
||||
#ifndef ARENA_RESET_PROF_C_
|
||||
#include "test/jemalloc_test.h"
|
||||
|
||||
#ifdef JEMALLOC_PROF
|
||||
const char *malloc_conf = "prof:true,lg_prof_sample:0";
|
||||
#endif
|
||||
|
||||
#include "test/extent_hooks.h"
|
||||
|
||||
static unsigned
|
||||
get_nsizes_impl(const char *cmd)
|
||||
{
|
||||
@ -79,57 +79,64 @@ vsalloc(tsdn_t *tsdn, const void *ptr)
|
||||
return (isalloc(tsdn, extent, ptr));
|
||||
}
|
||||
|
||||
TEST_BEGIN(test_arena_reset)
|
||||
static unsigned
|
||||
do_arena_create(extent_hooks_t *h)
|
||||
{
|
||||
unsigned arena_ind;
|
||||
size_t sz = sizeof(unsigned);
|
||||
assert_d_eq(mallctl("arenas.create", (void *)&arena_ind, &sz,
|
||||
(void *)(h != NULL ? &h : NULL), (h != NULL ? sizeof(h) : 0)), 0,
|
||||
"Unexpected mallctl() failure");
|
||||
return (arena_ind);
|
||||
}
|
||||
|
||||
static void
|
||||
do_arena_reset_pre(unsigned arena_ind, void ***ptrs, unsigned *nptrs)
|
||||
{
|
||||
#define NLARGE 32
|
||||
unsigned arena_ind, nsmall, nlarge, nptrs, i;
|
||||
size_t sz, miblen;
|
||||
void **ptrs;
|
||||
unsigned nsmall, nlarge, i;
|
||||
size_t sz;
|
||||
int flags;
|
||||
size_t mib[3];
|
||||
tsdn_t *tsdn;
|
||||
|
||||
sz = sizeof(unsigned);
|
||||
assert_d_eq(mallctl("arenas.create", (void *)&arena_ind, &sz, NULL, 0),
|
||||
0, "Unexpected mallctl() failure");
|
||||
|
||||
flags = MALLOCX_ARENA(arena_ind) | MALLOCX_TCACHE_NONE;
|
||||
|
||||
nsmall = get_nsmall();
|
||||
nlarge = get_nlarge() > NLARGE ? NLARGE : get_nlarge();
|
||||
nptrs = nsmall + nlarge;
|
||||
ptrs = (void **)malloc(nptrs * sizeof(void *));
|
||||
assert_ptr_not_null(ptrs, "Unexpected malloc() failure");
|
||||
*nptrs = nsmall + nlarge;
|
||||
*ptrs = (void **)malloc(*nptrs * sizeof(void *));
|
||||
assert_ptr_not_null(*ptrs, "Unexpected malloc() failure");
|
||||
|
||||
/* Allocate objects with a wide range of sizes. */
|
||||
for (i = 0; i < nsmall; i++) {
|
||||
sz = get_small_size(i);
|
||||
ptrs[i] = mallocx(sz, flags);
|
||||
assert_ptr_not_null(ptrs[i],
|
||||
(*ptrs)[i] = mallocx(sz, flags);
|
||||
assert_ptr_not_null((*ptrs)[i],
|
||||
"Unexpected mallocx(%zu, %#x) failure", sz, flags);
|
||||
}
|
||||
for (i = 0; i < nlarge; i++) {
|
||||
sz = get_large_size(i);
|
||||
ptrs[nsmall + i] = mallocx(sz, flags);
|
||||
assert_ptr_not_null(ptrs[i],
|
||||
(*ptrs)[nsmall + i] = mallocx(sz, flags);
|
||||
assert_ptr_not_null((*ptrs)[i],
|
||||
"Unexpected mallocx(%zu, %#x) failure", sz, flags);
|
||||
}
|
||||
|
||||
tsdn = tsdn_fetch();
|
||||
|
||||
/* Verify allocations. */
|
||||
for (i = 0; i < nptrs; i++) {
|
||||
assert_zu_gt(ivsalloc(tsdn, ptrs[i]), 0,
|
||||
for (i = 0; i < *nptrs; i++) {
|
||||
assert_zu_gt(ivsalloc(tsdn, (*ptrs)[i]), 0,
|
||||
"Allocation should have queryable size");
|
||||
}
|
||||
}
|
||||
|
||||
/* Reset. */
|
||||
miblen = sizeof(mib)/sizeof(size_t);
|
||||
assert_d_eq(mallctlnametomib("arena.0.reset", mib, &miblen), 0,
|
||||
"Unexpected mallctlnametomib() failure");
|
||||
mib[1] = (size_t)arena_ind;
|
||||
assert_d_eq(mallctlbymib(mib, miblen, NULL, NULL, NULL, 0), 0,
|
||||
"Unexpected mallctlbymib() failure");
|
||||
static void
|
||||
do_arena_reset_post(void **ptrs, unsigned nptrs)
|
||||
{
|
||||
tsdn_t *tsdn;
|
||||
unsigned i;
|
||||
|
||||
tsdn = tsdn_fetch();
|
||||
|
||||
/* Verify allocations no longer exist. */
|
||||
for (i = 0; i < nptrs; i++) {
|
||||
@ -139,6 +146,193 @@ TEST_BEGIN(test_arena_reset)
|
||||
|
||||
free(ptrs);
|
||||
}
|
||||
|
||||
static void
|
||||
do_arena_reset_destroy(const char *name, unsigned arena_ind)
|
||||
{
|
||||
size_t mib[3];
|
||||
size_t miblen;
|
||||
|
||||
miblen = sizeof(mib)/sizeof(size_t);
|
||||
assert_d_eq(mallctlnametomib(name, mib, &miblen), 0,
|
||||
"Unexpected mallctlnametomib() failure");
|
||||
mib[1] = (size_t)arena_ind;
|
||||
assert_d_eq(mallctlbymib(mib, miblen, NULL, NULL, NULL, 0), 0,
|
||||
"Unexpected mallctlbymib() failure");
|
||||
}
|
||||
|
||||
static void
|
||||
do_arena_reset(unsigned arena_ind)
|
||||
{
|
||||
|
||||
do_arena_reset_destroy("arena.0.reset", arena_ind);
|
||||
}
|
||||
|
||||
static void
|
||||
do_arena_destroy(unsigned arena_ind)
|
||||
{
|
||||
|
||||
do_arena_reset_destroy("arena.0.destroy", arena_ind);
|
||||
}
|
||||
|
||||
TEST_BEGIN(test_arena_reset)
|
||||
{
|
||||
unsigned arena_ind;
|
||||
void **ptrs;
|
||||
unsigned nptrs;
|
||||
|
||||
arena_ind = do_arena_create(NULL);
|
||||
do_arena_reset_pre(arena_ind, &ptrs, &nptrs);
|
||||
do_arena_reset(arena_ind);
|
||||
do_arena_reset_post(ptrs, nptrs);
|
||||
}
|
||||
TEST_END
|
||||
|
||||
static bool
|
||||
arena_i_initialized(unsigned arena_ind, bool refresh)
|
||||
{
|
||||
bool initialized;
|
||||
size_t mib[3];
|
||||
size_t miblen, sz;
|
||||
|
||||
if (refresh) {
|
||||
uint64_t epoch = 1;
|
||||
assert_d_eq(mallctl("epoch", NULL, NULL, (void *)&epoch,
|
||||
sizeof(epoch)), 0, "Unexpected mallctl() failure");
|
||||
}
|
||||
|
||||
miblen = sizeof(mib)/sizeof(size_t);
|
||||
assert_d_eq(mallctlnametomib("arena.0.initialized", mib, &miblen), 0,
|
||||
"Unexpected mallctlnametomib() failure");
|
||||
mib[1] = (size_t)arena_ind;
|
||||
sz = sizeof(initialized);
|
||||
assert_d_eq(mallctlbymib(mib, miblen, (void *)&initialized, &sz, NULL,
|
||||
0), 0, "Unexpected mallctlbymib() failure");
|
||||
|
||||
return (initialized);
|
||||
}
|
||||
|
||||
TEST_BEGIN(test_arena_destroy_initial)
|
||||
{
|
||||
|
||||
assert_false(arena_i_initialized(MALLCTL_ARENAS_DESTROYED, false),
|
||||
"Destroyed arena stats should not be initialized");
|
||||
}
|
||||
TEST_END
|
||||
|
||||
TEST_BEGIN(test_arena_destroy_hooks_default)
|
||||
{
|
||||
unsigned arena_ind, arena_ind_another, arena_ind_prev;
|
||||
void **ptrs;
|
||||
unsigned nptrs;
|
||||
|
||||
arena_ind = do_arena_create(NULL);
|
||||
do_arena_reset_pre(arena_ind, &ptrs, &nptrs);
|
||||
|
||||
assert_false(arena_i_initialized(arena_ind, false),
|
||||
"Arena stats should not be initialized");
|
||||
assert_true(arena_i_initialized(arena_ind, true),
|
||||
"Arena stats should be initialized");
|
||||
|
||||
/*
|
||||
* Create another arena before destroying one, to better verify arena
|
||||
* index reuse.
|
||||
*/
|
||||
arena_ind_another = do_arena_create(NULL);
|
||||
|
||||
do_arena_destroy(arena_ind);
|
||||
|
||||
assert_false(arena_i_initialized(arena_ind, true),
|
||||
"Arena stats should not be initialized");
|
||||
assert_true(arena_i_initialized(MALLCTL_ARENAS_DESTROYED, false),
|
||||
"Destroyed arena stats should be initialized");
|
||||
|
||||
do_arena_reset_post(ptrs, nptrs);
|
||||
|
||||
arena_ind_prev = arena_ind;
|
||||
arena_ind = do_arena_create(NULL);
|
||||
do_arena_reset_pre(arena_ind, &ptrs, &nptrs);
|
||||
assert_u_eq(arena_ind, arena_ind_prev,
|
||||
"Arena index should have been recycled");
|
||||
do_arena_destroy(arena_ind);
|
||||
do_arena_reset_post(ptrs, nptrs);
|
||||
|
||||
do_arena_destroy(arena_ind_another);
|
||||
}
|
||||
TEST_END
|
||||
|
||||
/*
|
||||
* Actually unmap extents, regardless of config_munmap, so that attempts to
|
||||
* access a destroyed arena's memory will segfault.
|
||||
*/
|
||||
static bool
|
||||
extent_dalloc_unmap(extent_hooks_t *extent_hooks, void *addr, size_t size,
|
||||
bool committed, unsigned arena_ind)
|
||||
{
|
||||
|
||||
TRACE_HOOK("%s(extent_hooks=%p, addr=%p, size=%zu, committed=%s, "
|
||||
"arena_ind=%u)\n", __func__, extent_hooks, addr, size, committed ?
|
||||
"true" : "false", arena_ind);
|
||||
assert_ptr_eq(extent_hooks, &hooks,
|
||||
"extent_hooks should be same as pointer used to set hooks");
|
||||
assert_ptr_eq(extent_hooks->dalloc, extent_dalloc_unmap,
|
||||
"Wrong hook function");
|
||||
called_dalloc = true;
|
||||
if (!try_dalloc)
|
||||
return (true);
|
||||
pages_unmap(addr, size);
|
||||
did_dalloc = true;
|
||||
return (false);
|
||||
}
|
||||
|
||||
static extent_hooks_t hooks_orig;
|
||||
|
||||
static extent_hooks_t hooks_unmap = {
|
||||
extent_alloc_hook,
|
||||
extent_dalloc_unmap, /* dalloc */
|
||||
extent_commit_hook,
|
||||
extent_decommit_hook,
|
||||
extent_purge_lazy_hook,
|
||||
extent_purge_forced_hook,
|
||||
extent_split_hook,
|
||||
extent_merge_hook
|
||||
};
|
||||
|
||||
TEST_BEGIN(test_arena_destroy_hooks_unmap)
|
||||
{
|
||||
unsigned arena_ind;
|
||||
void **ptrs;
|
||||
unsigned nptrs;
|
||||
|
||||
extent_hooks_prep();
|
||||
try_decommit = false;
|
||||
memcpy(&hooks_orig, &hooks, sizeof(extent_hooks_t));
|
||||
memcpy(&hooks, &hooks_unmap, sizeof(extent_hooks_t));
|
||||
|
||||
did_alloc = false;
|
||||
arena_ind = do_arena_create(&hooks);
|
||||
do_arena_reset_pre(arena_ind, &ptrs, &nptrs);
|
||||
|
||||
assert_true(did_alloc, "Expected alloc");
|
||||
|
||||
assert_false(arena_i_initialized(arena_ind, false),
|
||||
"Arena stats should not be initialized");
|
||||
assert_true(arena_i_initialized(arena_ind, true),
|
||||
"Arena stats should be initialized");
|
||||
|
||||
did_dalloc = false;
|
||||
do_arena_destroy(arena_ind);
|
||||
assert_true(did_dalloc, "Expected dalloc");
|
||||
|
||||
assert_false(arena_i_initialized(arena_ind, true),
|
||||
"Arena stats should not be initialized");
|
||||
assert_true(arena_i_initialized(MALLCTL_ARENAS_DESTROYED, false),
|
||||
"Destroyed arena stats should be initialized");
|
||||
|
||||
do_arena_reset_post(ptrs, nptrs);
|
||||
|
||||
memcpy(&hooks, &hooks_orig, sizeof(extent_hooks_t));
|
||||
}
|
||||
TEST_END
|
||||
|
||||
int
|
||||
@ -146,5 +340,8 @@ main(void)
|
||||
{
|
||||
|
||||
return (test(
|
||||
test_arena_reset));
|
||||
test_arena_reset,
|
||||
test_arena_destroy_initial,
|
||||
test_arena_destroy_hooks_default,
|
||||
test_arena_destroy_hooks_unmap));
|
||||
}
|
||||
|
5
test/unit/arena_reset_prof.c
Normal file
5
test/unit/arena_reset_prof.c
Normal file
@ -0,0 +1,5 @@
|
||||
#include "test/jemalloc_test.h"
|
||||
#define ARENA_RESET_PROF_C_
|
||||
|
||||
const char *malloc_conf = "prof:true,lg_prof_sample:0";
|
||||
#include "arena_reset.c"
|
@ -381,6 +381,15 @@ TEST_BEGIN(test_arena_i_initialized)
|
||||
"Unexpected mallctl() failure");
|
||||
assert_true(initialized,
|
||||
"Merged arena statistics should always be initialized");
|
||||
|
||||
/* Equivalent to the above but using mallctl() directly. */
|
||||
sz = sizeof(initialized);
|
||||
assert_d_eq(mallctl(
|
||||
"arena." STRINGIFY(MALLCTL_ARENAS_ALL) ".initialized",
|
||||
(void *)&initialized, &sz, NULL, 0), 0,
|
||||
"Unexpected mallctl() failure");
|
||||
assert_true(initialized,
|
||||
"Merged arena statistics should always be initialized");
|
||||
}
|
||||
TEST_END
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user