Bug 1006769 - Part 1: Improve heap-overhead memory reporting. r=njn
authorEric Rahm <erahm@mozilla.com>
Wed, 21 May 2014 17:34:06 -0700
changeset 184768 b8891092318100e90243d9ab0fef4611d757c25f
parent 184767 3b04fd85e0884e258b1bdcb0a8016c8abdba5d29
child 184769 a0e903ba9249e0239916ee23cf626330f7856508
push id26836
push usercbook@mozilla.com
push dateMon, 26 May 2014 12:37:49 +0000
treeherdermozilla-central@5cd52de816f9 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersnjn
bugs1006769
milestone32.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1006769 - Part 1: Improve heap-overhead memory reporting. r=njn
memory/mozjemalloc/jemalloc.c
memory/mozjemalloc/jemalloc_types.h
xpcom/base/nsMemoryReporterManager.cpp
--- a/memory/mozjemalloc/jemalloc.c
+++ b/memory/mozjemalloc/jemalloc.c
@@ -6579,17 +6579,17 @@ malloc_usable_size_impl(const void *ptr)
 
 	return (isalloc(ptr));
 #endif
 }
 
 MOZ_JEMALLOC_API void
 jemalloc_stats_impl(jemalloc_stats_t *stats)
 {
-	size_t i;
+	size_t i, non_arena_mapped, chunk_header_size;
 
 	assert(stats != NULL);
 
 	/*
 	 * Gather runtime settings.
 	 */
 	stats->opt_abort = opt_abort;
 	stats->opt_junk =
@@ -6639,65 +6639,105 @@ jemalloc_stats_impl(jemalloc_stats_t *st
 	/*
 	 * Gather current memory usage statistics.
 	 */
 	stats->mapped = 0;
 	stats->allocated = 0;
         stats->waste = 0;
 	stats->page_cache = 0;
         stats->bookkeeping = 0;
+	stats->bin_unused = 0;
+
+	non_arena_mapped = 0;
 
 	/* Get huge mapped/allocated. */
 	malloc_mutex_lock(&huge_mtx);
-	stats->mapped += huge_mapped;
+	non_arena_mapped += huge_mapped;
 	stats->allocated += huge_allocated;
 	assert(huge_mapped >= huge_allocated);
 	malloc_mutex_unlock(&huge_mtx);
 
 	/* Get base mapped/allocated. */
 	malloc_mutex_lock(&base_mtx);
-	stats->mapped += base_mapped;
+	non_arena_mapped += base_mapped;
 	stats->bookkeeping += base_committed;
 	assert(base_mapped >= base_committed);
 	malloc_mutex_unlock(&base_mtx);
 
 	/* Iterate over arenas. */
 	for (i = 0; i < narenas; i++) {
 		arena_t *arena = arenas[i];
-		size_t arena_mapped, arena_allocated, arena_committed, arena_dirty;
+		size_t arena_mapped, arena_allocated, arena_committed, arena_dirty, j,
+		    arena_unused, arena_headers;
+		arena_run_t* run;
+		arena_chunk_map_t* mapelm;
 
 		if (arena == NULL) {
 			continue;
 		}
 
+		arena_headers = 0;
+		arena_unused = 0;
+
 		malloc_spin_lock(&arena->lock);
 
 		arena_mapped = arena->stats.mapped;
 
 		/* "committed" counts dirty and allocated memory. */
 		arena_committed = arena->stats.committed << pagesize_2pow;
 
 		arena_allocated = arena->stats.allocated_small +
 				  arena->stats.allocated_large;
 
 		arena_dirty = arena->ndirty << pagesize_2pow;
 
+		for (j = 0; j < ntbins + nqbins + nsbins; j++) {
+			arena_bin_t* bin = &arena->bins[j];
+			size_t bin_unused = 0;
+			const size_t run_header_size = sizeof(arena_run_t) +
+			    (sizeof(unsigned) * (bin->regs_mask_nelms - 1));
+
+			rb_foreach_begin(arena_chunk_map_t, link, &bin->runs, mapelm) {
+				run = (arena_run_t *)(mapelm->bits & ~pagesize_mask);
+				bin_unused += run->nfree * bin->reg_size;
+			} rb_foreach_end(arena_chunk_map_t, link, &bin->runs, mapelm)
+
+			if (bin->runcur) {
+				bin_unused += bin->runcur->nfree * bin->reg_size;
+			}
+
+			arena_unused += bin_unused;
+			arena_headers += bin->stats.curruns * bin->reg0_offset;
+		}
+
 		malloc_spin_unlock(&arena->lock);
 
 		assert(arena_mapped >= arena_committed);
 		assert(arena_committed >= arena_allocated + arena_dirty);
 
 		/* "waste" is committed memory that is neither dirty nor
 		 * allocated. */
 		stats->mapped += arena_mapped;
 		stats->allocated += arena_allocated;
 		stats->page_cache += arena_dirty;
-		stats->waste += arena_committed - arena_allocated - arena_dirty;
+		stats->waste += arena_committed -
+		    arena_allocated - arena_dirty - arena_unused - arena_headers;
+		stats->bin_unused += arena_unused;
+		stats->bookkeeping += arena_headers;
 	}
 
+	/* Account for arena chunk headers in bookkeeping rather than waste. */
+	chunk_header_size =
+	    ((stats->mapped / stats->chunksize) * arena_chunk_header_npages) <<
+	    pagesize_2pow;
+
+	stats->mapped += non_arena_mapped;
+	stats->bookkeeping += chunk_header_size;
+	stats->waste -= chunk_header_size;
+
 	assert(stats->mapped >= stats->allocated + stats->waste +
 				stats->page_cache + stats->bookkeeping);
 }
 
 #ifdef MALLOC_DOUBLE_PURGE
 
 /* Explicitly remove all of this chunk's MADV_FREE'd pages from memory. */
 static void
--- a/memory/mozjemalloc/jemalloc_types.h
+++ b/memory/mozjemalloc/jemalloc_types.h
@@ -76,15 +76,16 @@ typedef struct {
 	size_t	allocated;	/* Bytes allocated (committed, in use by application). */
         size_t  waste;          /* Bytes committed, not in use by the
                                    application, and not intentionally left
                                    unused (i.e., not dirty). */
         size_t	page_cache;	/* Committed, unused pages kept around as a
                                    cache.  (jemalloc calls these "dirty".) */
         size_t  bookkeeping;    /* Committed bytes used internally by the
                                    allocator. */
+	size_t bin_unused; /* Bytes committed to a bin but currently unused. */
 } jemalloc_stats_t;
 
 #ifdef __cplusplus
 } /* extern "C" */
 #endif
 
 #endif /* _JEMALLOC_TYPES_H_ */
