Bug 661474 - Add per-compartment memory reporters. r=wmccloskey, dolske.
authorNicholas Nethercote <nnethercote@mozilla.com>
Thu, 30 Jun 2011 15:44:17 +1000
changeset 72069 fb03584dd82353f80836360edbf651131ff4b8af
parent 72068 71cb30dd52b42df8264479e16e3915f125b32e20
child 72070 c2f48684b9f556a7809008d4978764b35761b61f
push idunknown
push userunknown
push dateunknown
reviewerswmccloskey, dolske
bugs661474
milestone7.0a1
Bug 661474 - Add per-compartment memory reporters. r=wmccloskey, dolske.
js/src/jscntxt.h
js/src/jsgc.cpp
js/src/jsgc.h
js/src/jsscript.h
js/src/methodjit/Compiler.cpp
js/src/methodjit/MethodJIT.cpp
js/src/shell/js.cpp
js/src/xpconnect/src/xpcjsruntime.cpp
toolkit/components/aboutmemory/content/aboutMemory.js
toolkit/components/aboutmemory/tests/chrome/test_aboutmemory.xul
toolkit/components/telemetry/TelemetryPing.js
--- a/js/src/jscntxt.h
+++ b/js/src/jscntxt.h
@@ -612,21 +612,16 @@ struct JSRuntime {
     volatile uint32     shapeGen;
 
     /* Literal table maintained by jsatom.c functions. */
     JSAtomState         atomState;
 
     JSWrapObjectCallback wrapObjectCallback;
     JSPreWrapCallback    preWrapObjectCallback;
 
-#ifdef JS_METHODJIT
-    /* This measures the size of JITScripts, native maps and IC structs. */
-    size_t               mjitDataSize;
-#endif
-
     /*
      * To ensure that cx->malloc does not cause a GC, we set this flag during
      * OOM reporting (in js_ReportOutOfMemory). If a GC is requested while
      * reporting the OOM, we ignore it.
      */
     int32               inOOMReport;
 
 #if defined(MOZ_GCTIMER) || defined(JSGC_TESTPILOT)
--- a/js/src/jsgc.cpp
+++ b/js/src/jsgc.cpp
@@ -2724,68 +2724,66 @@ TraceRuntime(JSTracer *trc)
 
     /*
      * Calls from inside a normal GC or a recursive calls are OK and do not
      * require session setup.
      */
     MarkRuntime(trc);
 }
 
-static void
-IterateCompartmentCells(JSContext *cx, JSCompartment *comp, uint64 traceKindMask,
-                        void *data, IterateCallback callback)
+void
+IterateCompartmentsArenasCells(JSContext *cx, void *data,
+                               IterateCompartmentCallback compartmentCallback, 
+                               IterateArenaCallback arenaCallback,
+                               IterateCellCallback cellCallback)
 {
-    for (unsigned thingKind = 0; thingKind < FINALIZE_LIMIT; thingKind++) {
-        size_t traceKind = GetFinalizableTraceKind(thingKind);
-        if (traceKindMask && !TraceKindInMask(traceKind, traceKindMask))
-            continue;
-
-        size_t thingSize = GCThingSizeMap[thingKind];
-        ArenaHeader *aheader = comp->arenas[thingKind].getHead();
-        for (; aheader; aheader = aheader->next) {
-            Arena *a = aheader->getArena();
-            FreeSpan firstSpan(aheader->getFirstFreeSpan());
-            FreeSpan *span = &firstSpan;
-            for (uintptr_t thing = a->thingsStart(thingSize);; thing += thingSize) {
-                JS_ASSERT(thing <= a->thingsEnd());
-                if (thing == span->start) {
-                    if (!span->hasNext())
-                        break;
-                    thing = span->end;
-                    span = span->nextSpan();
-                } else {
-                    (*callback)(cx, data, traceKind, reinterpret_cast<void *>(thing));
-                }
-            }
-        }
-    }
-}
-
-void
-IterateCells(JSContext *cx, JSCompartment *comp, uint64 traceKindMask,
-             void *data, IterateCallback callback)
-{
+    CHECK_REQUEST(cx);
+
     LeaveTrace(cx);
 
     JSRuntime *rt = cx->runtime;
     JS_ASSERT(!rt->gcRunning);
 
     AutoLockGC lock(rt);
     AutoGCSession gcsession(cx);
 #ifdef JS_THREADSAFE
     rt->gcHelperThread.waitBackgroundSweepEnd(rt, false);
 #endif
     AutoUnlockGC unlock(rt);
 
     AutoCopyFreeListToArenas copy(rt);
-    if (comp) {
-        IterateCompartmentCells(cx, comp, traceKindMask, data, callback);
-    } else {
-        for (JSCompartment **c = rt->compartments.begin(); c != rt->compartments.end(); ++c)
-            IterateCompartmentCells(cx, *c, traceKindMask, data, callback);
+    for (JSCompartment **c = rt->compartments.begin(); c != rt->compartments.end(); ++c) {
+        JSCompartment *compartment = *c;
+        (*compartmentCallback)(cx, data, compartment);
+
+        for (unsigned thingKind = 0; thingKind < FINALIZE_LIMIT; thingKind++) {
+            size_t traceKind = GetFinalizableTraceKind(thingKind);
+            size_t thingSize = GCThingSizeMap[thingKind];
+            ArenaHeader *aheader = compartment->arenas[thingKind].getHead();
+
+            for (; aheader; aheader = aheader->next) {
+                Arena *arena = aheader->getArena();
+                (*arenaCallback)(cx, data, arena, traceKind, thingSize);
+                FreeSpan firstSpan(aheader->getFirstFreeSpan());
+                FreeSpan *span = &firstSpan;
+
+                for (uintptr_t thing = arena->thingsStart(thingSize); ; thing += thingSize) {
+                    JS_ASSERT(thing <= arena->thingsEnd());
+                    if (thing == span->start) {
+                        if (!span->hasNext())
+                            break;
+                        thing = span->end;
+                        span = span->nextSpan();
+                    } else {
+                        (*cellCallback)(cx, data, reinterpret_cast<void *>(thing), traceKind,
+                                        thingSize);
+                    }
+                }
+            }
+        }
     }
 }
 
 namespace gc {
 
 JSCompartment *
 NewCompartment(JSContext *cx, JSPrincipals *principals)
 {
--- a/js/src/jsgc.h
+++ b/js/src/jsgc.h
@@ -1327,40 +1327,32 @@ struct GCMarker : public JSTracer {
         if (!xmlStack.push(xml))
             delayMarkingChildren(xml);
     }
 };
 
 void
 MarkStackRangeConservatively(JSTracer *trc, Value *begin, Value *end);
 
-static inline uint64
-TraceKindMask(unsigned kind)
-{
-    return uint64(1) << kind;
-}
-
-static inline bool
-TraceKindInMask(unsigned kind, uint64 mask)
-{
-    return !!(mask & TraceKindMask(kind));
-}
-
-typedef void (*IterateCallback)(JSContext *cx, void *data, size_t traceKind, void *obj);
+typedef void (*IterateCompartmentCallback)(JSContext *cx, void *data, JSCompartment *compartment);
+typedef void (*IterateArenaCallback)(JSContext *cx, void *data, gc::Arena *arena, size_t traceKind,
+                                     size_t thingSize);
+typedef void (*IterateCellCallback)(JSContext *cx, void *data, void *thing, size_t traceKind,
+                                    size_t thingSize);
 
 /*
- * This function calls |callback| on every cell in the GC heap. If |comp| is
- * non-null, then it only selects cells in that compartment. If |traceKindMask|
- * is non-zero, then only cells whose traceKind belongs to the mask will be
- * selected. The mask should be constructed by ORing |TraceKindMask(...)|
- * results.
+ * This function calls |compartmentCallback| on every compartment,
+ * |arenaCallback| on every in-use arena, and |cellCallback| on every in-use
+ * cell in the GC heap.
  */
 extern JS_FRIEND_API(void)
-IterateCells(JSContext *cx, JSCompartment *comp, uint64 traceKindMask,
-             void *data, IterateCallback callback);
+IterateCompartmentsArenasCells(JSContext *cx, void *data,
+                               IterateCompartmentCallback compartmentCallback, 
+                               IterateArenaCallback arenaCallback,
+                               IterateCellCallback cellCallback);
 
 } /* namespace js */
 
 extern void
 js_FinalizeStringRT(JSRuntime *rt, JSString *str);
 
 /*
  * This function is defined in jsdbgapi.cpp but is declared here to avoid
--- a/js/src/jsscript.h
+++ b/js/src/jsscript.h
@@ -570,16 +570,19 @@ struct JSScript {
     JITScriptStatus getJITStatus(bool constructing) {
         void *addr = constructing ? jitArityCheckCtor : jitArityCheckNormal;
         if (addr == NULL)
             return JITScript_None;
         if (addr == JS_UNJITTABLE_SCRIPT)
             return JITScript_Invalid;
         return JITScript_Valid;
     }
+
+    // This method is implemented in MethodJIT.h.
+    JS_FRIEND_API(size_t) jitDataSize();/* Size of the JITScript and all sections */
 #endif
 
     JS_FRIEND_API(size_t) totalSize();  /* Size of the JSScript and all sections */
     uint32 numNotes();                  /* Number of srcnote slots in the srcnotes section */
 
     /* Script notes are allocated right after the code. */
     jssrcnote *notes() { return (jssrcnote *)(code + length); }
 
--- a/js/src/methodjit/Compiler.cpp
+++ b/js/src/methodjit/Compiler.cpp
@@ -811,19 +811,16 @@ mjit::Compiler::finishThisUp(JITScript *
                             : from.returnOffset;
         to.initialize(codeOffset, from.pc - script->code, from.id);
     }
 
     JS_ASSERT(size_t(cursor - (uint8*)jit) == dataSize);
 
     *jitp = jit;
 
-    /* We tolerate a race in the stats. */
-    cx->runtime->mjitDataSize += dataSize;
-
     return Compile_Okay;
 }
 
 class SrcNoteLineScanner {
     ptrdiff_t offset;
     jssrcnote *sn;
 
 public:
--- a/js/src/methodjit/MethodJIT.cpp
+++ b/js/src/methodjit/MethodJIT.cpp
@@ -870,16 +870,27 @@ mjit::JITScript::~JITScript()
     }
     
     ic::CallICInfo *callICs_ = callICs();
     for (uint32 i = 0; i < nCallICs; i++)
         callICs_[i].releasePools();
 #endif
 }
 
