Bug 1735250 - Provide a less-magic array size for jemalloc_stats r=glandium

jemalloc_stats takes an array for its second argument.  It expects this
array to have enough space for all the bins, previously the maximum was set
as a magic number.  To make it dependent on the configured bins this patch
replaces the compile-time constant with a function.

Differential Revision: https://phabricator.services.mozilla.com/D127761
This commit is contained in:
Paul Bone 2021-10-21 06:42:34 +00:00
parent 1b721fc414
commit 1a20d57362
7 changed files with 43 additions and 42 deletions

View File

@ -63,11 +63,14 @@ MALLOC_DECL(malloc_good_size, size_t, size_t)
# endif
# if MALLOC_FUNCS & MALLOC_FUNCS_JEMALLOC
// The 2nd argument points to an optional array exactly JEMALLOC_MAX_STATS_BINS
// long to be filled in (if non-null). Any unused bin has it's size set to zero.
// The 2nd argument points to an optional array exactly
// jemalloc_stats_num_bins() long to be filled in (if non-null).
MALLOC_DECL(jemalloc_stats_internal, void, jemalloc_stats_t*,
jemalloc_bin_stats_t*)
// Return the size of the jemalloc_bin_stats_t array.
MALLOC_DECL(jemalloc_stats_num_bins, size_t)
// On some operating systems (Mac), we use madvise(MADV_FREE) to hand pages
// back to the operating system. On Mac, the operating system doesn't take
// this memory back immediately; instead, the OS takes it back only when the

View File