--- a/xpcom/base/nsMemoryReporterManager.cpp
+++ b/xpcom/base/nsMemoryReporterManager.cpp
@@ -706,23 +706,31 @@ public:
 "application because the allocator regularly rounds up request sizes. (The "
 "exact amount requested is not recorded.)");
     NS_ENSURE_SUCCESS(rv, rv);
 
     // We mark this and the other heap-overhead reporters as KIND_NONHEAP
     // because KIND_HEAP memory means "counted in heap-allocated", which
     // this is not.
     rv = MOZ_COLLECT_REPORT(
+      "explicit/heap-overhead/bin-unused", KIND_NONHEAP, UNITS_BYTES,
+      stats.bin_unused,
+"Bytes reserved for bins of fixed-size allocations which do not correspond to "
+"an active allocation.");
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    rv = MOZ_COLLECT_REPORT(
       "explicit/heap-overhead/waste", KIND_NONHEAP, UNITS_BYTES,
       stats.waste,
 "Committed bytes which do not correspond to an active allocation and which the "
 "allocator is not intentionally keeping alive (i.e., not 'heap-bookkeeping' or "
-"'heap-page-cache').  Although the allocator will waste some space under any "
-"circumstances, a large value here may indicate that the heap is highly "
-"fragmented, or that allocator is performing poorly for some other reason.");
+"'heap-page-cache' or 'heap-bin-unused').  Although the allocator will waste "
+"some space under any circumstances, a large value here may indicate that the "
+"heap is highly fragmented, or that allocator is performing poorly for some "
+"other reason.");
     NS_ENSURE_SUCCESS(rv, rv);
 
     rv = MOZ_COLLECT_REPORT(
       "explicit/heap-overhead/bookkeeping", KIND_NONHEAP, UNITS_BYTES,
       stats.bookkeeping,
 "Committed bytes which the heap allocator uses for internal data structures.");
     NS_ENSURE_SUCCESS(rv, rv);
 
@@ -748,16 +756,31 @@ public:
     rv = MOZ_COLLECT_REPORT(
       "heap-overhead-ratio", KIND_OTHER, UNITS_PERCENTAGE,
       HeapOverheadRatio(&stats),
 "Ratio of committed, unused bytes to allocated bytes; i.e., "
 "'heap-overhead' / 'heap-allocated'.  This measures the overhead of "
 "the heap allocator relative to amount of memory allocated.");
     NS_ENSURE_SUCCESS(rv, rv);
 
+    rv = MOZ_COLLECT_REPORT(
+      "heap-mapped", KIND_OTHER, UNITS_BYTES, stats.mapped,
+      "Amount of memory currently mapped.");
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    rv = MOZ_COLLECT_REPORT(
+      "heap-chunksize", KIND_OTHER, UNITS_BYTES, stats.chunksize,
+      "Size of chunks.");
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    rv = MOZ_COLLECT_REPORT(
+      "heap-chunks", KIND_OTHER, UNITS_COUNT, (stats.mapped / stats.chunksize),
+      "Number of chunks currently mapped.");
+    NS_ENSURE_SUCCESS(rv, rv);
+
     return NS_OK;
   }
 };
 NS_IMPL_ISUPPORTS(JemallocHeapReporter, nsIMemoryReporter)
 
 #endif  // HAVE_JEMALLOC_STATS
 
 // Why is this here?  At first glance, you'd think it could be defined and