Bug 1134255 - Add breakdown of allocated and unused GC things by kind in memory reports r=njn
☠☠ backed out by c419d483d834 ☠ ☠
authorJon Coppeard <jcoppeard@mozilla.com>
Thu, 19 Feb 2015 11:56:00 +0000
changeset 256989 d60d2602deabe046e430cc8aab13ade02a1a334e
parent 256988 67ea8244ccf33be1fbc120d177e2175f0b19f4c9
child 256990 04a909655b92c61a467d3bcf364909fe9ee33e51
push id4610
push userjlund@mozilla.com
push dateMon, 30 Mar 2015 18:32:55 +0000
treeherdermozilla-beta@4df54044d9ef [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersnjn
bugs1134255
milestone38.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 1134255 - Add breakdown of allocated and unused GC things by kind in memory reports r=njn
js/public/MemoryMetrics.h
js/src/vm/MemoryMetrics.cpp
js/xpconnect/src/XPCJSRuntime.cpp
--- a/js/public/MemoryMetrics.h
+++ b/js/public/MemoryMetrics.h
@@ -430,41 +430,97 @@ struct RuntimeSizes
     // then used to fill in |notableScriptSources| (which actually gets
     // reported), and immediately discarded afterwards.
     ScriptSourcesHashMap *allScriptSources;
     js::Vector<NotableScriptSourceInfo, 0, js::SystemAllocPolicy> notableScriptSources;
 
 #undef FOR_EACH_SIZE
 };
 