+size_t
+JSScript::jitDataSize()
+{
+    size_t n = 0;
+    if (jitNormal)
+        n += jitNormal->scriptDataSize(); 
+    if (jitCtor)
+        n += jitCtor->scriptDataSize(); 
+    return n;
+}
+
 /* Please keep in sync with Compiler::finishThisUp! */
 size_t
 mjit::JITScript::scriptDataSize()
 {
     return sizeof(JITScript) +
         sizeof(NativeMapEntry) * nNmapPairs +
 #if defined JS_MONOIC
         sizeof(ic::GetGlobalNameIC) * nGetGlobalNames +
@@ -900,27 +911,23 @@ void
 mjit::ReleaseScriptCode(JSContext *cx, JSScript *script)
 {
     // NB: The recompiler may call ReleaseScriptCode, in which case it
     // will get called again when the script is destroyed, so we
     // must protect against calling ReleaseScriptCode twice.
     JITScript *jscr;
 
     if ((jscr = script->jitNormal)) {
-        cx->runtime->mjitDataSize -= jscr->scriptDataSize();
-
         jscr->~JITScript();
         cx->free_(jscr);
         script->jitNormal = NULL;
         script->jitArityCheckNormal = NULL;
     }
 
     if ((jscr = script->jitCtor)) {
-        cx->runtime->mjitDataSize -= jscr->scriptDataSize();
-
         jscr->~JITScript();
         cx->free_(jscr);
         script->jitCtor = NULL;
         script->jitArityCheckCtor = NULL;
     }
 }
 
 #ifdef JS_METHODJIT_PROFILE_STUBS
--- a/js/src/shell/js.cpp
+++ b/js/src/shell/js.cpp
@@ -4710,17 +4710,28 @@ MJitCodeStats(JSContext *cx, uintN argc,
 #endif
     return true;
 }
 
 JSBool
 MJitDataStats(JSContext *cx, uintN argc, jsval *vp)
 {
 #ifdef JS_METHODJIT
-     JS_SET_RVAL(cx, vp, INT_TO_JSVAL(cx->runtime->mjitDataSize));
+    JSRuntime *rt = cx->runtime;
+    AutoLockGC lock(rt);
+    size_t n = 0;
+    for (JSCompartment **c = rt->compartments.begin(); c != rt->compartments.end(); ++c) {
+        for (JSScript *script = (JSScript *)(*c)->scripts.next;
+             &script->links != &(*c)->scripts;
+             script = (JSScript *)script->links.next)
+        {
+            n += script->jitDataSize(); 
+        }
+    }
+    JS_SET_RVAL(cx, vp, INT_TO_JSVAL(n));
 #else
     JS_SET_RVAL(cx, vp, JSVAL_VOID);
 #endif
     return true;
 }
 
 JSBool
 StringStats(JSContext *cx, uintN argc, jsval *vp)
--- a/js/src/xpconnect/src/xpcjsruntime.cpp
+++ b/js/src/xpconnect/src/xpcjsruntime.cpp
@@ -1254,292 +1254,422 @@ private:
 
 protected:
     PRUint32 mNumGCChunksInUse;
 };
 
 static XPConnectGCChunkAllocator gXPCJSChunkAllocator;
 
 #ifdef MOZ_MEMORY
