Bug 1105069 - Part 1: Move GCTraceKind from jspubtd to TraceAPI; r=jonco, r=mccr8
authorTerrence Cole <terrence@mozilla.com>
Mon, 01 Dec 2014 14:49:07 -0800
changeset 218691 caa4ffd2f7657fe4e2a19a11e083aeb5a1b241f6
parent 218690 7b20423063f52eeb6d23f213a013ddcaa113ce6e
child 218692 7b57ab0cf44f7cb66a2af419931aae28492d6d60
push id52613
push usertcole@mozilla.com
push dateMon, 08 Dec 2014 18:41:16 +0000
treeherdermozilla-inbound@ec983c96d034 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjonco, mccr8
bugs1105069
milestone37.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 1105069 - Part 1: Move GCTraceKind from jspubtd to TraceAPI; r=jonco, r=mccr8
js/public/HeapAPI.h
js/public/TracingAPI.h
js/src/gc/Marking.cpp
js/src/gc/Tracer.cpp
js/src/jsgc.cpp
js/src/jsgc.h
js/src/jspubtd.h
js/src/vm/MemoryMetrics.cpp
js/src/vm/UbiNode.cpp
xpcom/base/CycleCollectedJSRuntime.cpp
--- a/js/public/HeapAPI.h
+++ b/js/public/HeapAPI.h
@@ -6,16 +6,17 @@
 
 #ifndef js_HeapAPI_h
 #define js_HeapAPI_h
 
 #include <limits.h>
 
 #include "jspubtd.h"
 
