Bug 1153959 - Use RAII to manage tracing details; r=jonco
authorTerrence Cole <terrence@mozilla.com>
Fri, 10 Apr 2015 13:33:56 -0700
changeset 239755 15df3c88bb3119d125b644db1974a82858e69676
parent 239754 be0b6754385647f6eb7798cc634d920156b8bc8a
child 239756 ae7c605075e22a1ec2ce1b3b8b654903802134ef
push id28610
push userphilringnalda@gmail.com
push dateSat, 18 Apr 2015 23:37:38 +0000
treeherdermozilla-central@0500d91113bb [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjonco
bugs1153959
milestone40.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 1153959 - Use RAII to manage tracing details; r=jonco
js/public/TracingAPI.h
js/src/builtin/MapObject.cpp
js/src/gc/Marking.cpp
js/src/gc/Marking.h
js/src/gc/RootMarking.cpp
js/src/gc/Tracer.cpp
js/src/gc/Tracer.h
js/src/gc/Verifier.cpp
js/src/gc/Zone.h
js/src/jscntxt.cpp
js/src/jsgc.h
js/src/jsobj.cpp
js/src/jsobj.h
js/src/vm/ObjectGroup.cpp
xpcom/base/CycleCollectedJSRuntime.cpp
--- a/js/public/TracingAPI.h
+++ b/js/public/TracingAPI.h
@@ -75,156 +75,247 @@ GCTraceKindToAscii(JSGCTraceKind kind);
 // JS_GetTraceThingInfo.
 //
 // If eagerlyTraceWeakMaps is true, when we trace a WeakMap visit all
 // of its mappings. This should be used in cases where the tracer
 // wants to use the existing liveness of entries.
 typedef void
 (* JSTraceCallback)(JS::CallbackTracer* trc, void** thingp, JSGCTraceKind kind);
 
-// Callback that JSTraceOp implementation can provide to return a string
-// describing the reference traced with JS_CallTracer.
-typedef void
-(* JSTraceNamePrinter)(JSTracer* trc, char* buf, size_t bufsize);
-
 enum WeakMapTraceKind {
     DoNotTraceWeakMaps = 0,
     TraceWeakMapValues = 1,
     TraceWeakMapKeysValues = 2
 };
 
 class JS_PUBLIC_API(JSTracer)
 {
   public:
-    // Set debugging information about a reference to a traceable thing to prepare
-    // for the following call to JS_CallTracer.
-    //
-    // When printer is null, arg must be const char * or char * C string naming
-    // the reference and index must be either (size_t)-1 indicating that the name
-    // alone describes the reference or it must be an index into some array vector
-    // that stores the reference.
-    //
-    // When printer callback is not null, the arg and index arguments are
-    // available to the callback as debugPrintArg_ and debugPrintIndex_ fields
-    // of JSTracer.
-    //
-    // The storage for name or callback's arguments needs to live only until
-    // the following call to JS_CallTracer returns.
-    void setTracingDetails(JSTraceNamePrinter printer, const void* arg, size_t index) {
-        debugPrinter_ = printer;
-        debugPrintArg_ = arg;
-        debugPrintIndex_ = index;
-    }
-
-    void setTracingIndex(const char* name, size_t index) {
-        setTracingDetails(nullptr, (void*)name, index);
-    }
-
-    void setTracingName(const char* name) {
-        setTracingDetails(nullptr, (void*)name, InvalidIndex);
-    }
-
-    // Remove the currently set tracing details.
-    void clearTracingDetails() {
-        debugPrinter_ = nullptr;
-        debugPrintArg_ = nullptr;
-    }
-
-    const static size_t InvalidIndex = size_t(-1);
-
-    // Return true if tracing details are currently set.
-    bool hasTracingDetails() const;
-
-    // Get the string set with the most recent call to setTracingName or return
-    // fallback if a name printer function has been installed.
-    const char* tracingName(const char* fallback) const;
-
-    // Build a description of this edge in the heap graph. This call may invoke
-    // the debug printer, which may inspect arbitrary areas of the heap.
-    const char* getTracingEdgeName(char* buffer, size_t bufferSize);
-
-    // Access the currently active tracing details.
-    JSTraceNamePrinter debugPrinter() const;
-    const void* debugPrintArg() const;
-    size_t debugPrintIndex() const;
-
     // Return the runtime set on the tracer.
     JSRuntime* runtime() const { return runtime_; }
 
     // Return the weak map tracing behavior set on this tracer.
     WeakMapTraceKind eagerlyTraceWeakMaps() const { return eagerlyTraceWeakMaps_; }
 
-#ifdef JS_GC_ZEAL
-    // Sets the "real" location for a marked reference, when passing the address
-    // directly is not feasable. This address is used for matching against the
-    // store buffer when verifying the correctness of the entrees there.
-    //
-    // This is currently complicated by our need to nest calls for Values
-    // stored as keys in hash tables.
-    void setTracingLocation(void* location);
-    void unsetTracingLocation();
-    void** tracingLocation(void** thingp);
-#else
-    void setTracingLocation(void* location) {}
-    void unsetTracingLocation() {}
-    void** tracingLocation(void** thingp) { return nullptr; }
-#endif
-
     // An intermediate state on the road from C to C++ style dispatch.
     enum TracerKindTag {
         MarkingTracer,
         CallbackTracer
     };
     bool isMarkingTracer() const { return tag == MarkingTracer; }
     bool isCallbackTracer() const { return tag == CallbackTracer; }
     inline JS::CallbackTracer* asCallbackTracer();
 
   protected:
     JSTracer(JSRuntime* rt, TracerKindTag tag,
              WeakMapTraceKind weakTraceKind = TraceWeakMapValues);
 
   private:
     JSRuntime*          runtime_;
     TracerKindTag       tag;
-    JSTraceNamePrinter  debugPrinter_;
-    const void*         debugPrintArg_;
-    size_t              debugPrintIndex_;
     WeakMapTraceKind    eagerlyTraceWeakMaps_;
-#ifdef JS_GC_ZEAL
-    void*               realLocation_;
-#endif
 };
 
 namespace JS {
 
+class AutoTracingName;
+class AutoTracingIndex;
+class AutoTracingCallback;
+class AutoOriginalTraceLocation;
+
 class JS_PUBLIC_API(CallbackTracer) : public JSTracer
 {
   public:
     CallbackTracer(JSRuntime* rt, JSTraceCallback traceCallback,
                    WeakMapTraceKind weakTraceKind = TraceWeakMapValues)
-      : JSTracer(rt, JSTracer::CallbackTracer, weakTraceKind), callback(traceCallback)
+      : JSTracer(rt, JSTracer::CallbackTracer, weakTraceKind), callback(traceCallback),
+        contextName_(nullptr), contextIndex_(InvalidIndex), contextFunctor_(nullptr),
+        contextRealLocation_(nullptr)
     {}
 
     // Update the trace callback.
     void setTraceCallback(JSTraceCallback traceCallback);
 
     // Test if the given callback is the same as our callback.
     bool hasCallback(JSTraceCallback maybeCallback) const {
         return maybeCallback == callback;
     }
 
     // Call the callback.
     void invoke(void** thing, JSGCTraceKind kind) {
         callback(this, thing, kind);
     }
 
+    // Access to the tracing context:
+    // When tracing with a JS::CallbackTracer, we invoke the callback with the
+    // edge location and the type of target. This is useful for operating on
+    // the edge in the abstract or on the target thing, satisfying most common
+    // use cases.  However, some tracers need additional detail about the
+    // specific edge that is being traced in order to be useful. Unfortunately,
+    // the raw pointer to the edge that we provide is not enough information to
+    // infer much of anything useful about that edge.
+    //
+    // In order to better support use cases that care in particular about edges
+    // -- as opposed to the target thing -- tracing implementations are
+    // responsible for providing extra context information about each edge they
+    // trace, as it is traced. This contains, at a minimum, an edge name and,
+    // when tracing an array, the index. Further specialization can be achived
+    // (with some complexity), by associating a functor with the tracer so
+    // that, when requested, the user can generate totally custom edge
+    // descriptions.
+
+    // Returns the current edge's name. It is only valid to call this when
+    // inside the trace callback, however, the edge name will always be set.
+    const char* contextName() const { MOZ_ASSERT(contextName_); return contextName_; }
+
+    // Returns the current edge's index, if marked as part of an array of edges.
+    // This must be called only inside the trace callback. When not tracing an
+    // array, the value will be InvalidIndex.
+    const static size_t InvalidIndex = size_t(-1);
+    size_t contextIndex() const { return contextIndex_; }
+
+    // Build a description of this edge in the heap graph. This call may invoke
+    // the context functor, if set, which may inspect arbitrary areas of the
+    // heap. On the other hand, the description provided by this method may be
+    // substantially more accurate and useful than those provided by only the
+    // contextName and contextIndex.
+    const char* getTracingEdgeName(char* buffer, size_t bufferSize);
+
+    // The trace implementation may associate a callback with one or more edges
+    // using AutoTracingDetails. This functor is called by getTracingEdgeName
+    // and is responsible for providing a textual representation of the
+    // currently being traced edge. The callback has access to the full heap,
+    // including the currently set tracing context.
+    class ContextFunctor {
+      public:
+        virtual void operator()(CallbackTracer* trc, char* buf, size_t bufsize) = 0;
+    };
+
+    // Return the original heap tracing location if the raw thingp reference
+    // has been moved. This is generally only useful for heap analyses that
+    // need to build an accurate model of the heap, and thus is only accurate
+    // when built with JS_GC_ZEAL.
+    void*const* tracingLocation(void** thingp) {
+        return contextRealLocation_ ? contextRealLocation_ : thingp;
+    }
+
   private:
     // Exposed publicly for several callers that need to check if the tracer
     // calling them is of the right type.
     JSTraceCallback callback;
+
+    friend class AutoTracingName;
+    const char* contextName_;
+
+    friend class AutoTracingIndex;
+    size_t contextIndex_;
+
+    friend class AutoTracingDetails;
+    ContextFunctor* contextFunctor_;
+
+    friend class AutoOriginalTraceLocation;
+    void*const* contextRealLocation_;
+};
+
+// Set the name portion of the tracer's context for the current edge.
+class AutoTracingName
+{
+    CallbackTracer* trc_;
+    const char *prior_;
+
+  public:
+    AutoTracingName(CallbackTracer* trc, const char* name) : trc_(trc), prior_(trc->contextName_) {
+        MOZ_ASSERT(name);
+        trc->contextName_ = name;
+    }
+    ~AutoTracingName() {
+        MOZ_ASSERT(trc_->contextName_);
+        trc_->contextName_ = prior_;
+    }
+};
+
+// Set the index portion of the tracer's context for the current range.
+class AutoTracingIndex
+{
+    CallbackTracer* trc_;
+
+  public:
+    explicit AutoTracingIndex(JSTracer* trc, size_t initial = 0) : trc_(nullptr) {
+        if (trc->isCallbackTracer()) {
+            trc_ = trc->asCallbackTracer();
+            MOZ_ASSERT(trc_->contextIndex_ == CallbackTracer::InvalidIndex);
+            trc_->contextIndex_ = initial;
+        }
+    }
+    ~AutoTracingIndex() {
+        if (trc_) {
+            MOZ_ASSERT(trc_->contextIndex_ != CallbackTracer::InvalidIndex);
+            trc_->contextIndex_ = CallbackTracer::InvalidIndex;
+        }
+    }
+
+    void operator++() {
+        if (trc_) {
+            MOZ_ASSERT(trc_->contextIndex_ != CallbackTracer::InvalidIndex);
+            ++trc_->contextIndex_;
+        }
+    }
+};
+
+// Set a context callback for the trace callback to use, if it needs a detailed
+// edge description.
+class AutoTracingDetails
+{
+    CallbackTracer* trc_;
+
+  public:
+    AutoTracingDetails(JSTracer* trc, CallbackTracer::ContextFunctor& func) : trc_(nullptr) {
+        if (trc->isCallbackTracer()) {
+            trc_ = trc->asCallbackTracer();
+            MOZ_ASSERT(trc_->contextFunctor_ == nullptr);
+            trc_->contextFunctor_ = &func;
+        }
+    }
+    ~AutoTracingDetails() {
+        if (trc_) {
+            MOZ_ASSERT(trc_->contextFunctor_);
+            trc_->contextFunctor_ = nullptr;
+        }
+    }
+};
+
+// Some dynamic analyses depend on knowing the edge source location as it
+// exists in the object graph. When marking some types of things, e.g. Value
+// edges, it is necessary to copy into a temporary on the stack. This class
+// records the original location if we need to copy the tracee, so that the
+// relevant analyses can continue to operate correctly.
+class AutoOriginalTraceLocation
+{
+#ifdef JS_GC_ZEAL
+    CallbackTracer *trc_;
+
+  public:
+    template <typename T>
+    AutoOriginalTraceLocation(JSTracer* trc, T*const* realLocation) : trc_(nullptr) {
+        if (trc->isCallbackTracer() && trc->asCallbackTracer()->contextRealLocation_ == nullptr) {
+            trc_ = trc->asCallbackTracer();
+            trc_->contextRealLocation_ = reinterpret_cast<void*const*>(realLocation);
+        }
+    }
+    ~AutoOriginalTraceLocation() {
+        if (trc_) {
+            MOZ_ASSERT(trc_->contextRealLocation_);
+            trc_->contextRealLocation_ = nullptr;
+        }
+    }
+#else
+  public:
+    template <typename T>
+    AutoOriginalTraceLocation(JSTracer* trc, T*const* realLocation) {}
+#endif
 };
 
 } // namespace JS
 
 JS::CallbackTracer*
 JSTracer::asCallbackTracer()
 {
     MOZ_ASSERT(isCallbackTracer());
@@ -279,17 +370,17 @@ JS_CallUnbarrieredStringTracer(JSTracer*
 extern JS_PUBLIC_API(void)
 JS_CallUnbarrieredScriptTracer(JSTracer* trc, JSScript** scriptp, const char* name);
 
 template <typename HashSetEnum>
 inline void
 JS_CallHashSetObjectTracer(JSTracer* trc, HashSetEnum& e, JSObject* const& key, const char* name)
 {
     JSObject* updated = key;
-    trc->setTracingLocation(reinterpret_cast<void*>(&const_cast<JSObject*&>(key)));
+    JS::AutoOriginalTraceLocation reloc(trc, &key);
     JS_CallUnbarrieredObjectTracer(trc, &updated, name);
     if (updated != key)
         e.rekeyFront(updated);
 }
 
 // Trace an object that is known to always be tenured.  No post barriers are
 // required in this case.
 extern JS_PUBLIC_API(void)
--- a/js/src/builtin/MapObject.cpp
+++ b/js/src/builtin/MapObject.cpp
@@ -834,17 +834,17 @@ HashableValue::operator==(const Hashable
 #endif
     return b;
 }
 
 HashableValue
 HashableValue::mark(JSTracer* trc) const
 {
     HashableValue hv(*this);
-    trc->setTracingLocation((void*)this);
+    JS::AutoOriginalTraceLocation reloc(trc, (void**)this);
     TraceEdge(trc, &hv.value, "key");
     return hv;
 }
 
 
 /*** MapIterator *********************************************************************************/
 
 namespace {
--- a/js/src/gc/Marking.cpp
+++ b/js/src/gc/Marking.cpp
@@ -363,62 +363,66 @@ template <typename T> struct PtrBaseGCTy
 
 template <typename T>
 typename PtrBaseGCType<T>::type*
 ConvertToBase(T* thingp)
 {
     return reinterpret_cast<typename PtrBaseGCType<T>::type*>(thingp);
 }
 
-template <typename T> void DispatchToTracer(JSTracer* trc, T* thingp, const char* name, size_t i);
-template <typename T> void DoTracing(JS::CallbackTracer* trc, T* thingp, const char* name, size_t i);
+template <typename T> void DispatchToTracer(JSTracer* trc, T* thingp, const char* name);
+template <typename T> void DoTracing(JS::CallbackTracer* trc, T* thingp, const char* name);
 template <typename T> void DoMarking(GCMarker* gcmarker, T thing);
 static bool ShouldMarkCrossCompartment(JSTracer* trc, JSObject* src, Cell* cell);
 static bool ShouldMarkCrossCompartment(JSTracer* trc, JSObject* src, Value val);
 
 template <typename T>
 void
 js::TraceEdge(JSTracer* trc, BarrieredBase<T>* thingp, const char* name)
 {
-    DispatchToTracer(trc, ConvertToBase(thingp->unsafeGet()), name, JSTracer::InvalidIndex);
+    DispatchToTracer(trc, ConvertToBase(thingp->unsafeGet()), name);
 }
 
 template <typename T>
 void
 js::TraceManuallyBarrieredEdge(JSTracer* trc, T* thingp, const char* name)
 {
-    DispatchToTracer(trc, ConvertToBase(thingp), name, JSTracer::InvalidIndex);
+    DispatchToTracer(trc, ConvertToBase(thingp), name);
 }
 
 template <typename T>
 void
 js::TraceRoot(JSTracer* trc, T* thingp, const char* name)
 {
     JS_ROOT_MARKING_ASSERT(trc);
-    DispatchToTracer(trc, ConvertToBase(thingp), name, JSTracer::InvalidIndex);
+    DispatchToTracer(trc, ConvertToBase(thingp), name);
 }
 
 template <typename T>
 void
 js::TraceRange(JSTracer* trc, size_t len, BarrieredBase<T>* vec, const char* name)
 {
+    JS::AutoTracingIndex index(trc);
     for (auto i : MakeRange(len)) {
         if (InternalGCMethods<T>::isMarkable(vec[i].get()))
-            DispatchToTracer(trc, ConvertToBase(vec[i].unsafeGet()), name, i);
+            DispatchToTracer(trc, ConvertToBase(vec[i].unsafeGet()), name);
+        ++index;
     }
 }
 
 template <typename T>
 void
 js::TraceRootRange(JSTracer* trc, size_t len, T* vec, const char* name)
 {
     JS_ROOT_MARKING_ASSERT(trc);
+    JS::AutoTracingIndex index(trc);
     for (auto i : MakeRange(len)) {
         if (InternalGCMethods<T>::isMarkable(vec[i]))
-            DispatchToTracer(trc, ConvertToBase(&vec[i]), name, i);
+            DispatchToTracer(trc, ConvertToBase(&vec[i]), name);
+        ++index;
     }
 }
 
 // Instantiate a copy of the Tracing templates for each derived type.
 #define INSTANTIATE_ALL_VALID_TRACE_FUNCTIONS(type) \
     template void js::TraceEdge<type>(JSTracer*, BarrieredBase<type>*, const char*); \
     template void js::TraceManuallyBarrieredEdge<type>(JSTracer*, type*, const char*); \
     template void js::TraceRoot<type>(JSTracer*, type*, const char*); \
@@ -428,52 +432,52 @@ FOR_EACH_GC_POINTER_TYPE(INSTANTIATE_ALL
 #undef INSTANTIATE_ALL_VALID_TRACE_FUNCTIONS
 
 template <typename T>
 void
 js::TraceManuallyBarrieredCrossCompartmentEdge(JSTracer* trc, JSObject* src, T* dst,
                                                const char* name)
 {
     if (ShouldMarkCrossCompartment(trc, src, *dst))
-        DispatchToTracer(trc, dst, name, -1);
+        DispatchToTracer(trc, dst, name);
 }
 template void js::TraceManuallyBarrieredCrossCompartmentEdge<JSObject*>(JSTracer*, JSObject*,
                                                                         JSObject**, const char*);
 template void js::TraceManuallyBarrieredCrossCompartmentEdge<JSScript*>(JSTracer*, JSObject*,
                                                                         JSScript**, const char*);
 
 template <typename T>
 void
 js::TraceCrossCompartmentEdge(JSTracer* trc, JSObject* src, BarrieredBase<T>* dst, const char* name)
 {
     if (ShouldMarkCrossCompartment(trc, src, dst->get()))
-        DispatchToTracer(trc, dst->unsafeGet(), name, -1);
+        DispatchToTracer(trc, dst->unsafeGet(), name);
 }
 template void js::TraceCrossCompartmentEdge<Value>(JSTracer*, JSObject*, BarrieredBase<Value>*,
                                                    const char*);
 
 // This method is responsible for dynamic dispatch to the real tracer
 // implementation. Consider replacing this choke point with virtual dispatch:
 // a sufficiently smart C++ compiler may be able to devirtualize some paths.
 template <typename T>
 void
-DispatchToTracer(JSTracer* trc, T* thingp, const char* name, size_t i)
+DispatchToTracer(JSTracer* trc, T* thingp, const char* name)
 {
 #define IS_SAME_TYPE_OR(name, type) mozilla::IsSame<type*, T>::value ||
     static_assert(
             FOR_EACH_GC_LAYOUT(IS_SAME_TYPE_OR)
             mozilla::IsSame<T, JS::Value>::value ||
             mozilla::IsSame<T, jsid>::value,
             "Only the base cell layout types are allowed into marking/tracing internals");
 #undef IS_SAME_TYPE_OR
     CheckMarkedThing(trc, *thingp);
 
     if (trc->isMarkingTracer())
         return DoMarking(static_cast<GCMarker*>(trc), *thingp);
-    return DoTracing(static_cast<JS::CallbackTracer*>(trc), thingp, name, i);
+    return DoTracing(static_cast<JS::CallbackTracer*>(trc), thingp, name);
 }
 
 template <typename T>
 static inline bool
 MustSkipMarking(T thing)
 {
     // Don't mark things outside a zone if we are in a per-zone GC.
     return !thing->zone()->isGCMarking();
@@ -537,182 +541,116 @@ void
 DoMarking<Value>(GCMarker* gcmarker, Value val)
 {
     if (val.isString())
         DoMarking(gcmarker, val.toString());
     else if (val.isObject())
         DoMarking(gcmarker, &val.toObject());
     else if (val.isSymbol())
         DoMarking(gcmarker, val.toSymbol());
-    else
-        gcmarker->clearTracingDetails();
 }
 
 template <>
 void
 DoMarking<jsid>(GCMarker* gcmarker, jsid id)
 {
     if (JSID_IS_STRING(id))
         DoMarking(gcmarker, JSID_TO_STRING(id));
     else if (JSID_IS_SYMBOL(id))
         DoMarking(gcmarker, JSID_TO_SYMBOL(id));
-    else
-        gcmarker->clearTracingDetails();
 }
 
 template <typename T>
 void
-DoTracing(JS::CallbackTracer* trc, T* thingp, const char* name, size_t i)
+DoTracing(JS::CallbackTracer* trc, T* thingp, const char* name)
 {
     JSGCTraceKind kind = MapTypeToTraceKind<typename mozilla::RemovePointer<T>::Type>::kind;
-    trc->setTracingIndex(name, i);
+    JS::AutoTracingName ctx(trc, name);
     trc->invoke((void**)thingp, kind);
-    trc->unsetTracingLocation();
 }
 
 template <>
 void
-DoTracing<Value>(JS::CallbackTracer* trc, Value* vp, const char* name, size_t i)
+DoTracing<Value>(JS::CallbackTracer* trc, Value* vp, const char* name)
 {
     if (vp->isObject()) {
         JSObject* prior = &vp->toObject();
         JSObject* obj = prior;
-        DoTracing(trc, &obj, name, i);
+        DoTracing(trc, &obj, name);
         if (obj != prior)
             vp->setObjectOrNull(obj);
     } else if (vp->isString()) {
         JSString* prior = vp->toString();
         JSString* str = prior;
-        DoTracing(trc, &str, name, i);
+        DoTracing(trc, &str, name);
         if (str != prior)
             vp->setString(str);
     } else if (vp->isSymbol()) {
         JS::Symbol* prior = vp->toSymbol();
         JS::Symbol* sym = prior;
-        DoTracing(trc, &sym, name, i);
+        DoTracing(trc, &sym, name);
         if (sym != prior)
             vp->setSymbol(sym);
-    } else {
-        /* Unset realLocation manually if we do not call MarkInternal. */
-        trc->unsetTracingLocation();
     }
 }
 
 template <>
 void
-DoTracing<jsid>(JS::CallbackTracer* trc, jsid* idp, const char* name, size_t i)
+DoTracing<jsid>(JS::CallbackTracer* trc, jsid* idp, const char* name)
 {
     if (JSID_IS_STRING(*idp)) {
         JSString* prior = JSID_TO_STRING(*idp);
         JSString* str = prior;
-        DoTracing(trc, &str, name, i);
+        DoTracing(trc, &str, name);
         if (str != prior)
             *idp = NON_INTEGER_ATOM_TO_JSID(reinterpret_cast<JSAtom*>(str));
     } else if (JSID_IS_SYMBOL(*idp)) {
         JS::Symbol* prior = JSID_TO_SYMBOL(*idp);
         JS::Symbol* sym = prior;
-        DoTracing(trc, &sym, name, i);
+        DoTracing(trc, &sym, name);
         if (sym != prior)
             *idp = SYMBOL_TO_JSID(sym);
-    } else {
-        /* Unset realLocation manually if we do not call MarkInternal. */
-        trc->unsetTracingLocation();
     }
 }
 
-template<typename T>
-static void
-MarkInternal(JSTracer* trc, T** thingp)
-{
-    T* thing = *thingp;
-    CheckMarkedThing(trc, thing);
-
-    if (trc->isMarkingTracer()) {
-        /*
-         * We may mark a Nursery thing outside the context of the
-         * MinorCollectionTracer because of a pre-barrier. The pre-barrier is
-         * not needed in this case because we perform a minor collection before
-         * each incremental slice.
-         */
-        if (IsInsideNursery(thing))
-            return;
-
-        /*
-         * Don't mark permanent atoms, as they may be associated with another
-         * runtime. Note that PushMarkStack() also checks this, but the tests
-         * and maybeAlive write below should only be done on the main thread.
-         */
-        if (ThingIsPermanentAtomOrWellKnownSymbol(thing))
-            return;
-
-        /*
-         * Don't mark things outside a compartment if we are in a
-         * per-compartment GC.
-         */
-        if (!thing->zone()->isGCMarking())
-            return;
-
-        PushMarkStack(AsGCMarker(trc), thing);
-        SetMaybeAliveFlag(thing);
-    } else {
-        trc->asCallbackTracer()->invoke((void**)thingp, MapTypeToTraceKind<T>::kind);
-        trc->unsetTracingLocation();
-    }
-
-    trc->clearTracingDetails();
-}
-
 namespace js {
 namespace gc {
 
 void
 MarkPermanentAtom(JSTracer* trc, JSAtom* atom, const char* name)
 {
-    trc->setTracingName(name);
-
     MOZ_ASSERT(atom->isPermanent());
 
+    // We have to mark permanent atoms through a special method because the
+    // default DoMarking implementation automatically skips them. Fortunatly,
+    // atoms cannot refer to other GC things, so they do not need to go through
+    // the mark stack and may simply be marked directly.
     CheckMarkedThing(trc, atom);
-
-    if (trc->isMarkingTracer()) {
-        // Atoms do not refer to other GC things so don't need to go on the mark stack.
-        // Additionally, PushMarkStack will ignore permanent atoms.
+    if (trc->isMarkingTracer())
         atom->markIfUnmarked();
-    } else {
-        void* thing = atom;
-        trc->asCallbackTracer()->invoke(&thing, JSTRACE_STRING);
-        MOZ_ASSERT(thing == atom);
-        trc->unsetTracingLocation();
-    }
-
-    trc->clearTracingDetails();
+    else
+        DoTracing(trc->asCallbackTracer(), reinterpret_cast<JSString**>(&atom), name);
 }
 
 void
 MarkWellKnownSymbol(JSTracer* trc, JS::Symbol* sym)
 {
     if (!sym)
         return;
+    MOZ_ASSERT(sym->isWellKnownSymbol());
 
-    trc->setTracingName("wellKnownSymbols");
-
-    MOZ_ASSERT(sym->isWellKnownSymbol());
+    // As per permanent atoms, the normal marking path is not adequate.
     CheckMarkedThing(trc, sym);
     if (trc->isMarkingTracer()) {
         // Permanent atoms are marked before well-known symbols.
         MOZ_ASSERT(sym->description()->isMarked());
         sym->markIfUnmarked();
     } else {
-        void* thing = sym;
-        trc->asCallbackTracer()->invoke(&thing, JSTRACE_SYMBOL);
-        MOZ_ASSERT(thing == sym);
-        trc->unsetTracingLocation();
+        DoTracing(trc->asCallbackTracer(), &sym, "wellKnownSymbol");
     }
-
-    trc->clearTracingDetails();
 }
 
 template <typename T>
 static inline void
 CheckIsMarkedThing(T* thingp)
 {
 #define IS_SAME_TYPE_OR(name, type) mozilla::IsSame<type*, T>::value ||
     static_assert(
@@ -969,81 +907,50 @@ js::gc::TraceManuallyBarrieredGenericPoi
 {
     MOZ_ASSERT(thingp);
     if (!*thingp)
         return;
     TraceManuallyBarrieredEdgeFunctor f;
     CallTyped(f, (*thingp)->getTraceKind(), trc, thingp, name);
 }
 
-/*** Value Marking ***/
-
-static inline void
-MarkValueInternal(JSTracer* trc, Value* v)
-{
-    if (v->isMarkable()) {
-        MOZ_ASSERT(v->toGCThing());
-        void* thing = v->toGCThing();
-        trc->setTracingLocation((void*)v);
-        if (v->isString()) {
-            JSString* str = static_cast<JSString*>(thing);
-            MarkInternal(trc, &str);
-            if (str != thing)
-                v->setString(str);
-        } else if (v->isObject()) {
-            JSObject* obj = static_cast<JSObject*>(thing);
-            MarkInternal(trc, &obj);
-            if (obj != thing)
-                v->setObjectOrNull(obj);
-        } else {
-            MOZ_ASSERT(v->isSymbol());
-            JS::Symbol* sym = static_cast<JS::Symbol*>(thing);
-            MarkInternal(trc, &sym);
-            if (sym != thing)
-                v->setSymbol(sym);
-        }
-    } else {
-        /* Unset realLocation manually if we do not call MarkInternal. */
-        trc->unsetTracingLocation();
-    }
-}
-
 /*** Type Marking ***/
 
 void
 TypeSet::MarkTypeRoot(JSTracer* trc, TypeSet::Type* v, const char* name)
 {
     JS_ROOT_MARKING_ASSERT(trc);
     MarkTypeUnbarriered(trc, v, name);
 }
 
 void
 TypeSet::MarkTypeUnbarriered(JSTracer* trc, TypeSet::Type* v, const char* name)
 {
-    trc->setTracingName(name);
     if (v->isSingletonUnchecked()) {
         JSObject* obj = v->singleton();
-        MarkInternal(trc, &obj);
+        DispatchToTracer(trc, &obj, name);
         *v = TypeSet::ObjectType(obj);
     } else if (v->isGroupUnchecked()) {
         ObjectGroup* group = v->group();
-        MarkInternal(trc, &group);
+        DispatchToTracer(trc, &group, name);
         *v = TypeSet::ObjectType(group);
     }
 }
 
 /*** Slot Marking ***/
 
 void
 gc::MarkObjectSlots(JSTracer* trc, NativeObject* obj, uint32_t start, uint32_t nslots)
 {
-    MOZ_ASSERT(obj->isNative());
+    JS::AutoTracingIndex index(trc, start);
     for (uint32_t i = start; i < (start + nslots); ++i) {
-        trc->setTracingDetails(GetObjectSlotName, obj, i);
-        MarkValueInternal(trc, obj->getSlotRef(i).unsafeGet());
+        HeapSlot& slot = obj->getSlotRef(i);
+        if (InternalGCMethods<Value>::isMarkable(slot))
+            DispatchToTracer(trc, slot.unsafeGet(), "object slot");
+        ++index;
     }
 }
 
 static bool
 ShouldMarkCrossCompartment(JSTracer* trc, JSObject* src, Cell* cell)
 {
     if (!trc->isMarkingTracer())
         return true;
--- a/js/src/gc/Marking.h
+++ b/js/src/gc/Marking.h
@@ -72,24 +72,16 @@ MarkWellKnownSymbol(JSTracer* trc, JS::S
 MOZ_ALWAYS_INLINE bool
 IsNullTaggedPointer(void* p)
 {
     return uintptr_t(p) < 32;
 }
 
 /*** Externally Typed Marking ***/
 
-/*
- * Note: this must only be called by the GC and only when we are tracing through
- * MarkRoots. It is explicitly for ConservativeStackMarking and should go away
- * after we transition to exact rooting.
- */
-void
-MarkKind(JSTracer* trc, void** thingp, JSGCTraceKind kind);
-
 void
 TraceGenericPointerRoot(JSTracer* trc, Cell** thingp, const char* name);
 
 void
 TraceManuallyBarrieredGenericPointerEdge(JSTracer* trc, Cell** thingp, const char* name);
 
 /*** Slot Marking ***/
 
@@ -162,17 +154,17 @@ class HashKeyRef : public BufferableRef
   public:
     HashKeyRef(Map* m, const Key& k) : map(m), key(k) {}
 
     void mark(JSTracer* trc) {
         Key prior = key;
         typename Map::Ptr p = map->lookup(key);
         if (!p)
             return;
-        trc->setTracingLocation(&*p);
+        JS::AutoOriginalTraceLocation reloc(trc, (void**)&*p);
         TraceManuallyBarrieredEdge(trc, &key, "HashKeyRef");
         map->rekeyIfMoved(prior, key);
     }
 };
 
 } /* namespace gc */
 
 void
--- a/js/src/gc/RootMarking.cpp
+++ b/js/src/gc/RootMarking.cpp
@@ -216,18 +216,18 @@ AutoGCRooter::trace(JSTracer* trc)
         TraceRootRange(trc, vector.length(), vector.begin(), "js::AutoScriptVector.vector");
         return;
       }
 
       case OBJOBJHASHMAP: {
         AutoObjectObjectHashMap::HashMapImpl& map = static_cast<AutoObjectObjectHashMap*>(this)->map;
         for (AutoObjectObjectHashMap::Enum e(map); !e.empty(); e.popFront()) {
             TraceRoot(trc, &e.front().value(), "AutoObjectObjectHashMap value");
-            trc->setTracingLocation((void*)&e.front().key());
             JSObject* key = e.front().key();
+            JS::AutoOriginalTraceLocation reloc(trc, &e.front().key());
             TraceRoot(trc, &key, "AutoObjectObjectHashMap key");
             if (key != e.front().key())
                 e.rekeyFront(key);
         }
         return;
       }
 
       case OBJU32HASHMAP: {
@@ -578,56 +578,44 @@ js::gc::GCRuntime::bufferGrayRoots()
 struct SetMaybeAliveFunctor {
     template <typename T>
     void operator()(Cell* cell) {
         SetMaybeAliveFlag<T>(static_cast<T*>(cell));
     }
 };
 
 void
-BufferGrayRootsTracer::appendGrayRoot(Cell* thing, JSGCTraceKind kind)
+BufferGrayRootsTracer::appendGrayRoot(TenuredCell* thing, JSGCTraceKind kind)
 {
     MOZ_ASSERT(runtime()->isHeapBusy());
 
     if (bufferingGrayRootsFailed)
         return;
 
-    GrayRoot root(thing, kind);
-#ifdef DEBUG
-    root.debugPrinter = debugPrinter();
-    root.debugPrintArg = debugPrintArg();
-    root.debugPrintIndex = debugPrintIndex();
-#endif
-
-    Zone* zone = TenuredCell::fromPointer(thing)->zone();
+    Zone* zone = thing->zone();
     if (zone->isCollecting()) {
         // See the comment on SetMaybeAliveFlag to see why we only do this for
         // objects and scripts. We rely on gray root buffering for this to work,
         // but we only need to worry about uncollected dead compartments during
         // incremental GCs (when we do gray root buffering).
         CallTyped(SetMaybeAliveFunctor(), kind, thing);
 
-        if (!zone->gcGrayRoots.append(root))
+        if (!zone->gcGrayRoots.append(thing))
             bufferingGrayRootsFailed = true;
     }
 }
 
 void
 GCRuntime::markBufferedGrayRoots(JS::Zone* zone)
 {
     MOZ_ASSERT(grayBufferState == GrayBufferState::Okay);
     MOZ_ASSERT(zone->isGCMarkingGray() || zone->isGCCompacting());
 
-    for (GrayRoot* elem = zone->gcGrayRoots.begin(); elem != zone->gcGrayRoots.end(); elem++) {
-#ifdef DEBUG
-        marker.setTracingDetails(elem->debugPrinter, elem->debugPrintArg, elem->debugPrintIndex);
-#endif
-        TraceManuallyBarrieredGenericPointerEdge(&marker, reinterpret_cast<Cell**>(&elem->thing),
-                                                 "buffered gray root");
-    }
+    for (auto cell : zone->gcGrayRoots)
+        TraceManuallyBarrieredGenericPointerEdge(&marker, &cell, "buffered gray root");
 }
 
 void
 GCRuntime::resetBufferedGrayRoots() const
 {
     MOZ_ASSERT(grayBufferState != GrayBufferState::Okay,
                "Do not clear the gray buffers unless we are Failed or becoming Unused");
     for (GCZonesIter zone(rt); !zone.done(); zone.next())
--- a/js/src/gc/Tracer.cpp
+++ b/js/src/gc/Tracer.cpp
@@ -97,17 +97,17 @@ JS_CallFunctionTracer(JSTracer* trc, JS:
 
 JS_PUBLIC_API(void)
 JS_CallTenuredObjectTracer(JSTracer* trc, JS::TenuredHeap<JSObject*>* objp, const char* name)
 {
     JSObject* obj = objp->getPtr();
     if (!obj)
         return;
 
-    trc->setTracingLocation((void*)objp);
+    JS::AutoOriginalTraceLocation reloc(trc, (void**)objp);
     TraceManuallyBarrieredEdge(trc, &obj, name);
 
     objp->setPtr(obj);
 }
 
 JS_PUBLIC_API(void)
 JS_TraceChildren(JSTracer* trc, void* thing, JSGCTraceKind kind)
 {
@@ -324,100 +324,40 @@ JS_GetTraceThingInfo(char* buf, size_t b
     }
     buf[bufsize - 1] = '\0';
 }
 
 JSTracer::JSTracer(JSRuntime* rt, TracerKindTag kindTag,
                    WeakMapTraceKind weakTraceKind /* = TraceWeakMapValues */)
   : runtime_(rt)
   , tag(kindTag)
-  , debugPrinter_(nullptr)
-  , debugPrintArg_(nullptr)
-  , debugPrintIndex_(size_t(-1))
   , eagerlyTraceWeakMaps_(weakTraceKind)
-#ifdef JS_GC_ZEAL
-  , realLocation_(nullptr)
-#endif
 {
 }
 
-bool
-JSTracer::hasTracingDetails() const
-{
-    return debugPrinter_ || debugPrintArg_;
-}
-
-const char*
-JSTracer::tracingName(const char* fallback) const
-{
-    MOZ_ASSERT(hasTracingDetails());
-    return debugPrinter_ ? fallback : (const char*)debugPrintArg_;
-}
-
 const char*
-JSTracer::getTracingEdgeName(char* buffer, size_t bufferSize)
+JS::CallbackTracer::getTracingEdgeName(char* buffer, size_t bufferSize)
 {
-    if (debugPrinter_) {
-        debugPrinter_(this, buffer, bufferSize);
-        return buffer;
-    }
-    if (debugPrintIndex_ != size_t(-1)) {
-        JS_snprintf(buffer, bufferSize, "%s[%lu]",
-                    (const char*)debugPrintArg_,
-                    debugPrintIndex_);
+    if (contextFunctor_) {
+        (*contextFunctor_)(this, buffer, bufferSize);
         return buffer;
     }
-    return (const char*)debugPrintArg_;
-}
-
-JSTraceNamePrinter
-JSTracer::debugPrinter() const
-{
-    return debugPrinter_;
-}
-
-const void*
-JSTracer::debugPrintArg() const
-{
-    return debugPrintArg_;
-}
-
-size_t
-JSTracer::debugPrintIndex() const
-{
-    return debugPrintIndex_;
+    if (contextIndex_ != InvalidIndex) {
+        JS_snprintf(buffer, bufferSize, "%s[%lu]", contextName_, contextIndex_);
+        return buffer;
+    }
+    return contextName_;
 }
 
 void
 JS::CallbackTracer::setTraceCallback(JSTraceCallback traceCallback)
 {
     callback = traceCallback;
 }
 
-#ifdef JS_GC_ZEAL
-void
-JSTracer::setTracingLocation(void* location)
-{
-    if (!realLocation_ || !location)
-        realLocation_ = location;
-}
-
-void
-JSTracer::unsetTracingLocation()
-{
-    realLocation_ = nullptr;
-}
-
-void**
-JSTracer::tracingLocation(void** thingp)
-{
-    return realLocation_ ? (void**)realLocation_ : thingp;
-}
-#endif
-
 bool
 MarkStack::init(JSGCMode gcMode)
 {
     setBaseCapacity(gcMode);
 
     MOZ_ASSERT(!stack_);
     uintptr_t* newStack = js_pod_malloc<uintptr_t>(baseCapacity_);
     if (!newStack)
--- a/js/src/gc/Tracer.h
+++ b/js/src/gc/Tracer.h
@@ -299,18 +299,16 @@ class GCMarker : public JSTracer
     bool restoreValueArray(NativeObject* obj, void** vpp, void** endp);
     void saveValueRanges();
     inline void processMarkStackTop(SliceBudget& budget);
     void processMarkStackOther(uintptr_t tag, uintptr_t addr);
 
     void markAndScanString(JSObject* source, JSString* str);
     void markAndScanSymbol(JSObject* source, JS::Symbol* sym);
 
-    void appendGrayRoot(void* thing, JSGCTraceKind kind);
-
     /* The color is only applied to objects and functions. */
     uint32_t color;
 
     /* Pointer to the top of the stack of arenas we are delaying marking on. */
     js::gc::ArenaHeader* unmarkedArenaStackTop;
 
     /* Count of arenas that are currently in the stack. */
     mozilla::DebugOnly<size_t> markLaterArenas;
@@ -327,26 +325,26 @@ class GCMarker : public JSTracer
 
 // Append traced things to a buffer on the zone for use later in the GC.
 // See the comment in GCRuntime.h above grayBufferState for details.
 class BufferGrayRootsTracer : public JS::CallbackTracer
 {
     // Set to false if we OOM while buffering gray roots.
     bool bufferingGrayRootsFailed;
 
-    void appendGrayRoot(gc::Cell* thing, JSGCTraceKind kind);
+    void appendGrayRoot(gc::TenuredCell* thing, JSGCTraceKind kind);
 
   public:
     explicit BufferGrayRootsTracer(JSRuntime* rt)
       : JS::CallbackTracer(rt, grayTraceCallback), bufferingGrayRootsFailed(false)
     {}
 
     static void grayTraceCallback(JS::CallbackTracer* trc, void** thingp, JSGCTraceKind kind) {
-        static_cast<BufferGrayRootsTracer*>(trc)->appendGrayRoot(static_cast<gc::Cell*>(*thingp),
-                                                                kind);
+        auto tracer = static_cast<BufferGrayRootsTracer*>(trc);
+        tracer->appendGrayRoot(gc::TenuredCell::fromPointer(*thingp), kind);
     }
 
     bool failed() const { return bufferingGrayRootsFailed; }
 };
 
 void
 SetMarkStackLimit(JSRuntime* rt, size_t limit);
 
--- a/js/src/gc/Verifier.cpp
+++ b/js/src/gc/Verifier.cpp
@@ -128,17 +128,17 @@ AccumulateEdge(JS::CallbackTracer* jstrc
         return;
     }
 
     VerifyNode* node = trc->curnode;
     uint32_t i = node->count;
 
     node->edges[i].thing = *thingp;
     node->edges[i].kind = kind;
-    node->edges[i].label = trc->tracingName("<unknown>");
+    node->edges[i].label = trc->contextName();
     node->count++;
 }
 
 static VerifyNode*
 MakeNode(VerifyPreTracer* trc, void* thing, JSGCTraceKind kind)
 {
     NodeMap::AddPtr p = trc->nodemap.lookupForAdd(thing);
     if (!p) {
@@ -307,19 +307,17 @@ AssertMarkedOrAllocated(const EdgeValue&
 
     // Permanent atoms and well-known symbols aren't marked during graph traversal.
     if (edge.kind == JSTRACE_STRING && static_cast<JSString*>(edge.thing)->isPermanentAtom())
         return;
     if (edge.kind == JSTRACE_SYMBOL && static_cast<JS::Symbol*>(edge.thing)->isWellKnownSymbol())
         return;
 
     char msgbuf[1024];
-    const char* label = edge.label;
-
-    JS_snprintf(msgbuf, sizeof(msgbuf), "[barrier verifier] Unmarked edge: %s", label);
+    JS_snprintf(msgbuf, sizeof(msgbuf), "[barrier verifier] Unmarked edge: %s", edge.label);
     MOZ_ReportAssertionFailure(msgbuf, __FILE__, __LINE__);
     MOZ_CRASH();
 }
 
 bool
 gc::GCRuntime::endVerifyPreBarriers()
 {
     VerifyPreTracer* trc = (VerifyPreTracer*)verifyPreData;
@@ -384,17 +382,17 @@ struct VerifyPostTracer : JS::CallbackTr
 {
     /* The gcNumber when the verification began. */
     uint64_t number;
 
     /* This counts up to gcZealFrequency to decide whether to verify. */
     int count;
 
     /* The set of edges in the StoreBuffer at the end of verification. */
-    typedef HashSet<void**, PointerHasher<void**, 3>, SystemAllocPolicy> EdgeSet;
+    typedef HashSet<void*const*, PointerHasher<void*const*, 3>, SystemAllocPolicy> EdgeSet;
     EdgeSet* edges;
 
     VerifyPostTracer(JSRuntime* rt, JSTraceCallback callback)
       : JS::CallbackTracer(rt, callback), number(rt->gc.gcNumber()), count(0)
     {}
 };
 
 /*
@@ -433,23 +431,23 @@ PostVerifierCollectStoreBufferEdges(JS::
     if (trc->runtime()->gc.nursery.isInside(thingp) || !IsInsideNursery(dst))
         return;
 
     /*
      * Values will be unpacked to the stack before getting here. However, the
      * only things that enter this callback are marked by the store buffer. The
      * store buffer ensures that the real tracing location is set correctly.
      */
-    void** loc = trc->tracingLocation(thingp);
+    void*const* loc = trc->tracingLocation(thingp);
 
     trc->edges->put(loc);
 }
 
 static void
-AssertStoreBufferContainsEdge(VerifyPostTracer::EdgeSet* edges, void** loc, JSObject* dst)
+AssertStoreBufferContainsEdge(VerifyPostTracer::EdgeSet* edges, void*const* loc, JSObject* dst)
 {
     if (edges->has(loc))
         return;
 
     char msgbuf[1024];
     JS_snprintf(msgbuf, sizeof(msgbuf), "[post-barrier verifier] Missing edge @ %p to %p",
                 (void*)loc, (void*)dst);
     MOZ_ReportAssertionFailure(msgbuf, __FILE__, __LINE__);
@@ -472,17 +470,17 @@ PostVerifierVisitEdge(JS::CallbackTracer
         return;
 
     /*
      * Values will be unpacked to the stack before getting here. However, the
      * only things that enter this callback are marked by the JS_TraceChildren
      * below. Since JSObject::markChildren handles this, the real trace
      * location will be set correctly in these cases.
      */
-    void** loc = trc->tracingLocation(thingp);
+    void*const* loc = trc->tracingLocation(thingp);
 
     AssertStoreBufferContainsEdge(trc->edges, loc, dst);
 }
 
 bool
 js::gc::GCRuntime::endVerifyPostBarriers()
 {
     VerifyPostTracer* trc = (VerifyPostTracer*)verifyPostData;
--- a/js/src/gc/Zone.h
+++ b/js/src/gc/Zone.h
@@ -243,17 +243,17 @@ struct Zone : public JS::shadow::Zone,
 
     js::TypeZone types;
 
     // The set of compartments in this zone.
     typedef js::Vector<JSCompartment*, 1, js::SystemAllocPolicy> CompartmentVector;
     CompartmentVector compartments;
 
     // This compartment's gray roots.
-    typedef js::Vector<js::GrayRoot, 0, js::SystemAllocPolicy> GrayRootVector;
+    typedef js::Vector<js::gc::Cell*, 0, js::SystemAllocPolicy> GrayRootVector;
     GrayRootVector gcGrayRoots;
 
     // A set of edges from this zone to other zones.
     //
     // This is used during GC while calculating zone groups to record edges that
     // can't be determined by examining this zone by itself.
     ZoneSet gcZoneGroupEdges;
 
--- a/js/src/jscntxt.cpp
+++ b/js/src/jscntxt.cpp
@@ -81,17 +81,17 @@ js::AutoCycleDetector::~AutoCycleDetecto
     }
 }
 
 void
 js::TraceCycleDetectionSet(JSTracer* trc, js::ObjectSet& set)
 {
     for (js::ObjectSet::Enum e(set); !e.empty(); e.popFront()) {
         JSObject* key = e.front();
-        trc->setTracingLocation((void*)&e.front());
+        JS::AutoOriginalTraceLocation reloc(trc, &e.front());
         TraceRoot(trc, &key, "cycle detector table entry");
         if (key != e.front())
             e.rekeyFront(key);
     }
 }
 
 JSContext*
 js::NewContext(JSRuntime* rt, size_t stackChunkSize)
--- a/js/src/jsgc.h
+++ b/js/src/jsgc.h
@@ -1084,29 +1084,16 @@ struct GCChunkHasher {
         MOZ_ASSERT(!(uintptr_t(k) & gc::ChunkMask));
         MOZ_ASSERT(!(uintptr_t(l) & gc::ChunkMask));
         return k == l;
     }
 };
 
 typedef HashSet<js::gc::Chunk*, GCChunkHasher, SystemAllocPolicy> GCChunkSet;
 
-struct GrayRoot {
-    void* thing;
-    JSGCTraceKind kind;
-#ifdef DEBUG
-    JSTraceNamePrinter debugPrinter;
-    const void* debugPrintArg;
-    size_t debugPrintIndex;
-#endif
-
-    GrayRoot(void* thing, JSGCTraceKind kind)
-        : thing(thing), kind(kind) {}
-};
-
 typedef void (*IterateChunkCallback)(JSRuntime* rt, void* data, gc::Chunk* chunk);
 typedef void (*IterateZoneCallback)(JSRuntime* rt, void* data, JS::Zone* zone);
 typedef void (*IterateArenaCallback)(JSRuntime* rt, void* data, gc::Arena* arena,
                                      JSGCTraceKind traceKind, size_t thingSize);
 typedef void (*IterateCellCallback)(JSRuntime* rt, void* data, void* thing,
                                     JSGCTraceKind traceKind, size_t thingSize);
 
 /*
--- a/js/src/jsobj.cpp
+++ b/js/src/jsobj.cpp
@@ -3485,23 +3485,31 @@ js::ToObjectSlow(JSContext* cx, JS::Hand
                                  val.isNull() ? "null" : "undefined", "object");
         }
         return nullptr;
     }
 
     return PrimitiveToObject(cx, val);
 }
 
-void
-js::GetObjectSlotName(JSTracer* trc, char* buf, size_t bufsize)
+class GetObjectSlotNameFunctor : public JS::CallbackTracer::ContextFunctor
 {
-    MOZ_ASSERT(trc->debugPrinter() == GetObjectSlotName);
-
-    JSObject* obj = (JSObject*)trc->debugPrintArg();
-    uint32_t slot = uint32_t(trc->debugPrintIndex());
+    JSObject* obj;
+
+  public:
+    explicit GetObjectSlotNameFunctor(JSObject* ctx) : obj(ctx) {}
+    virtual void operator()(JS::CallbackTracer* trc, char* buf, size_t bufsize) override;
+};
+
+void
+GetObjectSlotNameFunctor::operator()(JS::CallbackTracer* trc, char* buf, size_t bufsize)
+{
+    MOZ_ASSERT(trc->contextIndex() != JS::CallbackTracer::InvalidIndex);
+
+    uint32_t slot = uint32_t(trc->contextIndex());
 
     Shape* shape;
     if (obj->isNative()) {
         shape = obj->as<NativeObject>().lastProperty();
         while (shape && (!shape->hasSlot() || shape->slot() != slot))
             shape = shape->previous();
     } else {
         shape = nullptr;
@@ -4064,17 +4072,21 @@ JSObject::markChildren(JSTracer* trc)
     if (clasp->trace)
         clasp->trace(trc, this);
 
     if (clasp->isNative()) {
         NativeObject* nobj = &as<NativeObject>();
 
         TraceEdge(trc, &nobj->shape_, "shape");
 
-        MarkObjectSlots(trc, nobj, 0, nobj->slotSpan());
+        {
+            GetObjectSlotNameFunctor func(nobj);
+            JS::AutoTracingDetails ctx(trc, func);
+            MarkObjectSlots(trc, nobj, 0, nobj->slotSpan());
+        }
 
         do {
             if (nobj->denseElementsAreCopyOnWrite()) {
                 HeapPtrNativeObject& owner = nobj->getElementsHeader()->ownerObject();
                 if (owner != nobj) {
                     TraceEdge(trc, &owner, "objectElementsOwner");
                     break;
                 }
--- a/js/src/jsobj.h
+++ b/js/src/jsobj.h
@@ -1253,19 +1253,16 @@ ToObjectFromStack(JSContext* cx, HandleV
 
 template<XDRMode mode>
 bool
 XDRObjectLiteral(XDRState<mode>* xdr, MutableHandleObject obj);
 
 extern JSObject*
 CloneObjectLiteral(JSContext* cx, HandleObject srcObj);
 
-extern void
-GetObjectSlotName(JSTracer* trc, char* buf, size_t bufsize);
-
 extern bool
 ReportGetterOnlyAssignment(JSContext* cx, bool strict);
 
 extern JSObject*
 NonNullObject(JSContext* cx, const Value& v);
 
 extern const char*
 InformalValueTypeName(const Value& v);
--- a/js/src/vm/ObjectGroup.cpp
+++ b/js/src/vm/ObjectGroup.cpp
@@ -411,17 +411,17 @@ class ObjectGroupCompartment::NewTableRe
 
   public:
     NewTableRef(NewTable* table, const Class* clasp, JSObject* proto, JSObject* associated)
         : table(table), clasp(clasp), proto(proto), associated(associated)
     {}
 
     void mark(JSTracer* trc) {
         JSObject* prior = proto;
-        trc->setTracingLocation(&*prior);
+        JS::AutoOriginalTraceLocation reloc(trc, &proto);
         TraceManuallyBarrieredEdge(trc, &proto, "newObjectGroups set prototype");
         if (prior == proto)
             return;
 
         NewTable::Ptr p = table->lookup(NewTable::Lookup(clasp, TaggedProto(prior),
                                                          TaggedProto(proto),
                                                          associated));
         if (!p)
--- a/xpcom/base/CycleCollectedJSRuntime.cpp
+++ b/xpcom/base/CycleCollectedJSRuntime.cpp
@@ -399,30 +399,19 @@ NoteJSChild(JS::CallbackTracer* aTrc, JS
    * AddToCCKind is true, the recursion terminates immediately as we just add
    * |thing| to the CC graph. So overflow is only possible when there are long
    * chains of non-AddToCCKind GC things. Currently, this only can happen via
    * shape parent pointers. The special JSTRACE_SHAPE case below handles
    * parent pointers iteratively, rather than recursively, to avoid overflow.
    */
   if (AddToCCKind(aThing.kind())) {
     if (MOZ_UNLIKELY(tracer->mCb.WantDebugInfo())) {
-      // based on DumpNotify in jsapi.cpp
-      if (tracer->debugPrinter()) {
-        char buffer[200];
-        tracer->debugPrinter()(aTrc, buffer, sizeof(buffer));
-        tracer->mCb.NoteNextEdgeName(buffer);
-      } else if (tracer->debugPrintIndex() != (size_t)-1) {
-        char buffer[200];
-        JS_snprintf(buffer, sizeof(buffer), "%s[%lu]",
-                    static_cast<const char*>(tracer->debugPrintArg()),
-                    tracer->debugPrintIndex());
-        tracer->mCb.NoteNextEdgeName(buffer);
-      } else {
-        tracer->mCb.NoteNextEdgeName(static_cast<const char*>(tracer->debugPrintArg()));
-      }
+      char buffer[200];
+      tracer->getTracingEdgeName(buffer, sizeof(buffer));
+      tracer->mCb.NoteNextEdgeName(buffer);
     }
     if (aThing.isObject()) {
       tracer->mCb.NoteJSObject(aThing.toObject());
     } else {
       tracer->mCb.NoteJSScript(aThing.toScript());
     }
   } else if (aThing.isShape()) {
     JS_TraceShapeCycleCollectorChildren(aTrc, aThing);