-#define JS_GC_HEAP_KIND  KIND_HEAP
+#define JS_GC_HEAP_KIND  nsIMemoryReporter::KIND_HEAP
 #else
-#define JS_GC_HEAP_KIND  KIND_MAPPED
+#define JS_GC_HEAP_KIND  nsIMemoryReporter::KIND_MAPPED
 #endif
 
+// We have per-compartment GC heap totals, so we can't put the total GC heap
+// size in the explicit allocations tree.  But it's a useful figure, so put it
+// in the "others" list.
 NS_MEMORY_REPORTER_IMPLEMENT(XPConnectJSGCHeap,
-    "explicit/js/gc-heap",
-    JS_GC_HEAP_KIND,
-    UNITS_BYTES,
+    "js-gc-heap",
+    KIND_OTHER,
+    nsIMemoryReporter::UNITS_BYTES,
     gXPCJSChunkAllocator.GetGCChunkBytesInUse,
     "Memory used by the garbage-collected JavaScript heap.")
 
 static PRInt64
-GetPerCompartmentSize(PRInt64 (*f)(JSCompartment *c))
-{
-    JSRuntime *rt = nsXPConnect::GetRuntimeInstance()->GetJSRuntime();
-    js::AutoLockGC lock(rt);
-    PRInt64 n = 0;
-    for (JSCompartment **c = rt->compartments.begin(); c != rt->compartments.end(); ++c)
-        n += f(*c);
-    return n;
-}
-
-static PRInt64
 GetJSStack()
 {
     JSRuntime *rt = nsXPConnect::GetRuntimeInstance()->GetJSRuntime();
     PRInt64 n = 0;
     for (js::ThreadDataIter i(rt); !i.empty(); i.popFront())
         n += i.threadData()->stackSpace.committedSize();
     return n;
 }
 
 NS_MEMORY_REPORTER_IMPLEMENT(XPConnectJSStack,
     "explicit/js/stack",
     KIND_MAPPED,
-    UNITS_BYTES,
+    nsIMemoryReporter::UNITS_BYTES,
     GetJSStack,
     "Memory used for the JavaScript stack.  This is the committed portion "
     "of the stack;  any uncommitted portion is not measured because it "
     "hardly costs anything.")
 
-static PRInt64
-GetCompartmentScriptsSize(JSCompartment *c)
+class XPConnectJSCompartmentsMultiReporter : public nsIMemoryMultiReporter
 {
-    PRInt64 n = 0;
-    for (JSScript *script = (JSScript *)c->scripts.next;
-         &script->links != &c->scripts;
-         script = (JSScript *)script->links.next)
+private:
+    struct CompartmentStats
     {
-        n += script->totalSize(); 
-    }
-    return n;
-}
+        CompartmentStats(JSContext *cx, JSCompartment *c) {
+            memset(this, 0, sizeof(*this));
+
+            if (c == cx->runtime->atomsCompartment) {
+                name = NS_LITERAL_CSTRING("atoms");
+            } else if (c->principals) {
+                if (c->principals->codebase) {
+                    // A hack: replace forward slashes with '\\' so they aren't
+                    // treated as path separators.  Users of the reporters
+                    // (such as about:memory) have to undo this change.
+                    name.Assign(c->principals->codebase);
+                    char* cur = name.BeginWriting();
+                    char* end = name.EndWriting();
+                    for (; cur < end; ++cur) {
+                        if ('/' == *cur) {
+                            *cur = '\\';
+                        }
+                    }
+                } else {
+                    name = NS_LITERAL_CSTRING("null-codebase");
+                }
+            } else {
+                name = NS_LITERAL_CSTRING("null-principal");
+            }
+        }
 
-static PRInt64
-GetJSScripts()
-{
-    return GetPerCompartmentSize(GetCompartmentScriptsSize);
-}
+        nsCString name;
+        PRInt64 gcHeapArenaHeaders;
+        PRInt64 gcHeapArenaPadding;
+        PRInt64 gcHeapArenaUnused;
+
+        PRInt64 gcHeapObjects;
+        PRInt64 gcHeapStrings;
+        PRInt64 gcHeapShapes;
+        PRInt64 gcHeapXml;
+
+        PRInt64 objectSlots;
+        PRInt64 stringChars;
 