+#include "js/TracingAPI.h"
 #include "js/Utility.h"
 
 /* These values are private to the JS engine. */
 namespace js {
 
 // Whether the current thread is permitted access to any part of the specified
 // runtime or zone.
 JS_FRIEND_API(bool)
--- a/js/public/TracingAPI.h
+++ b/js/public/TracingAPI.h
@@ -5,27 +5,69 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef js_TracingAPI_h
 #define js_TracingAPI_h
 
 #include "mozilla/NullPtr.h"
 
 #include "jsalloc.h"
-#include "jspubtd.h"
 
 #include "js/HashTable.h"
 
 class JS_PUBLIC_API(JSTracer);
 
 namespace JS {
 template <typename T> class Heap;
 template <typename T> class TenuredHeap;
 }
 
+// When tracing a thing, the GC needs to know about the layout of the object it
+// is looking at. There are a fixed number of different layouts that the GC
+// knows about. The "trace kind" is a static map which tells which layout a GC
+// thing has.
+//
+// Although this map is public, the details are completely hidden. Not all of
+// the matching C++ types are exposed, and those that are, are opaque.
+//
+// See Value::gcKind() and JSTraceCallback in Tracer.h for more details.
+enum JSGCTraceKind
+{
+    // These trace kinds have a publicly exposed, although opaque, C++ type.
+    // Note: The order here is determined by our Value packing. Other users
+    //       should sort alphabetically, for consistency.
+    JSTRACE_OBJECT = 0x00,
+    JSTRACE_STRING = 0x01,
+    JSTRACE_SYMBOL = 0x02,
+    JSTRACE_SCRIPT = 0x03,
+
+    // Shape details are exposed through JS_TraceShapeCycleCollectorChildren.
+    JSTRACE_SHAPE = 0x04,
+
+    // The kind associated with a nullptr.
+    JSTRACE_NULL = 0x06,
+
+    // A kind that indicates the real kind should be looked up in the arena.
+    JSTRACE_OUTOFLINE = 0x07,
+
+    // The following kinds do not have an exposed C++ idiom.
+    JSTRACE_BASE_SHAPE = 0x0F,
+    JSTRACE_JITCODE = 0x1F,
+    JSTRACE_LAZY_SCRIPT = 0x2F,
+    JSTRACE_TYPE_OBJECT = 0x3F,
+
+    JSTRACE_LAST = JSTRACE_TYPE_OBJECT
+};
+
+namespace JS {
+// Returns a static string equivalent of |kind|.
+JS_FRIEND_API(const char *)
+GCTraceKindToAscii(JSGCTraceKind kind);
+}
+
 // Tracer callback, called for each traceable thing directly referenced by a
 // particular object or runtime structure. It is the callback responsibility
 // to ensure the traversal of the full object graph via calling eventually
 // JS_TraceChildren on the passed thing. In this case the callback must be
 // prepared to deal with cycles in the traversal graph.
 //
 // kind argument is one of JSTRACE_OBJECT, JSTRACE_STRING or a tag denoting
 // internal implementation-specific traversal kind. In the latter case the only
--- a/js/src/gc/Marking.cpp
+++ b/js/src/gc/Marking.cpp
@@ -684,40 +684,42 @@ gc::MarkKind(JSTracer *trc, void **thing
     MOZ_ASSERT(*thingp);
     DebugOnly<Cell *> cell = static_cast<Cell *>(*thingp);
     MOZ_ASSERT_IF(cell->isTenured(),
                   kind == MapAllocToTraceKind(cell->asTenured().getAllocKind()));
     switch (kind) {
       case JSTRACE_OBJECT:
         MarkInternal(trc, reinterpret_cast<JSObject **>(thingp));
         break;
+      case JSTRACE_SCRIPT:
+        MarkInternal(trc, reinterpret_cast<JSScript **>(thingp));
+        break;
       case JSTRACE_STRING:
         MarkInternal(trc, reinterpret_cast<JSString **>(thingp));
         break;
       case JSTRACE_SYMBOL:
         MarkInternal(trc, reinterpret_cast<JS::Symbol **>(thingp));
         break;
-      case JSTRACE_SCRIPT:
-        MarkInternal(trc, reinterpret_cast<JSScript **>(thingp));
+      case JSTRACE_BASE_SHAPE:
+        MarkInternal(trc, reinterpret_cast<BaseShape **>(thingp));
+        break;
+      case JSTRACE_JITCODE:
+        MarkInternal(trc, reinterpret_cast<jit::JitCode **>(thingp));
         break;
       case JSTRACE_LAZY_SCRIPT:
         MarkInternal(trc, reinterpret_cast<LazyScript **>(thingp));
         break;
       case JSTRACE_SHAPE:
         MarkInternal(trc, reinterpret_cast<Shape **>(thingp));
         break;
-      case JSTRACE_BASE_SHAPE:
-        MarkInternal(trc, reinterpret_cast<BaseShape **>(thingp));
-        break;
       case JSTRACE_TYPE_OBJECT:
         MarkInternal(trc, reinterpret_cast<types::TypeObject **>(thingp));
         break;
-      case JSTRACE_JITCODE:
-        MarkInternal(trc, reinterpret_cast<jit::JitCode **>(thingp));
-        break;
+      default:
+        MOZ_CRASH("Invalid trace kind in MarkKind.");
     }
 }
 
 static void
 MarkGCThingInternal(JSTracer *trc, void **thingp, const char *name)
 {
     trc->setTracingName(name);
     MOZ_ASSERT(thingp);
@@ -1537,47 +1539,50 @@ PushArenaTyped(GCMarker *gcmarker, Arena
 void
 gc::PushArena(GCMarker *gcmarker, ArenaHeader *aheader)
 {
     switch (MapAllocToTraceKind(aheader->getAllocKind())) {
       case JSTRACE_OBJECT:
         PushArenaTyped<JSObject>(gcmarker, aheader);
         break;
 
+      case JSTRACE_SCRIPT:
+        PushArenaTyped<JSScript>(gcmarker, aheader);
+        break;
+
       case JSTRACE_STRING:
         PushArenaTyped<JSString>(gcmarker, aheader);
         break;
 
       case JSTRACE_SYMBOL:
         PushArenaTyped<JS::Symbol>(gcmarker, aheader);
         break;
 
-      case JSTRACE_SCRIPT:
-        PushArenaTyped<JSScript>(gcmarker, aheader);
+      case JSTRACE_BASE_SHAPE:
+        PushArenaTyped<js::BaseShape>(gcmarker, aheader);
+        break;
+
+      case JSTRACE_JITCODE:
+        PushArenaTyped<js::jit::JitCode>(gcmarker, aheader);
         break;
 
       case JSTRACE_LAZY_SCRIPT:
         PushArenaTyped<LazyScript>(gcmarker, aheader);
         break;
 
       case JSTRACE_SHAPE:
         PushArenaTyped<js::Shape>(gcmarker, aheader);
         break;
 
-      case JSTRACE_BASE_SHAPE:
-        PushArenaTyped<js::BaseShape>(gcmarker, aheader);
-        break;
-
       case JSTRACE_TYPE_OBJECT:
         PushArenaTyped<js::types::TypeObject>(gcmarker, aheader);
         break;
 
-      case JSTRACE_JITCODE:
-        PushArenaTyped<js::jit::JitCode>(gcmarker, aheader);
-        break;
+      default:
+        MOZ_CRASH("Invalid trace kind in PushArena.");
     }
 }
 
 struct SlotArrayLayout
 {
     union {
         HeapSlot *end;
         uintptr_t kind;
@@ -1941,47 +1946,50 @@ GCMarker::drainMarkStack(SliceBudget &bu
 void
 js::TraceChildren(JSTracer *trc, void *thing, JSGCTraceKind kind)
 {
     switch (kind) {
       case JSTRACE_OBJECT:
         MarkChildren(trc, static_cast<JSObject *>(thing));
         break;
 
+      case JSTRACE_SCRIPT:
+        MarkChildren(trc, static_cast<JSScript *>(thing));
+        break;
+
       case JSTRACE_STRING:
         MarkChildren(trc, static_cast<JSString *>(thing));
         break;
 
       case JSTRACE_SYMBOL:
         MarkChildren(trc, static_cast<JS::Symbol *>(thing));
         break;
 
-      case JSTRACE_SCRIPT:
-        MarkChildren(trc, static_cast<JSScript *>(thing));
+      case JSTRACE_BASE_SHAPE:
+        MarkChildren(trc, static_cast<BaseShape *>(thing));
+        break;
+
+      case JSTRACE_JITCODE:
+        MarkChildren(trc, (js::jit::JitCode *)thing);
         break;
 
       case JSTRACE_LAZY_SCRIPT:
         MarkChildren(trc, static_cast<LazyScript *>(thing));
         break;
 
       case JSTRACE_SHAPE:
         MarkChildren(trc, static_cast<Shape *>(thing));
         break;
 
-      case JSTRACE_JITCODE:
-        MarkChildren(trc, (js::jit::JitCode *)thing);
-        break;
-
-      case JSTRACE_BASE_SHAPE:
-        MarkChildren(trc, static_cast<BaseShape *>(thing));
-        break;
-
       case JSTRACE_TYPE_OBJECT:
         MarkChildren(trc, (types::TypeObject *)thing);
         break;
+
+      default:
+        MOZ_CRASH("Invalid trace kind in TraceChildren.");
     }
 }
 
 #ifdef DEBUG
 static void
 AssertNonGrayGCThing(JSTracer *trc, void **thingp, JSGCTraceKind kind)
 {
     MOZ_ASSERT(!JS::GCThingIsMarkedGray(*thingp));
--- a/js/src/gc/Tracer.cpp
+++ b/js/src/gc/Tracer.cpp
@@ -198,48 +198,52 @@ JS_GetTraceThingInfo(char *buf, size_t b
 
     switch (kind) {
       case JSTRACE_OBJECT:
       {
         name = static_cast<JSObject *>(thing)->getClass()->name;
         break;
       }
 
+      case JSTRACE_SCRIPT:
+        name = "script";
+        break;
+
       case JSTRACE_STRING:
         name = ((JSString *)thing)->isDependent()
                ? "substring"
                : "string";
         break;
 
       case JSTRACE_SYMBOL:
         name = "symbol";
         break;
 
-      case JSTRACE_SCRIPT:
-        name = "script";
+      case JSTRACE_BASE_SHAPE:
+        name = "base_shape";
+        break;
+
+      case JSTRACE_JITCODE:
+        name = "jitcode";
         break;
 
       case JSTRACE_LAZY_SCRIPT:
         name = "lazyscript";
         break;
 
-      case JSTRACE_JITCODE:
-        name = "jitcode";
-        break;
-
       case JSTRACE_SHAPE:
         name = "shape";
         break;
 
-      case JSTRACE_BASE_SHAPE:
-        name = "base_shape";
+      case JSTRACE_TYPE_OBJECT:
+        name = "type_object";
         break;
 
-      case JSTRACE_TYPE_OBJECT:
-        name = "type_object";
+      default:
+        name = "INVALID";
         break;
     }
 
     n = strlen(name);
     if (n > bufsize - 1)
         n = bufsize - 1;
     js_memcpy(buf, name, n + 1);
     buf += n;
@@ -261,16 +265,23 @@ JS_GetTraceThingInfo(char *buf, size_t b
             } else if (obj->getClass()->flags & JSCLASS_HAS_PRIVATE) {
                 JS_snprintf(buf, bufsize, " %p", obj->as<NativeObject>().getPrivate());
             } else {
                 JS_snprintf(buf, bufsize, " <no private>");
             }
             break;
           }
 
+          case JSTRACE_SCRIPT:
+          {
+            JSScript *script = static_cast<JSScript *>(thing);
+            JS_snprintf(buf, bufsize, " %s:%u", script->filename(), unsigned(script->lineno()));
+            break;
+          }
+
           case JSTRACE_STRING:
           {
             *buf++ = ' ';
             bufsize--;
             JSString *str = (JSString *)thing;
 
             if (str->isLinear()) {
                 bool willFit = str->length() + strlen("<length > ") +
@@ -301,28 +312,17 @@ JS_GetTraceThingInfo(char *buf, size_t b
                     JS_snprintf(buf, bufsize, "<nonlinear desc>");
                 }
             } else {
                 JS_snprintf(buf, bufsize, "<null>");
             }
             break;
           }
 
-          case JSTRACE_SCRIPT:
-          {
-            JSScript *script = static_cast<JSScript *>(thing);
-            JS_snprintf(buf, bufsize, " %s:%u", script->filename(), unsigned(script->lineno()));
-            break;
-          }
-
-          case JSTRACE_LAZY_SCRIPT:
-          case JSTRACE_JITCODE:
-          case JSTRACE_SHAPE:
-          case JSTRACE_BASE_SHAPE:
-          case JSTRACE_TYPE_OBJECT:
+          default:
             break;
         }
     }
     buf[bufsize - 1] = '\0';
 }
 
 JSTracer::JSTracer(JSRuntime *rt, JSTraceCallback traceCallback,
                    WeakMapTraceKind weakTraceKind /* = TraceWeakMapValues */)
--- a/js/src/jsgc.cpp
+++ b/js/src/jsgc.cpp
@@ -323,33 +323,16 @@ const uint32_t Arena::FirstThingOffsets[
     OFFSET(JSString),           /* FINALIZE_STRING              */
     OFFSET(JSExternalString),   /* FINALIZE_EXTERNAL_STRING     */
     OFFSET(JS::Symbol),         /* FINALIZE_SYMBOL              */
     OFFSET(jit::JitCode),       /* FINALIZE_JITCODE             */
 };
 
 #undef OFFSET
 
-const char *
-js::gc::TraceKindAsAscii(JSGCTraceKind kind)
-{
-    switch(kind) {
-      case JSTRACE_OBJECT: return "JSTRACE_OBJECT";
-      case JSTRACE_STRING: return "JSTRACE_STRING";
-      case JSTRACE_SYMBOL: return "JSTRACE_SYMBOL";
-      case JSTRACE_SCRIPT: return "JSTRACE_SCRIPT";
-      case JSTRACE_LAZY_SCRIPT: return "JSTRACE_SCRIPT";
-      case JSTRACE_JITCODE: return "JSTRACE_JITCODE";
-      case JSTRACE_SHAPE: return "JSTRACE_SHAPE";
-      case JSTRACE_BASE_SHAPE: return "JSTRACE_BASE_SHAPE";
-      case JSTRACE_TYPE_OBJECT: return "JSTRACE_TYPE_OBJECT";
-      default: return "INVALID";
-    }
-}
-
 struct js::gc::FinalizePhase
 {
     size_t length;
     const AllocKind *kinds;
     gcstats::Phase statsPhase;
 };
 
 #define PHASE(x, p) { ArrayLength(x), x, p }
@@ -7041,16 +7024,33 @@ JS::AutoAssertNoAlloc::~AutoAssertNoAllo
 #endif
 
 JS::AutoAssertGCCallback::AutoAssertGCCallback(JSObject *obj)
   : AutoSuppressGCAnalysis()
 {
     MOZ_ASSERT(obj->runtimeFromMainThread()->isHeapMajorCollecting());
 }
 
+JS_FRIEND_API(const char *)
+JS::GCTraceKindToAscii(JSGCTraceKind kind)
+{
+    switch(kind) {
+      case JSTRACE_OBJECT: return "Object";
+      case JSTRACE_SCRIPT: return "Script";
+      case JSTRACE_STRING: return "String";
+      case JSTRACE_SYMBOL: return "Symbol";
+      case JSTRACE_SHAPE: return "Shape";
+      case JSTRACE_BASE_SHAPE: return "BaseShape";
+      case JSTRACE_LAZY_SCRIPT: return "LazyScript";
+      case JSTRACE_JITCODE: return "JitCode";
+      case JSTRACE_TYPE_OBJECT: return "TypeObject";
+      default: return "Invalid";
+    }
+}
+
 #ifdef JSGC_HASH_TABLE_CHECKS
 void
 js::gc::CheckHashTablesAfterMovingGC(JSRuntime *rt)
 {
     /*
      * Check that internal hash tables no longer have any pointers to things
      * that have been moved.
      */
--- a/js/src/jsgc.h
+++ b/js/src/jsgc.h
@@ -56,20 +56,16 @@ struct FinalizePhase;
 enum State {
     NO_INCREMENTAL,
     MARK_ROOTS,
     MARK,
     SWEEP,
     COMPACT
 };
 
-/* Return a printable string for the given kind, for diagnostic purposes. */
-const char *
-TraceKindAsAscii(JSGCTraceKind kind);
-
 /* Map from C++ type to alloc kind. JSObject does not have a 1:1 mapping, so must use Arena::thingSize. */
 template <typename T> struct MapTypeToFinalizeKind {};
 template <> struct MapTypeToFinalizeKind<JSScript>          { static const AllocKind kind = FINALIZE_SCRIPT; };
 template <> struct MapTypeToFinalizeKind<LazyScript>        { static const AllocKind kind = FINALIZE_LAZY_SCRIPT; };
 template <> struct MapTypeToFinalizeKind<Shape>             { static const AllocKind kind = FINALIZE_SHAPE; };
 template <> struct MapTypeToFinalizeKind<AccessorShape>     { static const AllocKind kind = FINALIZE_ACCESSOR_SHAPE; };
 template <> struct MapTypeToFinalizeKind<BaseShape>         { static const AllocKind kind = FINALIZE_BASE_SHAPE; };
 template <> struct MapTypeToFinalizeKind<types::TypeObject> { static const AllocKind kind = FINALIZE_TYPE_OBJECT; };
--- a/js/src/jspubtd.h
+++ b/js/src/jspubtd.h
@@ -97,35 +97,16 @@ enum JSIterateOp {
 
     /* Iterate once. */
     JSENUMERATE_NEXT,
 
     /* Destroy iterator state. */
     JSENUMERATE_DESTROY
 };
 
-/* See Value::gcKind() and JSTraceCallback in Tracer.h. */
-enum JSGCTraceKind {
-    JSTRACE_OBJECT,
-    JSTRACE_STRING,
-    JSTRACE_SYMBOL,
-    JSTRACE_SCRIPT,
-
-    /*
-     * Trace kinds internal to the engine. The embedding can only see them if
-     * it implements JSTraceCallback.
-     */
-    JSTRACE_LAZY_SCRIPT,
-    JSTRACE_JITCODE,
-    JSTRACE_SHAPE,
-    JSTRACE_BASE_SHAPE,
-    JSTRACE_TYPE_OBJECT,
-    JSTRACE_LAST = JSTRACE_TYPE_OBJECT
-};
-
 /* Struct forward declarations. */
 struct JSClass;
 struct JSCompartment;
 struct JSCrossCompartmentCall;
 class JSErrorReport;
 struct JSExceptionState;
 struct JSFunctionSpec;
 struct JSIdArray;
--- a/js/src/vm/MemoryMetrics.cpp
+++ b/js/src/vm/MemoryMetrics.cpp
@@ -423,84 +423,16 @@ StatsCellCallback(JSRuntime *rt, void *d
         if (ObjectPrivateVisitor *opv = closure->opv) {
             nsISupports *iface;
             if (opv->getISupports_(obj, &iface) && iface)
                 cStats->objectsPrivate += opv->sizeOfIncludingThis(iface);
         }
         break;
       }
 
-      case JSTRACE_STRING: {
-        JSString *str = static_cast<JSString *>(thing);
-
-        JS::StringInfo info;
-        if (str->hasLatin1Chars()) {
-            info.gcHeapLatin1 = thingSize;
-            info.mallocHeapLatin1 = str->sizeOfExcludingThis(rtStats->mallocSizeOf_);
-        } else {
-            info.gcHeapTwoByte = thingSize;
-            info.mallocHeapTwoByte = str->sizeOfExcludingThis(rtStats->mallocSizeOf_);
-        }
-        info.numCopies = 1;
-
-        zStats->stringInfo.add(info);
-
-        // The primary use case for anonymization is automated crash submission
-        // (to help detect OOM crashes). In that case, we don't want to pay the
-        // memory cost required to do notable string detection.
-        if (granularity == FineGrained && !closure->anonymize) {
-            ZoneStats::StringsHashMap::AddPtr p = zStats->allStrings->lookupForAdd(str);
-            if (!p) {
-                // Ignore failure -- we just won't record the string as notable.
-                (void)zStats->allStrings->add(p, str, info);
-            } else {
-                p->value().add(info);
-            }
-        }
-        break;
-      }
-
-      case JSTRACE_SYMBOL:
-        zStats->symbolsGCHeap += thingSize;
-        break;
-
-      case JSTRACE_SHAPE: {
-        Shape *shape = static_cast<Shape *>(thing);
-        CompartmentStats *cStats = GetCompartmentStats(shape->compartment());
-        JS::ClassInfo info;        // This zeroes all the sizes.
-        if (shape->inDictionary())
-            info.shapesGCHeapDict += thingSize;
-        else
-            info.shapesGCHeapTree += thingSize;
-        shape->addSizeOfExcludingThis(rtStats->mallocSizeOf_, &info);
-        cStats->classInfo.add(info);
-
-        const BaseShape *base = shape->base();
-        const Class *clasp = base->clasp();
-        const char *className = clasp->name;
-        AddClassInfo(granularity, cStats, className, info);
-        break;
-      }
-
-      case JSTRACE_BASE_SHAPE: {
-        BaseShape *base = static_cast<BaseShape *>(thing);
-        CompartmentStats *cStats = GetCompartmentStats(base->compartment());
-
-        JS::ClassInfo info;        // This zeroes all the sizes.
-        info.shapesGCHeapBase += thingSize;
-        // No malloc-heap measurements.
-
-        cStats->classInfo.add(info);
-
-        const Class *clasp = base->clasp();
-        const char *className = clasp->name;
-        AddClassInfo(granularity, cStats, className, info);
-        break;
-      }
-
       case JSTRACE_SCRIPT: {
         JSScript *script = static_cast<JSScript *>(thing);
         CompartmentStats *cStats = GetCompartmentStats(script->compartment());
         cStats->scriptsGCHeap += thingSize;
         cStats->scriptsMallocHeapData += script->sizeOfData(rtStats->mallocSizeOf_);
         cStats->typeInferenceTypeScripts += script->sizeOfTypeScript(rtStats->mallocSizeOf_);
         jit::AddSizeOfBaselineData(script, rtStats->mallocSizeOf_, &cStats->baselineData,
                                    &cStats->baselineStubsFallback);
@@ -531,38 +463,106 @@ StatsCellCallback(JSRuntime *rt, void *d
                     p->value().add(info);
                 }
             }
         }
 
         break;
       }
 
+      case JSTRACE_STRING: {
+        JSString *str = static_cast<JSString *>(thing);
+
+        JS::StringInfo info;
+        if (str->hasLatin1Chars()) {
+            info.gcHeapLatin1 = thingSize;
+            info.mallocHeapLatin1 = str->sizeOfExcludingThis(rtStats->mallocSizeOf_);
+        } else {
+            info.gcHeapTwoByte = thingSize;
+            info.mallocHeapTwoByte = str->sizeOfExcludingThis(rtStats->mallocSizeOf_);
+        }
+        info.numCopies = 1;
+
+        zStats->stringInfo.add(info);
+
+        // The primary use case for anonymization is automated crash submission
+        // (to help detect OOM crashes). In that case, we don't want to pay the
+        // memory cost required to do notable string detection.
+        if (granularity == FineGrained && !closure->anonymize) {
+            ZoneStats::StringsHashMap::AddPtr p = zStats->allStrings->lookupForAdd(str);
+            if (!p) {
+                // Ignore failure -- we just won't record the string as notable.
+                (void)zStats->allStrings->add(p, str, info);
+            } else {
+                p->value().add(info);
+            }
+        }
+        break;
+      }
+
+      case JSTRACE_SYMBOL:
+        zStats->symbolsGCHeap += thingSize;
+        break;
+
+      case JSTRACE_BASE_SHAPE: {
+        BaseShape *base = static_cast<BaseShape *>(thing);
+        CompartmentStats *cStats = GetCompartmentStats(base->compartment());
+
+        JS::ClassInfo info;        // This zeroes all the sizes.
+        info.shapesGCHeapBase += thingSize;
+        // No malloc-heap measurements.
+
+        cStats->classInfo.add(info);
+
+        const Class *clasp = base->clasp();
+        const char *className = clasp->name;
+        AddClassInfo(granularity, cStats, className, info);
+        break;
+      }
+
+      case JSTRACE_JITCODE: {
+        zStats->jitCodesGCHeap += thingSize;
+        // The code for a script is counted in ExecutableAllocator::sizeOfCode().
+        break;
+      }
+
       case JSTRACE_LAZY_SCRIPT: {
         LazyScript *lazy = static_cast<LazyScript *>(thing);
         zStats->lazyScriptsGCHeap += thingSize;
         zStats->lazyScriptsMallocHeap += lazy->sizeOfExcludingThis(rtStats->mallocSizeOf_);
         break;
       }
 
-      case JSTRACE_JITCODE: {
-        zStats->jitCodesGCHeap += thingSize;
-        // The code for a script is counted in ExecutableAllocator::sizeOfCode().
+      case JSTRACE_SHAPE: {
+        Shape *shape = static_cast<Shape *>(thing);
+        CompartmentStats *cStats = GetCompartmentStats(shape->compartment());
+        JS::ClassInfo info;        // This zeroes all the sizes.
+        if (shape->inDictionary())
+            info.shapesGCHeapDict += thingSize;
+        else
+            info.shapesGCHeapTree += thingSize;
+        shape->addSizeOfExcludingThis(rtStats->mallocSizeOf_, &info);
+        cStats->classInfo.add(info);
+
+        const BaseShape *base = shape->base();
+        const Class *clasp = base->clasp();
+        const char *className = clasp->name;
+        AddClassInfo(granularity, cStats, className, info);
         break;
       }
 
       case JSTRACE_TYPE_OBJECT: {
         types::TypeObject *obj = static_cast<types::TypeObject *>(thing);
         zStats->typeObjectsGCHeap += thingSize;
         zStats->typeObjectsMallocHeap += obj->sizeOfExcludingThis(rtStats->mallocSizeOf_);
         break;
       }
 
       default:
-        MOZ_CRASH("invalid traceKind");
+        MOZ_CRASH("invalid traceKind in StatsCellCallback");
     }
 
     // Yes, this is a subtraction:  see StatsArenaCallback() for details.
     zStats->unusedGCThings -= thingSize;
 }
 
 bool
 ZoneStats::initStrings(JSRuntime *rt)
--- a/js/src/vm/UbiNode.cpp
+++ b/js/src/vm/UbiNode.cpp
@@ -54,23 +54,23 @@ Concrete<void>::size(mozilla::MallocSize
 {
     MOZ_CRASH("null ubi::Node");
 }
 
 Node::Node(JSGCTraceKind kind, void *ptr)
 {
     switch (kind) {
       case JSTRACE_OBJECT:      construct(static_cast<JSObject *>(ptr));              break;
+      case JSTRACE_SCRIPT:      construct(static_cast<JSScript *>(ptr));              break;
       case JSTRACE_STRING:      construct(static_cast<JSString *>(ptr));              break;
       case JSTRACE_SYMBOL:      construct(static_cast<JS::Symbol *>(ptr));            break;
-      case JSTRACE_SCRIPT:      construct(static_cast<JSScript *>(ptr));              break;
-      case JSTRACE_LAZY_SCRIPT: construct(static_cast<js::LazyScript *>(ptr));        break;
+      case JSTRACE_BASE_SHAPE:  construct(static_cast<js::BaseShape *>(ptr));         break;
       case JSTRACE_JITCODE:     construct(static_cast<js::jit::JitCode *>(ptr));      break;
+      case JSTRACE_LAZY_SCRIPT: construct(static_cast<js::LazyScript *>(ptr));        break;
       case JSTRACE_SHAPE:       construct(static_cast<js::Shape *>(ptr));             break;
-      case JSTRACE_BASE_SHAPE:  construct(static_cast<js::BaseShape *>(ptr));         break;
       case JSTRACE_TYPE_OBJECT: construct(static_cast<js::types::TypeObject *>(ptr)); break;
 
       default:
         MOZ_CRASH("bad JSGCTraceKind passed to JS::ubi::Node::Node");
     }
 }
 
 Node::Node(HandleValue value)
--- a/xpcom/base/CycleCollectedJSRuntime.cpp
+++ b/xpcom/base/CycleCollectedJSRuntime.cpp
@@ -577,34 +577,20 @@ CycleCollectedJSRuntime::DescribeGCThing
         AssignJSFlatString(chars, flat);
         NS_ConvertUTF16toUTF8 fname(chars);
         JS_snprintf(name, sizeof(name),
                     "JS Object (Function - %s)", fname.get());
       } else {
         JS_snprintf(name, sizeof(name), "JS Object (Function)");
       }
     } else {
-      JS_snprintf(name, sizeof(name), "JS Object (%s)",
-                  clasp->name);
+      JS_snprintf(name, sizeof(name), "JS Object (%s)", clasp->name);
     }
   } else {
-    static const char trace_types[][11] = {
-      "Object",
-      "String",
-      "Symbol",
-      "Script",
-      "LazyScript",
-      "IonCode",
-      "Shape",
-      "BaseShape",
-      "TypeObject",
-    };
-    static_assert(MOZ_ARRAY_LENGTH(trace_types) == JSTRACE_LAST + 1,
-                  "JSTRACE_LAST enum must match trace_types count.");
-    JS_snprintf(name, sizeof(name), "JS %s", trace_types[aTraceKind]);
+    JS_snprintf(name, sizeof(name), "JS %s", JS::GCTraceKindToAscii(aTraceKind));
   }
 
   // Disable printing global for objects while we figure out ObjShrink fallout.
   aCb.DescribeGCedNode(aIsMarked, name, compartmentAddress);
 }
 
 void
 CycleCollectedJSRuntime::NoteGCThingJSChildren(void* aThing,