@ -3966,8 +3966,6 @@ static bool malloc_init_hard() {
gRealPageSize = gPageSize = (size_t)result;
#endif
MOZ_RELEASE_ASSERT(JEMALLOC_MAX_STATS_BINS >= NUM_SMALL_CLASSES);
// Get runtime configuration.
if ((opts = getenv("MALLOC_OPTIONS"))) {
for (i = 0; opts[i] != '\0'; i++) {
@ -4326,10 +4324,7 @@ inline void MozJemalloc::jemalloc_stats_internal(
return;
}
if (aBinStats) {
// An assertion in malloc_init_hard will guarantee that
// JEMALLOC_MAX_STATS_BINS >= NUM_SMALL_CLASSES.
memset(aBinStats, 0,
sizeof(jemalloc_bin_stats_t) * JEMALLOC_MAX_STATS_BINS);
memset(aBinStats, 0, sizeof(jemalloc_bin_stats_t) * NUM_SMALL_CLASSES);
}
// Gather runtime settings.
@ -4455,6 +4450,11 @@ inline void MozJemalloc::jemalloc_stats_internal(
aStats->page_cache + aStats->bookkeeping);
}
template <>
inline size_t MozJemalloc::jemalloc_stats_num_bins() {
return NUM_SMALL_CLASSES;
}
#ifdef MALLOC_DOUBLE_PURGE
// Explicitly remove all of this chunk's MADV_FREE'd pages from memory.

View File

@ -113,9 +113,6 @@ typedef struct {
size_t bytes_per_run; // The number of bytes per run, including headers.
} jemalloc_bin_stats_t;
// This is the total number of bins.
#define JEMALLOC_MAX_STATS_BINS 51
enum PtrInfoTag {
// The pointer is not currently known to the allocator.
// 'addr', 'size', and 'arenaId' are always 0.

View File

@ -11,6 +11,7 @@
// necessary:
// - malloc_good_size (used to be called je_malloc_usable_in_advance)
// - jemalloc_stats
// - jemalloc_stats_num_bins
// - jemalloc_purge_freed_pages
// - jemalloc_free_dirty_pages
// - jemalloc_thread_local_arena

View File

@ -35,6 +35,7 @@
//
// - jemalloc specific functions:
// - jemalloc_stats
// - jemalloc_stats_num_bins
// - jemalloc_purge_freed_pages
// - jemalloc_free_dirty_pages
// - jemalloc_thread_local_arena

View File

@ -773,7 +773,10 @@ class Replay {
}
mOps++;
jemalloc_stats_t stats;
jemalloc_bin_stats_t bin_stats[JEMALLOC_MAX_STATS_BINS];
// Using a variable length array here is a GCC & Clang extension. But it
// allows us to place this on the stack and not alter jemalloc's profiling.
const size_t num_bins = ::jemalloc_stats_num_bins();
jemalloc_bin_stats_t bin_stats[num_bins];
::jemalloc_stats_internal(&stats, bin_stats);
#ifdef XP_LINUX
@ -788,7 +791,8 @@ class Replay {
size_t large_used = 0;
size_t huge_slop = 0;
size_t huge_used = 0;
size_t bin_slop[JEMALLOC_MAX_STATS_BINS] = {0};
size_t bin_slop[num_bins];
memset(bin_slop, 0, sizeof(size_t) * num_bins);
for (size_t slot_id = 0; slot_id < mNumUsedSlots; slot_id++) {
MemSlot& slot = mSlots[slot_id];
@ -806,7 +810,7 @@ class Replay {
(stats.subpage_max ? stats.subpage_max : stats.quantum_wide_max)) {
// We know that this is an inefficient linear search, but there's a
// small number of bins and this is simple.
for (unsigned i = 0; i < JEMALLOC_MAX_STATS_BINS; i++) {
for (unsigned i = 0; i < num_bins; i++) {
auto& bin = bin_stats[i];
if (used == bin.size) {
bin_slop[i] += slop;
@ -866,23 +870,20 @@ class Replay {
"unused (c)", "total (c)", "used (c)", "non-full (r)", "total (r)",
"used (r)");
for (auto& bin : bin_stats) {
if (bin.size) {
FdPrintf(mStdErr, "%8zu %8zuKiB %7zuKiB %7zu%% %12zu %9zu %7zu%%\n",
bin.size, bin.bytes_unused / 1024, bin.bytes_total / 1024,
percent(bin.bytes_total - bin.bytes_unused, bin.bytes_total),
bin.num_non_full_runs, bin.num_runs,
percent(bin.num_runs - bin.num_non_full_runs, bin.num_runs));
}
MOZ_ASSERT(bin.size);
FdPrintf(mStdErr, "%8zu %8zuKiB %7zuKiB %7zu%% %12zu %9zu %7zu%%\n",
bin.size, bin.bytes_unused / 1024, bin.bytes_total / 1024,
percent(bin.bytes_total - bin.bytes_unused, bin.bytes_total),
bin.num_non_full_runs, bin.num_runs,
percent(bin.num_runs - bin.num_non_full_runs, bin.num_runs));
}
FdPrintf(mStdErr, "\n%5s %8s %9s %7s\n", "bin", "slop", "used", "percent");
for (unsigned i = 0; i < JEMALLOC_MAX_STATS_BINS; i++) {
for (unsigned i = 0; i < num_bins; i++) {
auto& bin = bin_stats[i];
if (bin.size) {
size_t used = bin.bytes_total - bin.bytes_unused;
FdPrintf(mStdErr, "%5zu %8zu %9zu %6zu%%\n", bin.size, bin_slop[i],
used, percent(bin_slop[i], used));
}
size_t used = bin.bytes_total - bin.bytes_unused;
FdPrintf(mStdErr, "%5zu %8zu %9zu %6zu%%\n", bin.size, bin_slop[i], used,
percent(bin_slop[i], used));
}
FdPrintf(mStdErr, "%5s %8zu %9zu %6zu%%\n", "large", large_slop, large_used,
percent(large_slop, large_used));
@ -896,22 +897,21 @@ class Replay {
/*
* Create and print frequency distributions of memory requests.
*/
void print_distributions(
jemalloc_stats_t& stats,
jemalloc_bin_stats_t (&bin_stats)[JEMALLOC_MAX_STATS_BINS]) {
void print_distributions(jemalloc_stats_t& stats,
jemalloc_bin_stats_t* bin_stats) {
const size_t num_bins = ::jemalloc_stats_num_bins();
// We compute distributions for all of the bins for small allocations
// (JEMALLOC_MAX_STATS_BINS) plus two more distributions for larger
// allocations.
Distribution dists[JEMALLOC_MAX_STATS_BINS + 2];
// (num_bins) plus two more distributions for larger allocations.
Distribution dists[num_bins + 2];
unsigned last_size = 0;
unsigned num_dists = 0;
for (auto& bin : bin_stats) {
if (bin.size == 0) {
break;
}
for (unsigned i = 0; i < num_bins; i++) {
auto& bin = bin_stats[i];
auto& dist = dists[num_dists++];
MOZ_ASSERT(bin.size);
if (bin.size <= 16) {
// 1 byte buckets.
dist = Distribution(bin.size, last_size, 1);
@ -938,7 +938,7 @@ class Replay {
Distribution(stats.page_size * 4, stats.page_size, stats.page_size / 4);
num_dists++;
MOZ_RELEASE_ASSERT(num_dists <= JEMALLOC_MAX_STATS_BINS + 2);
MOZ_RELEASE_ASSERT(num_dists <= num_bins + 2);
for (size_t slot_id = 0; slot_id < mNumUsedSlots; slot_id++) {
MemSlot& slot = mSlots[slot_id];

View File

@ -1206,7 +1206,8 @@ class JemallocHeapReporter final : public nsIMemoryReporter {
NS_IMETHOD CollectReports(nsIHandleReportCallback* aHandleReport,
nsISupports* aData, bool aAnonymize) override {
jemalloc_stats_t stats;
jemalloc_bin_stats_t bin_stats[JEMALLOC_MAX_STATS_BINS];
const size_t num_bins = jemalloc_stats_num_bins();
jemalloc_bin_stats_t bin_stats[num_bins];
jemalloc_stats(&stats, bin_stats);
// clang-format off
@ -1225,9 +1226,7 @@ class JemallocHeapReporter final : public nsIMemoryReporter {
// because KIND_HEAP memory means "counted in heap-allocated", which
// this is not.
for (auto& bin : bin_stats) {
if (!bin.size) {
continue;
}
MOZ_ASSERT(bin.size);
nsPrintfCString path("explicit/heap-overhead/bin-unused/bin-%zu",
bin.size);
aHandleReport->Callback(EmptyCString(), path, KIND_NONHEAP, UNITS_BYTES,