Add stats counters for number of zero reallocs
This commit is contained in:
parent
9cfa805947
commit
de81a4eada
@ -235,7 +235,8 @@ TESTS_UNIT := \
|
||||
$(srcroot)test/unit/zero.c \
|
||||
$(srcroot)test/unit/zero_realloc_abort.c \
|
||||
$(srcroot)test/unit/zero_realloc_free.c \
|
||||
$(srcroot)test/unit/zero_realloc_strict.c
|
||||
$(srcroot)test/unit/zero_realloc_strict.c \
|
||||
$(srcroot)test/unit/zero_reallocs.c
|
||||
ifeq (@enable_prof@, 1)
|
||||
TESTS_UNIT += \
|
||||
$(srcroot)test/unit/arena_reset_prof.c
|
||||
|
@ -2451,6 +2451,21 @@ struct extent_hooks_s {
|
||||
</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry id="stats.zero_reallocs">
|
||||
<term>
|
||||
<mallctl>stats.zero_reallocs</mallctl>
|
||||
(<type>size_t</type>)
|
||||
<literal>r-</literal>
|
||||
[<option>--enable-stats</option>]
|
||||
</term>
|
||||
<listitem><para>Number of times that the <function>realloc()</function>
|
||||
was called with a non-<constant>NULL</constant> pointer argument and a
|
||||
<constant>0</constant> size argument. This is a fundamentally unsafe
|
||||
pattern in portable programs; see <link linkend="opt.zero_realloc">
|
||||
<mallctl>opt.zero_realloc</mallctl></link> for details.
|
||||
</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry id="stats.background_thread.num_threads">
|
||||
<term>
|
||||
<mallctl>stats.background_thread.num_threads</mallctl>
|
||||
|
@ -20,6 +20,7 @@ extern bool opt_zero;
|
||||
extern unsigned opt_narenas;
|
||||
extern zero_realloc_action_t opt_zero_realloc_action;
|
||||
extern const char *zero_realloc_mode_names[];
|
||||
extern atomic_zu_t zero_realloc_count;
|
||||
|
||||
/* Number of CPUs. */
|
||||
extern unsigned ncpus;
|
||||
|
@ -224,6 +224,7 @@ CTL_PROTO(stats_metadata_thp)
|
||||
CTL_PROTO(stats_resident)
|
||||
CTL_PROTO(stats_mapped)
|
||||
CTL_PROTO(stats_retained)
|
||||
CTL_PROTO(stats_zero_reallocs)
|
||||
CTL_PROTO(experimental_hooks_install)
|
||||
CTL_PROTO(experimental_hooks_remove)
|
||||
CTL_PROTO(experimental_utilization_query)
|
||||
@ -593,7 +594,8 @@ static const ctl_named_node_t stats_node[] = {
|
||||
{NAME("background_thread"),
|
||||
CHILD(named, stats_background_thread)},
|
||||
{NAME("mutexes"), CHILD(named, stats_mutexes)},
|
||||
{NAME("arenas"), CHILD(indexed, stats_arenas)}
|
||||
{NAME("arenas"), CHILD(indexed, stats_arenas)},
|
||||
{NAME("zero_reallocs"), CTL(stats_zero_reallocs)},
|
||||
};
|
||||
|
||||
static const ctl_named_node_t experimental_hooks_node[] = {
|
||||
@ -2841,6 +2843,9 @@ CTL_RO_CGEN(config_stats, stats_background_thread_num_runs,
|
||||
CTL_RO_CGEN(config_stats, stats_background_thread_run_interval,
|
||||
nstime_ns(&ctl_stats->background_thread.run_interval), uint64_t)
|
||||
|
||||
CTL_RO_CGEN(config_stats, stats_zero_reallocs,
|
||||
atomic_load_zu(&zero_realloc_count, ATOMIC_RELAXED), size_t)
|
||||
|
||||
CTL_RO_GEN(stats_arenas_i_dss, arenas_i(mib[2])->dss, const char *)
|
||||
CTL_RO_GEN(stats_arenas_i_dirty_decay_ms, arenas_i(mib[2])->dirty_decay_ms,
|
||||
ssize_t)
|
||||
|
@ -70,6 +70,8 @@ bool opt_junk_free =
|
||||
zero_realloc_action_t opt_zero_realloc_action =
|
||||
zero_realloc_action_strict;
|
||||
|
||||
atomic_zu_t zero_realloc_count = ATOMIC_INIT(0);
|
||||
|
||||
const char *zero_realloc_mode_names[] = {
|
||||
"strict",
|
||||
"free",
|
||||
@ -3160,6 +3162,9 @@ je_rallocx(void *ptr, size_t size, int flags) {
|
||||
|
||||
static void *
|
||||
do_realloc_nonnull_zero(void *ptr) {
|
||||
if (config_stats) {
|
||||
atomic_fetch_add_zu(&zero_realloc_count, 1, ATOMIC_RELAXED);
|
||||
}
|
||||
if (opt_zero_realloc_action == zero_realloc_action_strict) {
|
||||
/*
|
||||
* The user might have gotten a strict setting while expecting a
|
||||
|
@ -1252,6 +1252,7 @@ stats_print_helper(emitter_t *emitter, bool merged, bool destroyed,
|
||||
size_t allocated, active, metadata, metadata_thp, resident, mapped,
|
||||
retained;
|
||||
size_t num_background_threads;
|
||||
size_t zero_reallocs;
|
||||
uint64_t background_thread_num_runs, background_thread_run_interval;
|
||||
|
||||
CTL_GET("stats.allocated", &allocated, size_t);
|
||||
@ -1262,6 +1263,8 @@ stats_print_helper(emitter_t *emitter, bool merged, bool destroyed,
|
||||
CTL_GET("stats.mapped", &mapped, size_t);
|
||||
CTL_GET("stats.retained", &retained, size_t);
|
||||
|
||||
CTL_GET("stats.zero_reallocs", &zero_reallocs, size_t);
|
||||
|
||||
if (have_background_thread) {
|
||||
CTL_GET("stats.background_thread.num_threads",
|
||||
&num_background_threads, size_t);
|
||||
@ -1285,12 +1288,18 @@ stats_print_helper(emitter_t *emitter, bool merged, bool destroyed,
|
||||
emitter_json_kv(emitter, "resident", emitter_type_size, &resident);
|
||||
emitter_json_kv(emitter, "mapped", emitter_type_size, &mapped);
|
||||
emitter_json_kv(emitter, "retained", emitter_type_size, &retained);
|
||||
emitter_json_kv(emitter, "zero_reallocs", emitter_type_size,
|
||||
&zero_reallocs);
|
||||
|
||||
emitter_table_printf(emitter, "Allocated: %zu, active: %zu, "
|
||||
"metadata: %zu (n_thp %zu), resident: %zu, mapped: %zu, "
|
||||
"retained: %zu\n", allocated, active, metadata, metadata_thp,
|
||||
resident, mapped, retained);
|
||||
|
||||
/* Strange behaviors */
|
||||
emitter_table_printf(emitter,
|
||||
"Count of realloc(non-null-ptr, 0) calls: %zu\n", zero_reallocs);
|
||||
|
||||
/* Background thread stats. */
|
||||
emitter_json_object_kv_begin(emitter, "background_thread");
|
||||
emitter_json_kv(emitter, "num_threads", emitter_type_size,
|
||||
|
40
test/unit/zero_reallocs.c
Normal file
40
test/unit/zero_reallocs.c
Normal file
@ -0,0 +1,40 @@
|
||||
#include "test/jemalloc_test.h"
|
||||
|
||||
static size_t
|
||||
zero_reallocs() {
|
||||
if (!config_stats) {
|
||||
return 0;
|
||||
}
|
||||
size_t count = 12345;
|
||||
size_t sz = sizeof(count);
|
||||
|
||||
assert_d_eq(mallctl("stats.zero_reallocs", (void *)&count, &sz,
|
||||
NULL, 0), 0, "Unexpected mallctl failure");
|
||||
return count;
|
||||
}
|
||||
|
||||
TEST_BEGIN(test_zero_reallocs) {
|
||||
test_skip_if(!config_stats);
|
||||
|
||||
for (size_t i = 0; i < 100; ++i) {
|
||||
void *ptr = mallocx(i * i + 1, 0);
|
||||
assert_ptr_not_null(ptr, "Unexpected mallocx error");
|
||||
size_t count = zero_reallocs();
|
||||
assert_zu_eq(i, count, "Incorrect zero realloc count");
|
||||
ptr = realloc(ptr, 0);
|
||||
assert_ptr_null(ptr, "Realloc didn't free");
|
||||
count = zero_reallocs();
|
||||
assert_zu_eq(i + 1, count, "Realloc didn't adjust count");
|
||||
}
|
||||
}
|
||||
TEST_END
|
||||
|
||||
int
|
||||
main(void) {
|
||||
/*
|
||||
* We expect explicit counts; reentrant tests run multiple times, so
|
||||
* counts leak across runs.
|
||||
*/
|
||||
return test_no_reentrancy(
|
||||
test_zero_reallocs);
|
||||
}
|
3
test/unit/zero_reallocs.sh
Normal file
3
test/unit/zero_reallocs.sh
Normal file
@ -0,0 +1,3 @@
|
||||
#!/bin/sh
|
||||
|
||||
export MALLOC_CONF="zero_realloc:free"
|
Loading…
Reference in New Issue
Block a user