-struct PRInt64Data {
-    PRInt64Data() : n(0) { }
-    PRInt64 n;
-};
+        PRInt64 scripts;
+#ifdef JS_METHODJIT
+        PRInt64 mjitCode;
+        PRInt64 mjitData;
+#endif
+#ifdef JS_TRACER
+        PRInt64 tjitCode;
+        PRInt64 tjitDataAllocatorsMain;
+        PRInt64 tjitDataAllocatorsReserve;
+#endif
+    };
+
+    struct IterateData
+    {
+        IterateData(JSRuntime *rt)
+        : compartmentStatsVector()
+        , currCompartmentStats(NULL)
+        {
+            compartmentStatsVector.reserve(rt->compartments.length());
+        }
 
-// This function is miscompiled with MSVC 2005 when PGO is on.  See bug 664647.
-#ifdef _MSC_VER
-#pragma optimize("", off)
-#endif
-void
-GetJSObjectSlotsCallback(JSContext *cx, void *v, size_t traceKind, void *thing)
-{
-    JS_ASSERT(traceKind == JSTRACE_OBJECT);
-    JSObject *obj = (JSObject *)thing;
-    if (obj->hasSlotsArray()) {
-        PRInt64Data *data = (PRInt64Data *) v;
-        data->n += obj->numSlots() * sizeof(js::Value);
+        js::Vector<CompartmentStats, 0, js::SystemAllocPolicy> compartmentStatsVector;
+        CompartmentStats *currCompartmentStats;
+    };
+
+    static PRInt64
+    GetCompartmentScriptsSize(JSCompartment *c)
+    {
+        PRInt64 n = 0;
+        for (JSScript *script = (JSScript *)c->scripts.next;
+             &script->links != &c->scripts;
+             script = (JSScript *)script->links.next)
+        {
+            n += script->totalSize(); 
+        }
+        return n;
+    }
+
+    #ifdef JS_METHODJIT
+
+    static PRInt64
+    GetCompartmentMjitCodeSize(JSCompartment *c)
+    {
+        return c->getMjitCodeSize();
     }
-}
-#ifdef _MSC_VER
-#pragma optimize("", on)
-#endif
+
+    static PRInt64
+    GetCompartmentMjitDataSize(JSCompartment *c)
+    {
+        PRInt64 n = 0;
+        for (JSScript *script = (JSScript *)c->scripts.next;
+             &script->links != &c->scripts;
+             script = (JSScript *)script->links.next)
+        {
+            n += script->jitDataSize(); 
+        }
+        return n;
+    }
+
+    #endif  // JS_METHODJIT
+
+    #ifdef JS_TRACER
 
-static PRInt64
-GetJSObjectSlots()
-{
-    JSRuntime *rt = nsXPConnect::GetRuntimeInstance()->GetJSRuntime();
-    JSContext *cx = JS_NewContext(rt, 0);
-    if (!cx) {
-        NS_ERROR("couldn't create context for memory tracing");
-        return (PRInt64) -1;
+    static PRInt64
+    GetCompartmentTjitCodeSize(JSCompartment *c)
+    {
+        if (c->hasTraceMonitor()) {
+            size_t total, frag_size, free_size;
+            c->traceMonitor()->getCodeAllocStats(total, frag_size, free_size);
+            return total;
+        }
+        return 0;
+    }
+
+    static PRInt64
+    GetCompartmentTjitDataAllocatorsMainSize(JSCompartment *c)
+    {
+        return c->hasTraceMonitor()
+             ? c->traceMonitor()->getVMAllocatorsMainSize()
+             : 0;
+    }
+
+    static PRInt64
+    GetCompartmentTjitDataAllocatorsReserveSize(JSCompartment *c)
+    {
+        return c->hasTraceMonitor()
+             ? c->traceMonitor()->getVMAllocatorsReserveSize()
+             : 0;
     }
 
-    PRInt64Data data;
-    js::IterateCells(cx, NULL, js::TraceKindMask(JSTRACE_OBJECT), &data,
-                     *GetJSObjectSlotsCallback);
-
-    JS_DestroyContextNoGC(cx);
+    #endif  // JS_TRACER
 
-    return data.n;
-}
+    static void
+    CompartmentCallback(JSContext *cx, void *vdata, JSCompartment *compartment)
+    {
+        // Append a new CompartmentStats to the vector.
+        IterateData *data = static_cast<IterateData *>(vdata);
+        CompartmentStats compartmentStats(cx, compartment);
+        data->compartmentStatsVector.infallibleAppend(compartmentStats);
+        CompartmentStats *curr = data->compartmentStatsVector.end() - 1;
+        data->currCompartmentStats = curr;
 
-// This function is miscompiled with MSVC 2005 when PGO is on.  See bug 664647.
-#ifdef _MSC_VER
-#pragma optimize("", off)
+        // Get the compartment-level numbers.
+        curr->scripts = GetCompartmentScriptsSize(compartment);
+#ifdef JS_METHODJIT
+        curr->mjitCode = GetCompartmentMjitCodeSize(compartment);
+        curr->mjitData = GetCompartmentMjitDataSize(compartment);
+#endif
+#ifdef JS_TRACER
+        curr->tjitCode = GetCompartmentTjitCodeSize(compartment);
+        curr->tjitDataAllocatorsMain = GetCompartmentTjitDataAllocatorsMainSize(compartment);
+        curr->tjitDataAllocatorsReserve = GetCompartmentTjitDataAllocatorsReserveSize(compartment);
 #endif
-void
-GetJSStringCharsCallback(JSContext *cx, void *v, size_t traceKind, void *thing)
-{
-    JS_ASSERT(traceKind == JSTRACE_STRING);
-    JSString *str = (JSString *)thing;
-    PRInt64Data *data = (PRInt64Data *) v;
-    data->n += str->charsHeapSize();
-}
-#ifdef _MSC_VER
-#pragma optimize("", on)
-#endif
- 
-static PRInt64
-GetJSStringChars()
-{
-    JSRuntime *rt = nsXPConnect::GetRuntimeInstance()->GetJSRuntime();
-    JSContext *cx = JS_NewContext(rt, 0);
-    if (!cx) {
-        NS_ERROR("couldn't create context for memory tracing");
-        return (PRInt64) -1;
+    }
+
+    static void
+    ArenaCallback(JSContext *cx, void *vdata, js::gc::Arena *arena,
+                  size_t traceKind, size_t thingSize)
+    {
+        IterateData *data = static_cast<IterateData *>(vdata);
+        data->currCompartmentStats->gcHeapArenaHeaders +=
+            sizeof(js::gc::ArenaHeader);
+        data->currCompartmentStats->gcHeapArenaPadding +=
+            arena->thingsStartOffset(thingSize) - sizeof(js::gc::ArenaHeader);
+        // 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 CellCallback().
+        data->currCompartmentStats->gcHeapArenaUnused += arena->thingsSpan(thingSize);
+    }
+
+    static void
+    CellCallback(JSContext *cx, void *vdata, void *thing, size_t traceKind,
+                 size_t thingSize)
+    {
+        IterateData *data = static_cast<IterateData *>(vdata);
+        CompartmentStats *curr = data->currCompartmentStats;
+        if (traceKind == JSTRACE_OBJECT) {
+            JSObject *obj = static_cast<JSObject *>(thing);
+            curr->gcHeapObjects += thingSize;
+            if (obj->hasSlotsArray()) {
+                curr->objectSlots += obj->numSlots() * sizeof(js::Value);
+            }
+        } else if (traceKind == JSTRACE_STRING) {
+            JSString *str = static_cast<JSString *>(thing);
+            curr->gcHeapStrings += thingSize;
+            curr->stringChars += str->charsHeapSize();
+        } else if (traceKind == JSTRACE_SHAPE) {
+            curr->gcHeapShapes += thingSize;
+        } else {
+            JS_ASSERT(traceKind == JSTRACE_XML);
+            curr->gcHeapXml += thingSize;
+        }
+        // Yes, this is a subtraction:  see ArenaCallback() for details.
+        curr->gcHeapArenaUnused -= thingSize;
+    }
+
+public:
+    NS_DECL_ISUPPORTS
+
+    XPConnectJSCompartmentsMultiReporter()
+    {
+    }
+
+    nsCString mkPath(const nsACString &compartmentName,
+                     const char* reporterName)
+    {
+        nsCString path(NS_LITERAL_CSTRING("explicit/js/compartment("));
+        path += compartmentName;
+        path += NS_LITERAL_CSTRING(")/");
+        path += nsDependentCString(reporterName);
+        return path;
     }
 
-    PRInt64Data data;
-    js::IterateCells(cx, NULL, js::TraceKindMask(JSTRACE_STRING), &data,
-                     *GetJSStringCharsCallback);
+    NS_IMETHOD CollectReports(nsIMemoryMultiReporterCallback *callback,
+                              nsISupports *closure)
+    {
+        JSRuntime *rt = nsXPConnect::GetRuntimeInstance()->GetJSRuntime();
+        IterateData data(rt);
 
-    JS_DestroyContextNoGC(cx);
+        // In the first step we get all the stats and stash them in a local
+        // data structure.  In the second step we pass all the stashed stats to
+        // the callback.  Separating these steps is important because the
+        // callback may be a JS function, and executing JS while getting these
+        // stats seems like a bad idea.
+        {
+            JSContext *cx = JS_NewContext(rt, 0);
+            if (!cx) {
+                NS_ERROR("couldn't create context for memory tracing");
+                return NS_ERROR_FAILURE;
+            }
+            JS_BeginRequest(cx);
+            js::IterateCompartmentsArenasCells(cx, &data, CompartmentCallback, ArenaCallback,
+                                               CellCallback);
+            JS_EndRequest(cx);
+            JS_DestroyContextNoGC(cx);
+        }
 
-    return data.n;
-}
+        NS_NAMED_LITERAL_CSTRING(p, "");
+
+        PRInt64 gcHeapChunkTotal = gXPCJSChunkAllocator.GetGCChunkBytesInUse();
+        // This is initialized to gcHeapChunkUnused, and then we subtract used
+        // space from it each time around the loop.
+        PRInt64 gcHeapChunkUnused = gcHeapChunkTotal;
+
+        #define DO(path, kind, amount, desc) \
+            callback->Callback(p, path, kind, nsIMemoryReporter::UNITS_BYTES, \
+                               amount, NS_LITERAL_CSTRING(desc), closure);
+
+        // This is the second step (see above).
+        for (CompartmentStats *stats = data.compartmentStatsVector.begin();
+             stats != data.compartmentStatsVector.end();
+             ++stats)
+        {
+            nsCString &name = stats->name;
 
-NS_MEMORY_REPORTER_IMPLEMENT(XPConnectJSScripts,
-    "explicit/js/scripts",
-    KIND_HEAP,
-    UNITS_BYTES,
-    GetJSScripts,
-    "Memory allocated for JSScripts.  A JSScript is created for each "
-    "user-defined function in a script.  One is also created for "
-    "the top-level code in a script.  Each JSScript includes byte-code and "
-    "various other things.")
+            gcHeapChunkUnused -=
+                stats->gcHeapArenaHeaders + stats->gcHeapArenaPadding +
+                stats->gcHeapArenaUnused +
+                stats->gcHeapObjects + stats->gcHeapStrings +
+                stats->gcHeapShapes + stats->gcHeapXml;
+
+            DO(mkPath(name, "gc-heap/arena-headers"),
+               JS_GC_HEAP_KIND, stats->gcHeapArenaHeaders,
+    "Memory on the garbage-collected JavaScript heap, within arenas, that is "
+    "used to hold internal book-keeping information.");
+
+            DO(mkPath(name, "gc-heap/arena-padding"),
+               JS_GC_HEAP_KIND, stats->gcHeapArenaPadding,
+    "Memory on the garbage-collected JavaScript heap, within arenas, that is "
+    "unused and present only so that other data is aligned.");
+
+            DO(mkPath(name, "gc-heap/arena-unused"),
+               JS_GC_HEAP_KIND, stats->gcHeapArenaUnused,
+    "Memory on the garbage-collected JavaScript heap, within arenas, that "
+    "could be holding useful data but currently isn't.");
 
-NS_MEMORY_REPORTER_IMPLEMENT(XPConnectJSObjectSlots,
-    "explicit/js/object-slots",
-    KIND_HEAP,
-    UNITS_BYTES,
-    GetJSObjectSlots,
+            DO(mkPath(name, "gc-heap/objects"),
+               JS_GC_HEAP_KIND, stats->gcHeapObjects,
+    "Memory on the garbage-collected JavaScript heap that holds objects.");
+
+            DO(mkPath(name, "gc-heap/strings"),
+               JS_GC_HEAP_KIND, stats->gcHeapStrings,
+    "Memory on the garbage-collected JavaScript heap that holds string "
+    "headers.");
+
+            DO(mkPath(name, "gc-heap/shapes"),
+               JS_GC_HEAP_KIND, stats->gcHeapShapes,
+    "Memory on the garbage-collected JavaScript heap that holds shapes. "
+    "A shape is an internal data structure that makes property accesses "
+    "fast.");
+
+            DO(mkPath(name, "gc-heap/xml"),
+               JS_GC_HEAP_KIND, stats->gcHeapXml,
+    "Memory on the garbage-collected JavaScript heap that holds E4X XML "
+    "objects.");
+
+            DO(mkPath(name, "object-slots"),
+               nsIMemoryReporter::KIND_HEAP, stats->objectSlots,
     "Memory allocated for non-fixed object slot arrays, which are used "
     "to represent object properties.  Some objects also contain a fixed "
     "number of slots which are stored on the JavaScript heap;  those slots "
-    "are not counted here.")
+    "are not counted here, but in 'gc-heap/objects'.");
 
-NS_MEMORY_REPORTER_IMPLEMENT(XPConnectJSStringChars,
-    "explicit/js/string-chars",
-    KIND_HEAP,
-    UNITS_BYTES,
-    GetJSStringChars,
+            DO(mkPath(name, "string-chars"),
+               nsIMemoryReporter::KIND_HEAP, stats->stringChars,
     "Memory allocated to hold string characters.  Not all of this allocated "
     "memory is necessarily used to hold characters.  Each string also "
     "includes a header which is stored on the JavaScript heap;  that header "
-    "is not counted here.")
+    "is not counted here, but in 'gc-heap/strings'.");
+
+            DO(mkPath(name, "scripts"),
+               nsIMemoryReporter::KIND_HEAP, stats->scripts,
+    "Memory allocated for JSScripts.  A JSScript is created for each "
+    "user-defined function in a script.  One is also created for "
+    "the top-level code in a script.  Each JSScript includes byte-code and "
+    "various other things.");
 
 #ifdef JS_METHODJIT
-
-static PRInt64
-GetCompartmentMjitCodeSize(JSCompartment *c)
-{
-    return c->getMjitCodeSize();
-}
-
-static PRInt64
-GetJSMjitCode()
-{
-    return GetPerCompartmentSize(GetCompartmentMjitCodeSize);
-}
-
-static PRInt64
-GetJSMJitData()
-{
-    JSRuntime *rt = nsXPConnect::GetRuntimeInstance()->GetJSRuntime();
-    return rt->mjitDataSize;
-}
+            DO(mkPath(name, "mjit-code"),
+               nsIMemoryReporter::KIND_MAPPED, stats->mjitCode,
+    "Memory used by the method JIT to hold generated code.");
 
-NS_MEMORY_REPORTER_IMPLEMENT(XPConnectJSMjitCode,
-    "explicit/js/mjit-code",
-    KIND_MAPPED,
-    UNITS_BYTES,
-    GetJSMjitCode,
-    "Memory used by the method JIT to hold generated code.")
-
-NS_MEMORY_REPORTER_IMPLEMENT(XPConnectJSMjitData,
-    "explicit/js/mjit-data",
-    KIND_HEAP,
-    UNITS_BYTES,
-    GetJSMJitData,
+            DO(mkPath(name, "mjit-data"),
+               nsIMemoryReporter::KIND_HEAP, stats->mjitData,
     "Memory used by the method JIT for the following data: "
-    "JITScripts, native maps, and inline cache structs.")
+    "JITScripts, native maps, and inline cache structs.");
+#endif
+#ifdef JS_TRACER
+            DO(mkPath(name, "tjit-code"),
+               nsIMemoryReporter::KIND_MAPPED, stats->tjitCode,
+    "Memory used by the trace JIT to hold generated code.");
 
-#endif  // JS_METHODJIT
-
-#ifdef JS_TRACER
+            DO(mkPath(name, "tjit-data/allocators-main"),
+               nsIMemoryReporter::KIND_HEAP, stats->tjitDataAllocatorsMain,
+    "Memory used by the trace JIT's VMAllocators.");
 
-static PRInt64
-GetCompartmentTjitCode(JSCompartment *c)
-{
-    if (c->hasTraceMonitor()) {
-        size_t total, frag_size, free_size;
-        c->traceMonitor()->getCodeAllocStats(total, frag_size, free_size);
-        return total;
-    }
-    return 0;
-}
+            DO(mkPath(name, "tjit-data/allocators-reserve"),
+               nsIMemoryReporter::KIND_HEAP, stats->tjitDataAllocatorsReserve,
+    "Memory used by the trace JIT and held in reserve for VMAllocators "
+    "in case of OOM.");
+#endif
+        }
 
-static PRInt64
-GetCompartmentTjitDataAllocatorsMain(JSCompartment *c)
-{
-    return c->hasTraceMonitor()
-         ? c->traceMonitor()->getVMAllocatorsMainSize()
-         : 0;
-}
+        JS_ASSERT(gcHeapChunkTotal % js::GC_CHUNK_SIZE == 0);
+        size_t numChunks = gcHeapChunkTotal / js::GC_CHUNK_SIZE;
+        PRInt64 perChunkAdmin =
+            sizeof(js::gc::Chunk) - (sizeof(js::gc::Arena) * js::gc::ArenasPerChunk);
+        PRInt64 gcHeapChunkAdmin = numChunks * perChunkAdmin;
+        gcHeapChunkUnused -= gcHeapChunkAdmin;
 
-static PRInt64
-GetCompartmentTjitDataAllocatorsReserve(JSCompartment *c)
-{
-    return c->hasTraceMonitor()
-         ? c->traceMonitor()->getVMAllocatorsReserveSize()
-         : 0;
-}
-
-static PRInt64
-GetJSTjitCode()
-{
-    return GetPerCompartmentSize(GetCompartmentTjitCode);
-}
-
-static PRInt64
-GetJSTjitDataAllocatorsMain()
-{
-    return GetPerCompartmentSize(GetCompartmentTjitDataAllocatorsMain);
-}
+        DO(NS_LITERAL_CSTRING("explicit/js/gc-heap-chunk-unused"),
+           JS_GC_HEAP_KIND, gcHeapChunkUnused,
+    "Memory on the garbage-collected JavaScript heap, within chunks, that "
+    "could be holding useful data but currently isn't.");
 
-static PRInt64
-GetJSTjitDataAllocatorsReserve()
-{
-    return GetPerCompartmentSize(GetCompartmentTjitDataAllocatorsReserve);
-}
-
-NS_MEMORY_REPORTER_IMPLEMENT(XPConnectJSTjitCode,
-    "explicit/js/tjit-code",
-    KIND_MAPPED,
-    UNITS_BYTES,
-    GetJSTjitCode,
-    "Memory used by the trace JIT to hold generated code.")
+        DO(NS_LITERAL_CSTRING("explicit/js/gc-heap-chunk-admin"),
+           JS_GC_HEAP_KIND, gcHeapChunkAdmin,
+    "Memory on the garbage-collected JavaScript heap, within chunks, that is "
+    "used to hold internal book-keeping information.");
 
-NS_MEMORY_REPORTER_IMPLEMENT(XPConnectJSTjitDataAllocatorsMain,
-    "explicit/js/tjit-data/allocators-main",
-    KIND_HEAP,
-    UNITS_BYTES,
-    GetJSTjitDataAllocatorsMain,
-    "Memory used by the trace JIT's VMAllocators.")
-
-NS_MEMORY_REPORTER_IMPLEMENT(XPConnectJSTjitDataAllocatorsReserve,
-    "explicit/js/tjit-data/allocators-reserve",
-    KIND_HEAP,
-    UNITS_BYTES,
-    GetJSTjitDataAllocatorsReserve,
-    "Memory used by the trace JIT and held in reserve for VMAllocators "
-    "in case of OOM.")
-
-#endif  // JS_TRACER
+        return NS_OK;
+    }
+};
+NS_IMPL_THREADSAFE_ISUPPORTS1(
+  XPConnectJSCompartmentsMultiReporter
+, nsIMemoryMultiReporter
+)
 
 XPCJSRuntime::XPCJSRuntime(nsXPConnect* aXPConnect)
  : mXPConnect(aXPConnect),
    mJSRuntime(nsnull),
    mWrappedJSMap(JSObject2WrappedJSMap::newMap(XPC_JS_MAP_SIZE)),
    mWrappedJSClassMap(IID2WrappedJSClassMap::newMap(XPC_JS_CLASS_MAP_SIZE)),
    mIID2NativeInterfaceMap(IID2NativeInterfaceMap::newMap(XPC_NATIVE_INTERFACE_MAP_SIZE)),
    mClassInfo2NativeSetMap(ClassInfo2NativeSetMap::newMap(XPC_NATIVE_SET_MAP_SIZE)),
@@ -1598,28 +1728,17 @@ XPCJSRuntime::XPCJSRuntime(nsXPConnect* 
             NS_RUNTIMEABORT("JS_NEW_CONDVAR failed.");
 
         mJSRuntime->setActivityCallback(ActivityCallback, this);
 
         mJSRuntime->setCustomGCChunkAllocator(&gXPCJSChunkAllocator);
 
         NS_RegisterMemoryReporter(new NS_MEMORY_REPORTER_NAME(XPConnectJSGCHeap));
         NS_RegisterMemoryReporter(new NS_MEMORY_REPORTER_NAME(XPConnectJSStack));
-        NS_RegisterMemoryReporter(new NS_MEMORY_REPORTER_NAME(XPConnectJSScripts));
-        NS_RegisterMemoryReporter(new NS_MEMORY_REPORTER_NAME(XPConnectJSObjectSlots));
-        NS_RegisterMemoryReporter(new NS_MEMORY_REPORTER_NAME(XPConnectJSStringChars));
-#ifdef JS_METHODJIT
-        NS_RegisterMemoryReporter(new NS_MEMORY_REPORTER_NAME(XPConnectJSMjitCode));
-        NS_RegisterMemoryReporter(new NS_MEMORY_REPORTER_NAME(XPConnectJSMjitData));
-#endif
-#ifdef JS_TRACER
-        NS_RegisterMemoryReporter(new NS_MEMORY_REPORTER_NAME(XPConnectJSTjitCode));
-        NS_RegisterMemoryReporter(new NS_MEMORY_REPORTER_NAME(XPConnectJSTjitDataAllocatorsMain));
-        NS_RegisterMemoryReporter(new NS_MEMORY_REPORTER_NAME(XPConnectJSTjitDataAllocatorsReserve));
-#endif
+        NS_RegisterMemoryMultiReporter(new XPConnectJSCompartmentsMultiReporter);
     }
 
     if(!JS_DHashTableInit(&mJSHolders, JS_DHashGetStubOps(), nsnull,
                           sizeof(ObjectHolder), 512))
         mJSHolders.ops = nsnull;
 
     mCompartmentMap.Init();
     mMTCompartmentMap.Init();
--- a/toolkit/components/aboutmemory/content/aboutMemory.js
+++ b/toolkit/components/aboutmemory/content/aboutMemory.js
@@ -238,26 +238,25 @@ function update()
         : "<span class='option'><a href='about:memory?verbose'>More verbose</a></span>";
   text += "</div>";
 
   text += "<div>" +
           "<span class='legend'>Hover the pointer over the name of a memory " +
           "reporter to see a detailed description of what it measures.</span>"
           "</div>";
 
-
   var div = document.createElement("div");
   div.innerHTML = text;
   content.appendChild(div);
 }
 
 // Compare two memory reporter nodes.  We want to group together measurements
 // with the same units, so sort first by the nodes' _units field, then sort by
 // the amount if the units are equal.
-function cmp_amount(a, b)
+function cmpAmount(a, b)
 {
   if (a._units != b._units)
     return a._units - b._units;   // use the enum order from nsIMemoryReporter
   else
     return b._amount - a._amount;
 };
 
 /**
@@ -446,40 +445,45 @@ function genProcessText(aProcess, aRepor
      * Sort all kid nodes from largest to smallest and aggregate
      * insignificant nodes.
      *
      * @param aT
      *        The tree
      */
     function filterTree(aT)
     {
-      aT._kids.sort(cmp_amount);
+      aT._kids.sort(cmpAmount);
 
       for (var i = 0; i < aT._kids.length; i++) {
         if (shouldOmit(aT._kids[i]._amount)) {
           // This sub-tree is below the significance threshold
           // Remove it and all remaining (smaller) sub-trees, and
           // replace them with a single aggregate node.
           var i0 = i;
           var aggBytes = 0;
-          var aggNames = [];
           for ( ; i < aT._kids.length; i++) {
             aggBytes += aT._kids[i]._amount;
-            aggNames.push(aT._kids[i]._name);
           }
           aT._kids.splice(i0);
           var n = i - i0;
           var rSub = {
             _name: "(" + n + " omitted)",
             _kind: KIND_OTHER,
-            _description: "Omitted sub-trees: " + aggNames.join(", ") + ".",
             _amount: aggBytes,
+            _description: n + " sub-trees that were below the " + 
+                          omitThresholdPerc + "% significance threshold.  " +
+                          "Click 'More verbose' at the bottom of this page " +
+                          "to see them.",
             _kids: []
           };
+          // Add the "omitted" sub-tree at the end and then resort, because the
+          // sum of the omitted sub-trees may be larger than some of the
+          // shown sub-trees.
           aT._kids[i0] = rSub;
+          aT._kids.sort(cmpAmount);
           break;
         }
         filterTree(aT._kids[i]);
       }
     }
     filterTree(t);
 
     return t;
@@ -643,28 +647,62 @@ function kindToString(aKind)
    case KIND_HEAP:   return "(Heap) ";
    case KIND_OTHER:  return "";
    default:          return "(???) ";
   }
 }
 
 function escapeQuotes(aStr)
 {
-  return aStr.replace(/'/g, '&#39;');
+  return aStr.replace(/\&/g, '&amp;').replace(/'/g, '&#39;');
+}
+
+// For user-controlled strings.
+function escapeAll(aStr)
+{
+  return aStr.replace(/\&/g, '&amp;').replace(/'/g, '&#39;').
+              replace(/\</g, '&lt;').replace(/>/g, '&gt;').
+              replace(/\"/g, '&quot;');
+}
+
+// Compartment reporter names are URLs and so can include forward slashes.  But
+// forward slash is the memory reporter path separator.  So the memory
+// reporters change them to backslashes.  Undo that here.  
+function flipBackslashes(aStr)
+{
+  return aStr.replace(/\\/g, '/');
+}
+
+// Truncate the URL in a compartment name if not in verbose mode.
+function truncateCompartmentName(aStr)
+{
+  return (gVerbose)
+       ? aStr
+       : aStr.replace(/compartment\((.{40}).*\)/, 'compartment($1...)');
+}
+
+function prepName(aStr)
+{
+  return escapeAll(flipBackslashes(truncateCompartmentName(aStr)));
+}
+
+function prepDesc(aStr)
+{
+  return escapeQuotes(flipBackslashes(aStr));
 }
 
 function genMrNameText(aKind, aDesc, aName, aHasProblem)
 {
   const problemDesc =
     "Warning: this memory reporter was unable to compute a useful value. " +
     "The reported value is the sum of all entries below '" + aName + "', " +
     "which is probably less than the true value.";
   var text = "-- <span class='mrName hasDesc' title='" +
-             kindToString(aKind) + escapeQuotes(aDesc) +
-             "'>" + aName + "</span>";
+             kindToString(aKind) + prepDesc(aDesc) +
+             "'>" + prepName(aName) + "</span>";
   text += aHasProblem
         ? " <span class='mrStar' title=\"" + problemDesc + "\">[*]</span>\n"
         : "\n";
   return text;
 }
 
 /**
  * Generates the text for the tree, including its heading.
@@ -797,28 +835,28 @@ function genOtherText(aReporters)
       var hasProblem = false;
       if (r._amount === kUnknown) {
         hasProblem = true;
       }
       var elem = {
         _path:        r._path,
         _kind:        r._kind,
         _units:       r._units,
-        _amount:  hasProblem ? 0 : r._amount,
+        _amount:      hasProblem ? 0 : r._amount,
         _description: r._description,
         _hasProblem:  hasProblem
       };
       rArray.push(elem);
       var thisAmountLength = formatReporterAmount(elem).length;
       if (thisAmountLength > maxAmountLength) {
         maxAmountLength = thisAmountLength;
       }
     }
   }
-  rArray.sort(cmp_amount);
+  rArray.sort(cmpAmount);
 
   // Generate text for the not-yet-printed values.
   var text = "";
   for (var i = 0; i < rArray.length; i++) {
     var elem = rArray[i];
     text += genMrValueText(
               pad(formatReporterAmount(elem), maxAmountLength, ' ')) + " ";
     text += genMrNameText(elem._kind, elem._description, elem._path,
--- a/toolkit/components/aboutmemory/tests/chrome/test_aboutmemory.xul
+++ b/toolkit/components/aboutmemory/tests/chrome/test_aboutmemory.xul
@@ -79,18 +79,24 @@
     f("", "explicit/g/b",       HEAP,     5 * MB),
     f("", "other1",             OTHER,  111 * MB),
     f("", "other2",             OTHER,  222 * MB),
 
     f("2nd", "heap-used",       OTHER, 1000 * MB),
     f("2nd", "heap-unused",     OTHER,  100 * MB),
     f("2nd", "explicit/a/b/c",  HEAP,   498 * MB),
     f("2nd", "explicit/a/b/c",  HEAP,     1 * MB), // dup: merge
-    f("2nd", "explicit/b",      HEAP,   400 * MB),
-    f("2nd", "other1",          OTHER,  777 * MB),
+    f("2nd", "explicit/flip\\the\\backslashes",
+                                HEAP,   200 * MB),
+    f("2nd", "explicit/compartment(this-will-be-truncated-in-non-verbose-mode-abcdefghijklmnopqrstuvwxyz)",
+                                HEAP,   200 * MB),
+    // The escaping of compartment names must prevent this script from running.
+    f("2nd", "danger<script>window.alert(1)</script>",
+                                OTHER,  666 * MB),
+    f("2nd", "other1",          OTHER,  111 * MB),
 
     // kUnknown should be handled gracefully for "heap-used", non-leaf
     // reporters, leaf-reporters, and "other" reporters.
     f("3rd", "heap-used",       OTHER, kUnknown),
     f("3rd", "explicit/a",      HEAP,  kUnknown),
     f("3rd", "explicit/a/b",    HEAP,  333 * MB),
     f("3rd", "explicit/a/c",    HEAP,  444 * MB),
     f("3rd", "explicit/a/d",    HEAP,  kUnknown),
@@ -141,22 +147,24 @@ 100.00 MB -- heap-unused\n\
 \n\
 2nd Process\n\
 \n\
 Explicit Allocations\n\
 1,000.00 MB (100.0%) -- explicit\n\
 ├────499.00 MB (49.90%) -- a\n\
 │    └──499.00 MB (49.90%) -- b\n\
 │       └──499.00 MB (49.90%) -- c\n\
-├────400.00 MB (40.00%) -- b\n\
+├────200.00 MB (20.00%) -- flip/the/backslashes\n\
+├────200.00 MB (20.00%) -- compartment(this-will-be-truncated-in-non-verbose-mo...)\n\
 └────101.00 MB (10.10%) -- heap-unclassified\n\
 \n\
 Other Measurements\n\
 1,000.00 MB -- heap-used\n\
-  777.00 MB -- other1\n\
+  666.00 MB -- danger<script>window.alert(1)</script>\n\
+  111.00 MB -- other1\n\
   100.00 MB -- heap-unused\n\
 \n\
 3rd Process\n\
 \n\
 Explicit Allocations\n\
 777.00 MB (100.0%) -- explicit\n\
 ├──777.00 MB (100.0%) -- a [*]\n\
 │  ├──444.00 MB (57.14%) -- c\n\
@@ -204,22 +212,24 @@ 104,857,600 B -- heap-unused\n\
 \n\
 2nd Process\n\
 \n\
 Explicit Allocations\n\
 1,048,576,000 B (100.0%) -- explicit\n\
 ├────523,239,424 B (49.90%) -- a\n\
 │    └──523,239,424 B (49.90%) -- b\n\
 │       └──523,239,424 B (49.90%) -- c\n\
-├────419,430,400 B (40.00%) -- b\n\
+├────209,715,200 B (20.00%) -- flip/the/backslashes\n\
+├────209,715,200 B (20.00%) -- compartment(this-will-be-truncated-in-non-verbose-mode-abcdefghijklmnopqrstuvwxyz)\n\
 └────105,906,176 B (10.10%) -- heap-unclassified\n\
 \n\
 Other Measurements\n\
 1,048,576,000 B -- heap-used\n\
-  814,743,552 B -- other1\n\
+  698,351,616 B -- danger<script>window.alert(1)</script>\n\
+  116,391,936 B -- other1\n\
   104,857,600 B -- heap-unused\n\
 \n\
 3rd Process\n\
 \n\
 Explicit Allocations\n\
 814,743,552 B (100.0%) -- explicit\n\
 ├──814,743,552 B (100.0%) -- a [*]\n\
 │  ├──465,567,744 B (57.14%) -- c\n\
--- a/toolkit/components/telemetry/TelemetryPing.js
+++ b/toolkit/components/telemetry/TelemetryPing.js
@@ -49,17 +49,17 @@ const PAYLOAD_VERSION = 1;
 const PREF_SERVER = "toolkit.telemetry.server";
 const PREF_ENABLED = "toolkit.telemetry.enabled";
 // Do not gather data more than once a minute
 const TELEMETRY_INTERVAL = 60;
 // Delay before intializing telemetry (ms)
 const TELEMETRY_DELAY = 60000;
 // about:memory values to turn into histograms
 const MEM_HISTOGRAMS = {
-  "explicit/js/gc-heap": "MEMORY_JS_GC_HEAP",
+  "js-gc-heap": "MEMORY_JS_GC_HEAP",
   "resident": "MEMORY_RESIDENT",
   "explicit/layout/all": "MEMORY_LAYOUT_ALL",
   "hard-page-faults": "HARD_PAGE_FAULTS"
 };
 
 XPCOMUtils.defineLazyGetter(this, "Telemetry", function () {
   return Cc["@mozilla.org/base/telemetry;1"].getService(Ci.nsITelemetry);
 });