Remove utilization query mallctl

This commit is contained in:
Slobodan Predolac 2026-05-28 10:54:42 -07:00
parent 39d4b20890
commit 44bb61e19e
4 changed files with 3 additions and 306 deletions

View file

@ -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 */

View file

@ -372,7 +372,6 @@ 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_utilization_query)
CTL_PROTO(experimental_utilization_batch_query)
CTL_PROTO(experimental_prof_recent_alloc_max)
CTL_PROTO(experimental_prof_recent_alloc_dump)
@ -911,7 +910,6 @@ static const ctl_named_node_t experimental_hooks_node[] = {
};
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_prof_recent_node[] = {
@ -4212,102 +4210,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:

View file

@ -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);
}

View file

@ -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);
}