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.c \
|
||||||
$(srcroot)test/unit/zero_realloc_abort.c \
|
$(srcroot)test/unit/zero_realloc_abort.c \
|
||||||
$(srcroot)test/unit/zero_realloc_free.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)
|
ifeq (@enable_prof@, 1)
|
||||||
TESTS_UNIT += \
|
TESTS_UNIT += \
|
||||||
$(srcroot)test/unit/arena_reset_prof.c
|
$(srcroot)test/unit/arena_reset_prof.c
|
||||||
|
@ -2451,6 +2451,21 @@ struct extent_hooks_s {
|
|||||||
</para></listitem>
|
</para></listitem>
|
||||||
</varlistentry>
|
</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">
|
<varlistentry id="stats.background_thread.num_threads">
|
||||||
<term>
|
<term>
|
||||||
<mallctl>stats.background_thread.num_threads</mallctl>
|
<mallctl>stats.background_thread.num_threads</mallctl>
|
||||||
|
@ -20,6 +20,7 @@ extern bool opt_zero;
|
|||||||
extern unsigned opt_narenas;
|
extern unsigned opt_narenas;
|
||||||
extern zero_realloc_action_t opt_zero_realloc_action;
|
extern zero_realloc_action_t opt_zero_realloc_action;
|
||||||
extern const char *zero_realloc_mode_names[];
|
extern const char *zero_realloc_mode_names[];
|
||||||
|
extern atomic_zu_t zero_realloc_count;
|
||||||
|
|
||||||
/* Number of CPUs. */
|
/* Number of CPUs. */
|
||||||
extern unsigned ncpus;
|
extern unsigned ncpus;
|
||||||
|
@ -224,6 +224,7 @@ CTL_PROTO(stats_metadata_thp)
|
|||||||
CTL_PROTO(stats_resident)
|
CTL_PROTO(stats_resident)
|
||||||
CTL_PROTO(stats_mapped)
|
CTL_PROTO(stats_mapped)
|
||||||
CTL_PROTO(stats_retained)
|
CTL_PROTO(stats_retained)
|
||||||
|
CTL_PROTO(stats_zero_reallocs)
|
||||||
CTL_PROTO(experimental_hooks_install)
|
CTL_PROTO(experimental_hooks_install)
|
||||||
CTL_PROTO(experimental_hooks_remove)
|
CTL_PROTO(experimental_hooks_remove)
|
||||||
CTL_PROTO(experimental_utilization_query)
|
CTL_PROTO(experimental_utilization_query)
|
||||||
@ -593,7 +594,8 @@ static const ctl_named_node_t stats_node[] = {
|
|||||||
{NAME("background_thread"),
|
{NAME("background_thread"),
|
||||||
CHILD(named, stats_background_thread)},
|
CHILD(named, stats_background_thread)},
|
||||||
{NAME("mutexes"), CHILD(named, stats_mutexes)},
|
{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[] = {
|
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,
|
CTL_RO_CGEN(config_stats, stats_background_thread_run_interval,
|
||||||
nstime_ns(&ctl_stats->background_thread.run_interval), uint64_t)
|
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_dss, arenas_i(mib[2])->dss, const char *)
|
||||||
CTL_RO_GEN(stats_arenas_i_dirty_decay_ms, arenas_i(mib[2])->dirty_decay_ms,
|
CTL_RO_GEN(stats_arenas_i_dirty_decay_ms, arenas_i(mib[2])->dirty_decay_ms,
|
||||||
ssize_t)
|
ssize_t)
|
||||||
|
@ -70,6 +70,8 @@ bool opt_junk_free =
|
|||||||
zero_realloc_action_t opt_zero_realloc_action =
|
zero_realloc_action_t opt_zero_realloc_action =
|
||||||
zero_realloc_action_strict;
|
zero_realloc_action_strict;
|
||||||
|
|
||||||
|
atomic_zu_t zero_realloc_count = ATOMIC_INIT(0);
|
||||||
|
|
||||||
const char *zero_realloc_mode_names[] = {
|
const char *zero_realloc_mode_names[] = {
|
||||||
"strict",
|
"strict",
|
||||||
"free",
|
"free",
|
||||||
@ -3160,6 +3162,9 @@ je_rallocx(void *ptr, size_t size, int flags) {
|
|||||||
|
|
||||||
static void *
|
static void *
|
||||||
do_realloc_nonnull_zero(void *ptr) {
|
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) {
|
if (opt_zero_realloc_action == zero_realloc_action_strict) {
|
||||||
/*
|
/*
|
||||||
* The user might have gotten a strict setting while expecting a
|
* 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,
|
size_t allocated, active, metadata, metadata_thp, resident, mapped,
|
||||||
retained;
|
retained;
|
||||||
size_t num_background_threads;
|
size_t num_background_threads;
|
||||||
|
size_t zero_reallocs;
|
||||||
uint64_t background_thread_num_runs, background_thread_run_interval;
|
uint64_t background_thread_num_runs, background_thread_run_interval;
|
||||||
|
|
||||||
CTL_GET("stats.allocated", &allocated, size_t);
|
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.mapped", &mapped, size_t);
|
||||||
CTL_GET("stats.retained", &retained, size_t);
|
CTL_GET("stats.retained", &retained, size_t);
|
||||||
|
|
||||||
|
CTL_GET("stats.zero_reallocs", &zero_reallocs, size_t);
|
||||||
|
|
||||||
if (have_background_thread) {
|
if (have_background_thread) {
|
||||||
CTL_GET("stats.background_thread.num_threads",
|
CTL_GET("stats.background_thread.num_threads",
|
||||||
&num_background_threads, size_t);
|
&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, "resident", emitter_type_size, &resident);
|
||||||
emitter_json_kv(emitter, "mapped", emitter_type_size, &mapped);
|
emitter_json_kv(emitter, "mapped", emitter_type_size, &mapped);
|
||||||
emitter_json_kv(emitter, "retained", emitter_type_size, &retained);
|
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, "
|
emitter_table_printf(emitter, "Allocated: %zu, active: %zu, "
|
||||||
"metadata: %zu (n_thp %zu), resident: %zu, mapped: %zu, "
|
"metadata: %zu (n_thp %zu), resident: %zu, mapped: %zu, "
|
||||||
"retained: %zu\n", allocated, active, metadata, metadata_thp,
|
"retained: %zu\n", allocated, active, metadata, metadata_thp,
|
||||||
resident, mapped, retained);
|
resident, mapped, retained);
|
||||||
|
|
||||||
|
/* Strange behaviors */
|
||||||
|
emitter_table_printf(emitter,
|
||||||
|
"Count of realloc(non-null-ptr, 0) calls: %zu\n", zero_reallocs);
|
||||||
|
|
||||||
/* Background thread stats. */
|
/* Background thread stats. */
|
||||||
emitter_json_object_kv_begin(emitter, "background_thread");
|
emitter_json_object_kv_begin(emitter, "background_thread");
|
||||||
emitter_json_kv(emitter, "num_threads", emitter_type_size,
|
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