+struct GCThingSizes
+{
+#define FOR_EACH_SIZE(macro) \
+    macro(_, _, object) \
+    macro(_, _, script) \
+    macro(_, _, lazyScript) \
+    macro(_, _, shape) \
+    macro(_, _, baseShape) \
+    macro(_, _, objectGroup) \
+    macro(_, _, string) \
+    macro(_, _, symbol) \
+    macro(_, _, jitcode) \
+
+    GCThingSizes()
+      : FOR_EACH_SIZE(ZERO_SIZE)
+        dummy()
+    {}
+
+    GCThingSizes(GCThingSizes &&other)
+      : FOR_EACH_SIZE(COPY_OTHER_SIZE)
+        dummy()
+    {}
+
+    void addToKind(JSGCTraceKind kind, intptr_t n) {
+        switch (kind) {
+          case JSTRACE_OBJECT:       object += n;      break;
+          case JSTRACE_STRING:       string += n;      break;
+          case JSTRACE_SYMBOL:       symbol += n;      break;
+          case JSTRACE_SCRIPT:       script += n;      break;
+          case JSTRACE_SHAPE:        shape += n;       break;
+          case JSTRACE_BASE_SHAPE:   baseShape += n;   break;
+          case JSTRACE_JITCODE:      jitcode += n;     break;
+          case JSTRACE_LAZY_SCRIPT:  lazyScript += n;  break;
+          case JSTRACE_OBJECT_GROUP: objectGroup += n; break;
+          default:
+            MOZ_CRASH("Bad trace kind for GCThingSizes");
+        }
+    }
+
+    void addSizes(const GCThingSizes &other) {
+        FOR_EACH_SIZE(ADD_OTHER_SIZE)
+    }
+
+    size_t totalSize() const {
+        size_t n = 0;
+        FOR_EACH_SIZE(ADD_SIZE_TO_N)
+        return n;
+    }
+
+    FOR_EACH_SIZE(DECL_SIZE)
+    int dummy;  // present just to absorb the trailing comma from FOR_EACH_SIZE(ZERO_SIZE)
+
+#undef FOR_EACH_SIZE
+};
+
 struct ZoneStats
 {
 #define FOR_EACH_SIZE(macro) \
     macro(Other,   IsLiveGCThing,  symbolsGCHeap) \
     macro(Other,   NotLiveGCThing, gcHeapArenaAdmin) \
-    macro(Other,   NotLiveGCThing, unusedGCThings) \
     macro(Other,   IsLiveGCThing,  lazyScriptsGCHeap) \
     macro(Other,   NotLiveGCThing, lazyScriptsMallocHeap) \
     macro(Other,   IsLiveGCThing,  jitCodesGCHeap) \
     macro(Other,   IsLiveGCThing,  objectGroupsGCHeap) \
     macro(Other,   NotLiveGCThing, objectGroupsMallocHeap) \
     macro(Other,   NotLiveGCThing, typePool) \
     macro(Other,   NotLiveGCThing, baselineStubsOptimized) \
 
     ZoneStats()
       : FOR_EACH_SIZE(ZERO_SIZE)
+        unusedGCThings(),
         stringInfo(),
         extra(),
         allStrings(nullptr),
         notableStrings(),
         isTotals(true)
     {}
 
     ZoneStats(ZoneStats &&other)
       : FOR_EACH_SIZE(COPY_OTHER_SIZE)
+        unusedGCThings(mozilla::Move(other.unusedGCThings)),
         stringInfo(mozilla::Move(other.stringInfo)),
         extra(other.extra),
         allStrings(other.allStrings),
         notableStrings(mozilla::Move(other.notableStrings)),
         isTotals(other.isTotals)
     {
         other.allStrings = nullptr;
         MOZ_ASSERT(!other.isTotals);
@@ -477,38 +533,41 @@ struct ZoneStats
         js_delete(allStrings);
     }
 
     bool initStrings(JSRuntime *rt);
 
     void addSizes(const ZoneStats &other) {
         MOZ_ASSERT(isTotals);
         FOR_EACH_SIZE(ADD_OTHER_SIZE)
+        unusedGCThings.addSizes(other.unusedGCThings);
         stringInfo.add(other.stringInfo);
     }
 
     size_t sizeOfLiveGCThings() const {
         MOZ_ASSERT(isTotals);
         size_t n = 0;
         FOR_EACH_SIZE(ADD_SIZE_TO_N_IF_LIVE_GC_THING)
         n += stringInfo.sizeOfLiveGCThings();
         return n;
     }
 
     void addToTabSizes(JS::TabSizes *sizes) const {
         MOZ_ASSERT(isTotals);
         FOR_EACH_SIZE(ADD_TO_TAB_SIZES)
+        sizes->add(JS::TabSizes::Other, unusedGCThings.totalSize());
         stringInfo.addToTabSizes(sizes);
     }
 
     // These string measurements are initially for all strings.  At the end,
     // if the measurement granularity is FineGrained, we subtract the
     // measurements of the notable script sources and move them into
     // |notableStrings|.
     FOR_EACH_SIZE(DECL_SIZE)
+    GCThingSizes unusedGCThings;
     StringInfo stringInfo;
     void *extra;    // This field can be used by embedders.
 
     typedef js::HashMap<JSString*, StringInfo,
                         js::InefficientNonFlatteningStringHashPolicy,
                         js::SystemAllocPolicy> StringsHashMap;
 
     // |allStrings| is only used transiently.  During the zone traversal it is
--- a/js/src/vm/MemoryMetrics.cpp
+++ b/js/src/vm/MemoryMetrics.cpp
@@ -353,17 +353,17 @@ StatsArenaCallback(JSRuntime *rt, void *
     // end of the header and the start of the first GC thing.
     size_t allocationSpace = arena->thingsSpan(thingSize);
     rtStats->currZoneStats->gcHeapArenaAdmin += gc::ArenaSize - allocationSpace;
 
     // We don't call the callback on unused things.  So we compute the
     // unused space like this:  arenaUnused = maxArenaUnused - arenaUsed.
     // We do this by setting arenaUnused to maxArenaUnused here, and then
     // subtracting thingSize for every used cell, in StatsCellCallback().
-    rtStats->currZoneStats->unusedGCThings += allocationSpace;
+    rtStats->currZoneStats->unusedGCThings.addToKind(traceKind, allocationSpace);
 }
 
 static CompartmentStats *
 GetCompartmentStats(JSCompartment *comp)
 {
     return static_cast<CompartmentStats *>(comp->compartmentStats);
 }
 
@@ -556,17 +556,17 @@ StatsCellCallback(JSRuntime *rt, void *d
         break;
       }
 
       default:
         MOZ_CRASH("invalid traceKind in StatsCellCallback");
     }
 
     // Yes, this is a subtraction:  see StatsArenaCallback() for details.
