diff --git a/include/jemalloc/internal/inspect.h b/include/jemalloc/internal/inspect.h index e8ed44d3..d8723f96 100644 --- a/include/jemalloc/internal/inspect.h +++ b/include/jemalloc/internal/inspect.h @@ -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 */ diff --git a/src/ctl.c b/src/ctl.c index c0a602fc..727b63a9 100644 --- a/src/ctl.c +++ b/src/ctl.c @@ -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: diff --git a/src/inspect.c b/src/inspect.c index 1c0de129..f46bc9ef 100644 --- a/src/inspect.c +++ b/src/inspect.c @@ -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); -} diff --git a/test/unit/inspect.c b/test/unit/inspect.c index 8111e4a5..cb1cd979 100644 --- a/test/unit/inspect.c +++ b/test/unit/inspect.c @@ -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); }