-    zStats->unusedGCThings -= thingSize;
+    zStats->unusedGCThings.addToKind(traceKind, -thingSize);
 }
 
 bool
 ZoneStats::initStrings(JSRuntime *rt)
 {
     isTotals = false;
     allStrings = rt->new_<StringsHashMap>();
     if (!allStrings || !allStrings->init()) {
@@ -758,17 +758,17 @@ JS::CollectRuntimeStats(JSRuntime *rt, R
     MOZ_ASSERT(!cTotals.allClasses);
 
     rtStats->gcHeapGCThings = rtStats->zTotals.sizeOfLiveGCThings() +
                               rtStats->cTotals.sizeOfLiveGCThings();
 
 #ifdef DEBUG
     // Check that the in-arena measurements look ok.
     size_t totalArenaSize = rtStats->zTotals.gcHeapArenaAdmin +
-                            rtStats->zTotals.unusedGCThings +
+                            rtStats->zTotals.unusedGCThings.totalSize() +
                             rtStats->gcHeapGCThings;
     MOZ_ASSERT(totalArenaSize % gc::ArenaSize == 0);
 #endif
 
     for (CompartmentsIter comp(rt, WithAtoms); !comp.done(); comp.next())
         comp->compartmentStats = nullptr;
 
     size_t numDirtyChunks =
@@ -777,17 +777,17 @@ JS::CollectRuntimeStats(JSRuntime *rt, R
         sizeof(gc::Chunk) - (sizeof(gc::Arena) * gc::ArenasPerChunk);
     rtStats->gcHeapChunkAdmin = numDirtyChunks * perChunkAdmin;
 
     // |gcHeapUnusedArenas| is the only thing left.  Compute it in terms of
     // all the others.  See the comment in RuntimeStats for explanation.
     rtStats->gcHeapUnusedArenas = rtStats->gcHeapChunkTotal -
                                   rtStats->gcHeapDecommittedArenas -
                                   rtStats->gcHeapUnusedChunks -
-                                  rtStats->zTotals.unusedGCThings -
+                                  rtStats->zTotals.unusedGCThings.totalSize() -
                                   rtStats->gcHeapChunkAdmin -
                                   rtStats->zTotals.gcHeapArenaAdmin -
                                   rtStats->gcHeapGCThings;
     return true;
 }
 
 JS_PUBLIC_API(size_t)
 JS::SystemCompartmentCount(JSRuntime *rt)
--- a/js/xpconnect/src/XPCJSRuntime.cpp
+++ b/js/xpconnect/src/XPCJSRuntime.cpp
@@ -1882,16 +1882,29 @@ NS_IMPL_ISUPPORTS(JSMainRuntimeTemporary
         rv = cb->Callback(EmptyCString(), _path,                              \
                           nsIMemoryReporter::_kind,                           \
                           nsIMemoryReporter::UNITS_BYTES, amount,             \
                           NS_LITERAL_CSTRING(_desc), closure);                \
         NS_ENSURE_SUCCESS(rv, rv);                                            \
         rtTotal += amount;                                                    \
     } while (0)
 
+// Report GC thing bytes.
+#define MREPORT_BYTES(_path, _kind, _amount, _desc)                           \
+    do {                                                                      \
+        size_t amount = _amount;  /* evaluate _amount only once */            \
+        nsresult rv;                                                          \
+        rv = cb->Callback(EmptyCString(), _path,                              \
+                          nsIMemoryReporter::_kind,                           \
+                          nsIMemoryReporter::UNITS_BYTES, amount,             \
+                          NS_LITERAL_CSTRING(_desc), closure);                \
+        NS_ENSURE_SUCCESS(rv, rv);                                            \
+        gcThingTotal += amount;                                               \
+    } while (0)
+
 MOZ_DEFINE_MALLOC_SIZE_OF(JSMallocSizeOf)
 
 namespace xpc {
 
 static nsresult
 ReportZoneStats(const JS::ZoneStats &zStats,
                 const xpc::ZoneStatsExtras &extras,
                 nsIMemoryReporterCallback *cb,
@@ -1908,18 +1921,18 @@ ReportZoneStats(const JS::ZoneStats &zSt
         zStats.symbolsGCHeap,
         "Symbols.");
 
     ZCREPORT_GC_BYTES(pathPrefix + NS_LITERAL_CSTRING("gc-heap-arena-admin"),
         zStats.gcHeapArenaAdmin,
         "Bookkeeping information and alignment padding within GC arenas.");
 
     ZCREPORT_GC_BYTES(pathPrefix + NS_LITERAL_CSTRING("unused-gc-things"),
-        zStats.unusedGCThings,
-        "Empty GC thing cells within non-empty arenas.");
+        zStats.unusedGCThings.totalSize(),
+        "Unused GC thing cells within non-empty arenas.");
 
     ZCREPORT_GC_BYTES(pathPrefix + NS_LITERAL_CSTRING("lazy-scripts/gc-heap"),
         zStats.lazyScriptsGCHeap,
         "Scripts that haven't executed yet.");
 
     ZCREPORT_BYTES(pathPrefix + NS_LITERAL_CSTRING("lazy-scripts/malloc-heap"),
         zStats.lazyScriptsMallocHeap,
         "Lazy script tables containing free variables or inner functions.");
@@ -2921,31 +2934,100 @@ JSReporter::CollectReports(WindowPaths *
     REPORT_BYTES(NS_LITERAL_CSTRING("js-main-runtime-gc-heap-committed/unused/chunks"),
         KIND_OTHER, rtStats.gcHeapUnusedChunks,
         "The same as 'explicit/js-non-window/gc-heap/unused-chunks'.");
 
     REPORT_BYTES(NS_LITERAL_CSTRING("js-main-runtime-gc-heap-committed/unused/arenas"),
         KIND_OTHER, rtStats.gcHeapUnusedArenas,
         "The same as 'explicit/js-non-window/gc-heap/unused-arenas'.");
 
-    REPORT_BYTES(NS_LITERAL_CSTRING("js-main-runtime-gc-heap-committed/unused/gc-things"),
-        KIND_OTHER, rtStats.zTotals.unusedGCThings,
-        "The same as 'js-main-runtime/zones/unused-gc-things'.");
+    REPORT_BYTES(NS_LITERAL_CSTRING("js-main-runtime-gc-heap-committed/unused/gc-things/objects"),
+        KIND_OTHER, rtStats.zTotals.unusedGCThings.object,
+        "Unused object cells within non-empty arenas.");
+
+    REPORT_BYTES(NS_LITERAL_CSTRING("js-main-runtime-gc-heap-committed/unused/gc-things/strings"),
+        KIND_OTHER, rtStats.zTotals.unusedGCThings.string,
+        "Unused string cells within non-empty arenas.");
+
+    REPORT_BYTES(NS_LITERAL_CSTRING("js-main-runtime-gc-heap-committed/unused/gc-things/symbols"),
+        KIND_OTHER, rtStats.zTotals.unusedGCThings.symbol,
+        "Unused symbol cells within non-empty arenas.");
+
+    REPORT_BYTES(NS_LITERAL_CSTRING("js-main-runtime-gc-heap-committed/unused/gc-things/shapes"),
+        KIND_OTHER, rtStats.zTotals.unusedGCThings.shape,
+        "Unused shape cells within non-empty arenas.");
+
+    REPORT_BYTES(NS_LITERAL_CSTRING("js-main-runtime-gc-heap-committed/unused/gc-things/base-shapes"),
+        KIND_OTHER, rtStats.zTotals.unusedGCThings.baseShape,
+        "Unused base shape cells within non-empty arenas.");
+
+    REPORT_BYTES(NS_LITERAL_CSTRING("js-main-runtime-gc-heap-committed/unused/gc-things/object-groups"),
+        KIND_OTHER, rtStats.zTotals.unusedGCThings.objectGroup,
+        "Unused object group cells within non-empty arenas.");
+
+    REPORT_BYTES(NS_LITERAL_CSTRING("js-main-runtime-gc-heap-committed/unused/gc-things/scripts"),
+        KIND_OTHER, rtStats.zTotals.unusedGCThings.script,
+        "Unused script cells within non-empty arenas.");
+
+    REPORT_BYTES(NS_LITERAL_CSTRING("js-main-runtime-gc-heap-committed/unused/gc-things/lazy-scripts"),
+        KIND_OTHER, rtStats.zTotals.unusedGCThings.lazyScript,
+        "Unused lazy script cells within non-empty arenas.");
+
+    REPORT_BYTES(NS_LITERAL_CSTRING("js-main-runtime-gc-heap-committed/unused/gc-things/jitcode"),
+        KIND_OTHER, rtStats.zTotals.unusedGCThings.jitcode,
+        "Unused jitcode cells within non-empty arenas.");
 
     REPORT_BYTES(NS_LITERAL_CSTRING("js-main-runtime-gc-heap-committed/used/chunk-admin"),
         KIND_OTHER, rtStats.gcHeapChunkAdmin,
         "The same as 'explicit/js-non-window/gc-heap/chunk-admin'.");
 
     REPORT_BYTES(NS_LITERAL_CSTRING("js-main-runtime-gc-heap-committed/used/arena-admin"),
         KIND_OTHER, rtStats.zTotals.gcHeapArenaAdmin,
         "The same as 'js-main-runtime/zones/gc-heap-arena-admin'.");
 
-    REPORT_BYTES(NS_LITERAL_CSTRING("js-main-runtime-gc-heap-committed/used/gc-things"),
-        KIND_OTHER, rtStats.gcHeapGCThings,
-        "GC things: objects, strings, scripts, etc.");
+    size_t gcThingTotal = 0;
+
+    MREPORT_BYTES(NS_LITERAL_CSTRING("js-main-runtime-gc-heap-committed/used/gc-things/objects"),
+        KIND_OTHER, rtStats.cTotals.classInfo.objectsGCHeap,
+        "Used object cells.");
+
+    MREPORT_BYTES(NS_LITERAL_CSTRING("js-main-runtime-gc-heap-committed/used/gc-things/strings"),
+        KIND_OTHER, rtStats.zTotals.stringInfo.sizeOfLiveGCThings(),
+        "Used string cells.");
+
+    MREPORT_BYTES(NS_LITERAL_CSTRING("js-main-runtime-gc-heap-committed/used/gc-things/symbols"),
+        KIND_OTHER, rtStats.zTotals.symbolsGCHeap,
+        "Used symbol cells.");
+
+    MREPORT_BYTES(NS_LITERAL_CSTRING("js-main-runtime-gc-heap-committed/used/gc-things/shapes"),
+        KIND_OTHER,
+        rtStats.cTotals.classInfo.shapesGCHeapTree + rtStats.cTotals.classInfo.shapesGCHeapDict,
+        "Used shape cells.");
+
+    MREPORT_BYTES(NS_LITERAL_CSTRING("js-main-runtime-gc-heap-committed/used/gc-things/base-shapes"),
+        KIND_OTHER, rtStats.cTotals.classInfo.shapesGCHeapBase,
+        "Used base shape cells.");
+
+    MREPORT_BYTES(NS_LITERAL_CSTRING("js-main-runtime-gc-heap-committed/used/gc-things/object-groups"),
+        KIND_OTHER, rtStats.zTotals.objectGroupsGCHeap,
+        "Used object group cells.");
+
+    MREPORT_BYTES(NS_LITERAL_CSTRING("js-main-runtime-gc-heap-committed/used/gc-things/scripts"),
+        KIND_OTHER, rtStats.cTotals.scriptsGCHeap,
+        "Used script cells.");
+
+    MREPORT_BYTES(NS_LITERAL_CSTRING("js-main-runtime-gc-heap-committed/used/gc-things/lazy-scripts"),
+        KIND_OTHER, rtStats.zTotals.lazyScriptsGCHeap,
+        "Used lazy script cells.");
+
+    MREPORT_BYTES(NS_LITERAL_CSTRING("js-main-runtime-gc-heap-committed/used/gc-things/jitcode"),
+        KIND_OTHER, rtStats.zTotals.jitCodesGCHeap,
+        "Used jitcode cells.");
+
+    MOZ_ASSERT(gcThingTotal == rtStats.gcHeapGCThings);
 
     // Report xpconnect.
 
     REPORT_BYTES(NS_LITERAL_CSTRING("explicit/xpconnect/runtime"),
         KIND_HEAP, xpcJSRuntimeSize,
         "The XPConnect runtime.");
 
     REPORT_BYTES(NS_LITERAL_CSTRING("explicit/xpconnect/wrappedjs"),