Bug 1467842 - Refactor heap state RAII classes r=sfink
authorJon Coppeard <jcoppeard@mozilla.com>
Tue, 19 Jun 2018 11:34:59 +0100
changeset 422970 4f50305f72aba391db457322d078d4908221e20e
parent 422969 84b05310b2c7a6b0c585add58fdb27e635e70963
child 422971 95b1c86a8b55c8ef5b3c955a3ab592e3c658f3fa
push id34160
push userdluca@mozilla.com
push dateTue, 19 Jun 2018 21:55:15 +0000
treeherdermozilla-central@e429320fcdd2 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssfink
bugs1467842
milestone62.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 1467842 - Refactor heap state RAII classes r=sfink
js/src/gc/GC.cpp
js/src/gc/GCInternals.h
js/src/gc/GCRuntime.h
js/src/gc/Nursery.cpp
js/src/gc/RootMarking.cpp
js/src/gc/Verifier.cpp
js/src/jsfriendapi.cpp
js/src/vm/BytecodeUtil.cpp
js/src/vm/Runtime.h
--- a/js/src/gc/GC.cpp
+++ b/js/src/gc/GC.cpp
@@ -2963,17 +2963,17 @@ GCRuntime::updateZonePointersToRelocated
     for (CompartmentsInZoneIter comp(zone); !comp.done(); comp.next())
         callWeakPointerCompartmentCallbacks(comp);
 }
 
 /*
  * Update runtime-wide pointers to relocated cells.
  */
 void
-GCRuntime::updateRuntimePointersToRelocatedCells(AutoTraceSession& session)
+GCRuntime::updateRuntimePointersToRelocatedCells(AutoGCSession& session)
 {
     MOZ_ASSERT(!rt->isBeingDestroyed());
 
     gcstats::AutoPhase ap1(stats(), gcstats::PhaseKind::COMPACT_UPDATE);
     MovingTracer trc(rt);
 
     Compartment::fixupCrossCompartmentWrappersAfterMovingGC(&trc);
 
@@ -4378,17 +4378,17 @@ UnmarkCollectedZones(GCParallelTask* tas
 
 static void
 BufferGrayRoots(GCParallelTask* task)
 {
     task->runtime()->gc.bufferGrayRoots();
 }
 
 bool
-GCRuntime::beginMarkPhase(JS::gcreason::Reason reason, AutoTraceSession& session)
+GCRuntime::beginMarkPhase(JS::gcreason::Reason reason, AutoGCSession& session)
 {
 #ifdef DEBUG
     if (fullCompartmentChecks)
         checkForCompartmentMismatches();
 #endif
 
     if (!prepareZonesForCollection(reason, &isFull.ref()))
         return false;
@@ -4658,17 +4658,17 @@ struct GCChunkHasher {
     }
 };
 
 class js::gc::MarkingValidator
 {
   public:
     explicit MarkingValidator(GCRuntime* gc);
     ~MarkingValidator();
-    void nonIncrementalMark(AutoTraceSession& session);
+    void nonIncrementalMark(AutoGCSession& session);
     void validate();
 
   private:
     GCRuntime* gc;
     bool initialized;
 
     typedef HashMap<Chunk*, ChunkBitmap*, GCChunkHasher, SystemAllocPolicy> BitmapMap;
     BitmapMap map;
@@ -4684,17 +4684,17 @@ js::gc::MarkingValidator::~MarkingValida
     if (!map.initialized())
         return;
 
     for (BitmapMap::Range r(map.all()); !r.empty(); r.popFront())
         js_delete(r.front().value());
 }
 
 void
-js::gc::MarkingValidator::nonIncrementalMark(AutoTraceSession& session)
+js::gc::MarkingValidator::nonIncrementalMark(AutoGCSession& session)
 {
     /*
      * Perform a non-incremental mark for all collecting zones and record
      * the results for later comparison.
      *
      * Currently this does not validate gray marking.
      */
 
@@ -4900,17 +4900,17 @@ js::gc::MarkingValidator::validate()
             }
         }
     }
 }
 
 #endif // JS_GC_ZEAL
 
 void
-GCRuntime::computeNonIncrementalMarkingForValidation(AutoTraceSession& session)
+GCRuntime::computeNonIncrementalMarkingForValidation(AutoGCSession& session)
 {
 #ifdef JS_GC_ZEAL
     MOZ_ASSERT(!markingValidator);
     if (isIncremental && hasZealMode(ZealMode::IncrementalMarkingValidator))
         markingValidator = js_new<MarkingValidator>(this);
     if (markingValidator)
         markingValidator->nonIncrementalMark(session);
 #endif
@@ -5876,17 +5876,17 @@ GCRuntime::endSweepingSweepGroup(FreeOp*
         queueZonesForBackgroundSweep(zones);
     else
         sweepBackgroundThings(zones, blocksToFreeAfterSweeping.ref());
 
     return Finished;
 }
 
 void
-GCRuntime::beginSweepPhase(JS::gcreason::Reason reason, AutoTraceSession& session)
+GCRuntime::beginSweepPhase(JS::gcreason::Reason reason, AutoGCSession& session)
 {
     /*
      * Sweep phase.
      *
      * Finalize as we sweep, outside of lock but with RuntimeHeapIsBusy()
      * true so that any attempt to allocate a GC-thing from a finalizer will
      * fail, rather than nest badly and leave the unmarked newborn to be swept.
      */
@@ -6751,17 +6751,17 @@ GCRuntime::beginCompactPhase()
     }
 
     MOZ_ASSERT(!relocatedArenasToRelease);
     startedCompacting = true;
 }
 
 IncrementalProgress
 GCRuntime::compactPhase(JS::gcreason::Reason reason, SliceBudget& sliceBudget,
-                        AutoTraceSession& session)
+                        AutoGCSession& session)
 {
     assertBackgroundSweepingFinished();
     MOZ_ASSERT(startedCompacting);
 
     gcstats::AutoPhase ap(stats(), gcstats::PhaseKind::COMPACT);
 
     // TODO: JSScripts can move. If the sampler interrupts the GC in the
     // middle of relocating an arena, invalid JSScript pointers may be
@@ -6862,48 +6862,44 @@ HeapStateToLabel(JS::HeapState heapState
       case JS::HeapState::CycleCollecting:
         MOZ_CRASH("Should never have an Idle or CC heap state when pushing GC profiling stack frames!");
     }
     MOZ_ASSERT_UNREACHABLE("Should have exhausted every JS::HeapState variant!");
     return nullptr;
 }
 
 /* Start a new heap session. */
-AutoTraceSession::AutoTraceSession(JSRuntime* rt, JS::HeapState heapState)
+AutoHeapSession::AutoHeapSession(JSRuntime* rt, JS::HeapState heapState)
   : runtime(rt),
     prevState(rt->heapState_),
     profilingStackFrame(rt->mainContextFromOwnThread(), HeapStateToLabel(heapState),
                         ProfilingStackFrame::Category::GCCC)
 {
     MOZ_ASSERT(CurrentThreadCanAccessRuntime(rt));
     MOZ_ASSERT(prevState == JS::HeapState::Idle);
     MOZ_ASSERT(heapState != JS::HeapState::Idle);
     MOZ_ASSERT_IF(heapState == JS::HeapState::MajorCollecting, rt->gc.nursery().isEmpty());
 
-    // If we are not performing a collection, take the exclusive access lock.
-    if (heapState == JS::HeapState::Tracing)
-        maybeLock.emplace(rt);
-
     rt->heapState_ = heapState;
 }
 
-AutoTraceSession::~AutoTraceSession()
+AutoHeapSession::~AutoHeapSession()
 {
     MOZ_ASSERT(JS::RuntimeHeapIsBusy());
     runtime->heapState_ = prevState;
 }
 
 JS_PUBLIC_API(JS::HeapState)
 JS::RuntimeHeapState()
 {
     return TlsContext.get()->runtime()->heapState();
 }
 
 GCRuntime::IncrementalResult
-GCRuntime::resetIncrementalGC(gc::AbortReason reason, AutoTraceSession& session)
+GCRuntime::resetIncrementalGC(gc::AbortReason reason, AutoGCSession& session)
 {
     MOZ_ASSERT(reason != gc::AbortReason::None);
 
     switch (incrementalState) {
       case State::NotActive:
           return IncrementalResult::Ok;
 
       case State::MarkRoots:
@@ -7085,17 +7081,17 @@ ShouldCleanUpEverything(JS::gcreason::Re
     // During shutdown, we must clean everything up, for the sake of leak
     // detection. When a runtime has no contexts, or we're doing a GC before a
     // shutdown CC, those are strong indications that we're shutting down.
     return IsShutdownGC(reason) || gckind == GC_SHRINK;
 }
 
 void
 GCRuntime::incrementalCollectSlice(SliceBudget& budget, JS::gcreason::Reason reason,
-                                   AutoTraceSession& session)
+                                   AutoGCSession& session)
 {
     AutoDisableBarriers disableBarriers(rt);
 
     bool destroyingRuntime = (reason == JS::gcreason::DESTROY_RUNTIME);
 
     initialState = incrementalState;
 
 #ifdef JS_GC_ZEAL
@@ -7314,17 +7310,17 @@ CheckZoneIsScheduled(Zone* zone, JS::gcr
     }
     fflush(stderr);
     MOZ_CRASH("Zone not scheduled");
 #endif
 }
 
 GCRuntime::IncrementalResult
 GCRuntime::budgetIncrementalGC(bool nonincrementalByAPI, JS::gcreason::Reason reason,
-                               SliceBudget& budget, AutoTraceSession& session)
+                               SliceBudget& budget, AutoGCSession& session)
 {
     if (nonincrementalByAPI) {
         stats().nonincremental(gc::AbortReason::NonIncrementalRequested);
         budget.makeUnlimited();
 
         // Reset any in progress incremental GC if this was triggered via the
         // API. This isn't required for correctness, but sometimes during tests
         // the caller expects this GC to collect certain objects, and we need
@@ -7481,17 +7477,17 @@ GCRuntime::gcCycle(bool nonincrementalBy
 {
     // Note that GC callbacks are allowed to re-enter GC.
     AutoCallGCCallbacks callCallbacks(*this);
 
     gcstats::AutoGCSlice agc(stats(), scanZonesBeforeGC(), invocationKind, budget, reason);
 
     minorGC(reason, gcstats::PhaseKind::EVICT_NURSERY_FOR_MAJOR_GC);
 
-    AutoTraceSession session(rt, JS::HeapState::MajorCollecting);
+    AutoGCSession session(rt, JS::HeapState::MajorCollecting);
 
     majorGCTriggerReason = JS::gcreason::NO_REASON;
 
     number++;
     if (!isIncrementalGCInProgress())
         incMajorGcNumber();
 
     // It's ok if threads other than the main thread have suppressGC set, as
@@ -7960,21 +7956,16 @@ js::gc::FinishGC(JSContext* cx)
     if (JS::IsIncrementalGCInProgress(cx)) {
         JS::PrepareForIncrementalGC(cx);
         JS::FinishIncrementalGC(cx, JS::gcreason::API);
     }
 
     cx->nursery().waitBackgroundFreeEnd();
 }
 
-AutoPrepareForTracing::AutoPrepareForTracing(JSContext* cx)
-  : finishGC(cx),
-    session(cx->runtime())
-{}
-
 Realm*
 js::NewRealm(JSContext* cx, JSPrincipals* principals, const JS::RealmOptions& options)
 {
     JSRuntime* rt = cx->runtime();
     JS_AbortIfWrongThread(cx);
 
     UniquePtr<Zone> zoneHolder;
     UniquePtr<Compartment> compHolder;
--- a/js/src/gc/GCInternals.h
+++ b/js/src/gc/GCInternals.h
@@ -45,66 +45,77 @@ class MOZ_RAII AutoCheckCanAccessAtomsDu
         runtime->setOffThreadParsingBlocked(false);
     }
 #else
   public:
     explicit AutoCheckCanAccessAtomsDuringGC(JSRuntime* rt) {}
 #endif
 };
 
-class MOZ_RAII AutoTraceSession
+// Abstract base class for exclusive heap access for tracing or GC.
+class MOZ_RAII AutoHeapSession
 {
   public:
-    explicit AutoTraceSession(JSRuntime* rt, JS::HeapState state = JS::HeapState::Tracing);
-    ~AutoTraceSession();
+    ~AutoHeapSession();
 
-    // Constructing an AutoTraceSession takes the exclusive access lock unless
-    // the session is being used for GC.
-    mozilla::Maybe<AutoLockForExclusiveAccess> maybeLock;
+  protected:
+    AutoHeapSession(JSRuntime* rt, JS::HeapState state);
+
+  private:
+    AutoHeapSession(const AutoHeapSession&) = delete;
+    void operator=(const AutoHeapSession&) = delete;
 
-    // During a GC we can check that it's not possible for anything else to be
-    // using the atoms zone.
-    mozilla::Maybe<AutoCheckCanAccessAtomsDuringGC> maybeCheckAtomsAccess;
+    JSRuntime* runtime;
+    JS::HeapState prevState;
+    AutoGeckoProfilerEntry profilingStackFrame;
+};
 
-    AutoLockForExclusiveAccess& lock() {
-        return maybeLock.ref();
-    }
+class MOZ_RAII AutoGCSession : public AutoHeapSession
+{
+  public:
+    explicit AutoGCSession(JSRuntime* rt, JS::HeapState state)
+      : AutoHeapSession(rt, state)
+    {}
 
     AutoCheckCanAccessAtomsDuringGC& checkAtomsAccess() {
         return maybeCheckAtomsAccess.ref();
     }
 
-  protected:
-    JSRuntime* runtime;
+    // During a GC we can check that it's not possible for anything else to be
+    // using the atoms zone.
+    mozilla::Maybe<AutoCheckCanAccessAtomsDuringGC> maybeCheckAtomsAccess;
+};
 
-  private:
-    AutoTraceSession(const AutoTraceSession&) = delete;
-    void operator=(const AutoTraceSession&) = delete;
-
-    JS::HeapState prevState;
-    AutoGeckoProfilerEntry profilingStackFrame;
+class MOZ_RAII AutoTraceSession : public AutoLockForExclusiveAccess,
+                                  public AutoHeapSession
+{
+  public:
+    explicit AutoTraceSession(JSRuntime* rt)
+      : AutoLockForExclusiveAccess(rt),
+        AutoHeapSession(rt, JS::HeapState::Tracing)
+    {}
 };
 
-/*
- * This class should be used by any code that needs to exclusive access to the
- * heap in order to trace through it.
- */
-struct MOZ_RAII AutoPrepareForTracing
+struct MOZ_RAII AutoFinishGC
 {
-    struct AutoFinishGC
-    {
-        explicit AutoFinishGC(JSContext* cx) {
-            FinishGC(cx);
-        }
-    };
+    explicit AutoFinishGC(JSContext* cx) {
+        FinishGC(cx);
+    }
+};
 
-    AutoFinishGC finishGC;
-    AutoTraceSession session;
-
-    explicit AutoPrepareForTracing(JSContext* cx);
+// This class should be used by any code that needs to exclusive access to the
+// heap in order to trace through it.
+class MOZ_RAII AutoPrepareForTracing : private AutoFinishGC,
+                                       public AutoTraceSession
+{
+  public:
+    explicit AutoPrepareForTracing(JSContext* cx)
+      : AutoFinishGC(cx),
+        AutoTraceSession(cx->runtime())
+    {}
 };
 
 AbortReason
 IsIncrementalGCUnsafe(JSRuntime* rt);
 
 #ifdef JS_GC_ZEAL
 
 class MOZ_RAII AutoStopVerifyingBarriers
--- a/js/src/gc/GCRuntime.h
+++ b/js/src/gc/GCRuntime.h
@@ -33,16 +33,17 @@ class AutoLockHelperThreadState;
 class VerifyPreTracer;
 
 namespace gc {
 
 using BlackGrayEdgeVector = Vector<TenuredCell*, 0, SystemAllocPolicy>;
 using ZoneVector = Vector<JS::Zone*, 4, SystemAllocPolicy>;
 
 class AutoCallGCCallbacks;
+class AutoGCSession;
 class AutoRunParallelTask;
 class AutoTraceSession;
 class MarkingValidator;
 struct MovingTracer;
 enum class ShouldCheckThresholds;
 class SweepGroupsIter;
 class WeakCacheSweepIterator;
 
@@ -260,17 +261,17 @@ class GCRuntime
     void runDebugGC();
     void notifyRootsRemoved();
 
     enum TraceOrMarkRuntime {
         TraceRuntime,
         MarkRuntime
     };
     void traceRuntime(JSTracer* trc, AutoTraceSession& session);
-    void traceRuntimeForMinorGC(JSTracer* trc, AutoTraceSession& session);
+    void traceRuntimeForMinorGC(JSTracer* trc, AutoGCSession& session);
 
     void purgeRuntimeForMinorGC();
 
     void shrinkBuffers();
     void onOutOfMallocMemory();
     void onOutOfMallocMemory(const AutoLockGC& lock);
 
 #ifdef JS_GC_ZEAL
@@ -536,60 +537,60 @@ class GCRuntime
 
     friend class BackgroundAllocTask;
     bool wantBackgroundAllocation(const AutoLockGC& lock) const;
     void startBackgroundAllocTaskIfIdle();
 
     void requestMajorGC(JS::gcreason::Reason reason);
     SliceBudget defaultBudget(JS::gcreason::Reason reason, int64_t millis);
     IncrementalResult budgetIncrementalGC(bool nonincrementalByAPI, JS::gcreason::Reason reason,
-                                          SliceBudget& budget, AutoTraceSession& session);
-    IncrementalResult resetIncrementalGC(AbortReason reason, AutoTraceSession& session);
+                                          SliceBudget& budget, AutoGCSession& session);
+    IncrementalResult resetIncrementalGC(AbortReason reason, AutoGCSession& session);
 
     // Assert if the system state is such that we should never
     // receive a request to do GC work.
     void checkCanCallAPI();
 
     // Check if the system state is such that GC has been supressed
     // or otherwise delayed.
     MOZ_MUST_USE bool checkIfGCAllowedInCurrentState(JS::gcreason::Reason reason);
 
     gcstats::ZoneGCStats scanZonesBeforeGC();
     void collect(bool nonincrementalByAPI, SliceBudget budget, JS::gcreason::Reason reason) JS_HAZ_GC_CALL;
     MOZ_MUST_USE IncrementalResult gcCycle(bool nonincrementalByAPI, SliceBudget& budget,
                                            JS::gcreason::Reason reason);
     bool shouldRepeatForDeadZone(JS::gcreason::Reason reason);
     void incrementalCollectSlice(SliceBudget& budget, JS::gcreason::Reason reason,
-                                 AutoTraceSession& session);
+                                 AutoGCSession& session);
 
     friend class AutoCallGCCallbacks;
     void maybeCallGCCallback(JSGCStatus status);
 
     void pushZealSelectedObjects();
     void purgeRuntime();
-    MOZ_MUST_USE bool beginMarkPhase(JS::gcreason::Reason reason, AutoTraceSession& session);
+    MOZ_MUST_USE bool beginMarkPhase(JS::gcreason::Reason reason, AutoGCSession& session);
     bool prepareZonesForCollection(JS::gcreason::Reason reason, bool* isFullOut);
     bool shouldPreserveJITCode(JS::Realm* realm, int64_t currentTime,
                                JS::gcreason::Reason reason, bool canAllocateMoreCode);
-    void traceRuntimeForMajorGC(JSTracer* trc, AutoTraceSession& session);
+    void traceRuntimeForMajorGC(JSTracer* trc, AutoGCSession& session);
     void traceRuntimeAtoms(JSTracer* trc, const AutoAccessAtomsZone& atomsAccess);
     void traceKeptAtoms(JSTracer* trc);
     void traceRuntimeCommon(JSTracer* trc, TraceOrMarkRuntime traceOrMark);
     void maybeDoCycleCollection();
     void markCompartments();
     IncrementalProgress drainMarkStack(SliceBudget& sliceBudget, gcstats::PhaseKind phase);
     template <class CompartmentIterT> void markWeakReferences(gcstats::PhaseKind phase);
     void markWeakReferencesInCurrentGroup(gcstats::PhaseKind phase);
     template <class ZoneIterT, class CompartmentIterT> void markGrayReferences(gcstats::PhaseKind phase);
     void markBufferedGrayRoots(JS::Zone* zone);
     void markGrayReferencesInCurrentGroup(gcstats::PhaseKind phase);
     void markAllWeakReferences(gcstats::PhaseKind phase);
     void markAllGrayReferences(gcstats::PhaseKind phase);
 
-    void beginSweepPhase(JS::gcreason::Reason reason, AutoTraceSession& session);
+    void beginSweepPhase(JS::gcreason::Reason reason, AutoGCSession& session);
     void groupZonesForSweeping(JS::gcreason::Reason reason);
     MOZ_MUST_USE bool findInterZoneEdges();
     void getNextSweepGroup();
     IncrementalProgress endMarkingSweepGroup(FreeOp* fop, SliceBudget& budget);
     IncrementalProgress beginSweepingSweepGroup(FreeOp* fop, SliceBudget& budget);
 #ifdef JS_GC_ZEAL
     IncrementalProgress maybeYieldForSweepingZeal(FreeOp* fop, SliceBudget& budget);
 #endif
@@ -612,34 +613,34 @@ class GCRuntime
     void decommitAllWithoutUnlocking(const AutoLockGC& lock);
     void startDecommit();
     void queueZonesForBackgroundSweep(ZoneList& zones);
     void sweepBackgroundThings(ZoneList& zones, LifoAlloc& freeBlocks);
     void assertBackgroundSweepingFinished();
     bool shouldCompact();
     void beginCompactPhase();
     IncrementalProgress compactPhase(JS::gcreason::Reason reason, SliceBudget& sliceBudget,
-                                     AutoTraceSession& session);
+                                     AutoGCSession& session);
     void endCompactPhase();
     void sweepTypesAfterCompacting(Zone* zone);
     void sweepZoneAfterCompacting(Zone* zone);
     MOZ_MUST_USE bool relocateArenas(Zone* zone, JS::gcreason::Reason reason,
                                      Arena*& relocatedListOut, SliceBudget& sliceBudget);
     void updateTypeDescrObjects(MovingTracer* trc, Zone* zone);
     void updateCellPointers(Zone* zone, AllocKinds kinds, size_t bgTaskCount);
     void updateAllCellPointers(MovingTracer* trc, Zone* zone);
     void updateZonePointersToRelocatedCells(Zone* zone);
-    void updateRuntimePointersToRelocatedCells(AutoTraceSession& session);
+    void updateRuntimePointersToRelocatedCells(AutoGCSession& session);
     void protectAndHoldArenas(Arena* arenaList);
     void unprotectHeldRelocatedArenas();
     void releaseRelocatedArenas(Arena* arenaList);
     void releaseRelocatedArenasWithoutUnlocking(Arena* arenaList, const AutoLockGC& lock);
     void finishCollection();
 
-    void computeNonIncrementalMarkingForValidation(AutoTraceSession& session);
+    void computeNonIncrementalMarkingForValidation(AutoGCSession& session);
     void validateIncrementalMarking();
     void finishMarkingValidation();
 
 #ifdef DEBUG
     void checkForCompartmentMismatches();
 #endif
 
     void callFinalizeCallbacks(FreeOp* fop, JSFinalizeStatus status) const;
@@ -986,17 +987,16 @@ class GCRuntime
                  gcstats::PhaseKind phase = gcstats::PhaseKind::MINOR_GC) JS_HAZ_GC_CALL;
     void evictNursery(JS::gcreason::Reason reason = JS::gcreason::EVICT_NURSERY) {
         minorGC(reason, gcstats::PhaseKind::EVICT_NURSERY);
     }
     void freeAllLifoBlocksAfterMinorGC(LifoAlloc* lifo);
 
     friend class js::GCHelperState;
     friend class MarkingValidator;
-    friend class AutoTraceSession;
     friend class AutoEnterIteration;
 };
 
 /* Prevent compartments and zones from being collected during iteration. */
 class MOZ_RAII AutoEnterIteration {
     GCRuntime* gc;
 
   public:
--- a/js/src/gc/Nursery.cpp
+++ b/js/src/gc/Nursery.cpp
@@ -767,17 +767,17 @@ js::Nursery::collect(JS::gcreason::Reaso
                 if (group->canPreTenure(sweep)) {
                     group->setShouldPreTenure(sweep, cx);
                     pretenureCount++;
                 }
             }
         }
     }
 
-    mozilla::Maybe<AutoTraceSession> session;
+    mozilla::Maybe<AutoGCSession> session;
     for (ZonesIter zone(rt, SkipAtoms); !zone.done(); zone.next()) {
         if (shouldPretenure && zone->allocNurseryStrings && zone->tenuredStrings >= 30 * 1000) {
             if (!session.isSome())
                 session.emplace(rt, JS::HeapState::MinorCollecting);
             CancelOffThreadIonCompile(zone);
             bool preserving = zone->isPreservingCode();
             zone->setPreservingCode(false);
             zone->discardJitCode(rt->defaultFreeOp());
@@ -841,17 +841,17 @@ js::Nursery::collect(JS::gcreason::Reaso
         }
     }
 }
 
 void
 js::Nursery::doCollection(JS::gcreason::Reason reason, TenureCountCache& tenureCounts)
 {
     JSRuntime* rt = runtime();
-    AutoTraceSession session(rt, JS::HeapState::MinorCollecting);
+    AutoGCSession session(rt, JS::HeapState::MinorCollecting);
     AutoSetThreadIsPerformingGC performingGC;
     AutoStopVerifyingBarriers av(rt, false);
     AutoDisableProxyCheck disableStrictProxyChecking;
     mozilla::DebugOnly<AutoEnterOOMUnsafeRegion> oomUnsafeRegion;
 
     const size_t initialNurseryCapacity = spaceToEnd(maxChunkCount());
     const size_t initialNurseryUsedBytes = initialNurseryCapacity - freeSpace();
 
--- a/js/src/gc/RootMarking.cpp
+++ b/js/src/gc/RootMarking.cpp
@@ -257,17 +257,17 @@ PropertyDescriptor::trace(JSTracer* trc)
     if ((attrs & JSPROP_SETTER) && setter) {
         JSObject* tmp = JS_FUNC_TO_DATA_PTR(JSObject*, setter);
         TraceRoot(trc, &tmp, "Descriptor::set");
         setter = JS_DATA_TO_FUNC_PTR(JSSetterOp, tmp);
     }
 }
 
 void
-js::gc::GCRuntime::traceRuntimeForMajorGC(JSTracer* trc, AutoTraceSession& session)
+js::gc::GCRuntime::traceRuntimeForMajorGC(JSTracer* trc, AutoGCSession& session)
 {
     MOZ_ASSERT(!TlsContext.get()->suppressGC);
 
     // FinishRoots will have asserted that every root that we do not expect
     // is gone, so we can simply skip traceRuntime here.
     if (rt->isBeingDestroyed())
         return;
 
@@ -275,17 +275,17 @@ js::gc::GCRuntime::traceRuntimeForMajorG
     if (atomsZone->isCollecting())
         traceRuntimeAtoms(trc, session.checkAtomsAccess());
     traceKeptAtoms(trc);
     Compartment::traceIncomingCrossCompartmentEdgesForZoneGC(trc);
     traceRuntimeCommon(trc, MarkRuntime);
 }
 
 void
-js::gc::GCRuntime::traceRuntimeForMinorGC(JSTracer* trc, AutoTraceSession& session)
+js::gc::GCRuntime::traceRuntimeForMinorGC(JSTracer* trc, AutoGCSession& session)
 {
     MOZ_ASSERT(!TlsContext.get()->suppressGC);
 
     // Note that we *must* trace the runtime during the SHUTDOWN_GC's minor GC
     // despite having called FinishRoots already. This is because FinishRoots
     // does not clear the crossCompartmentWrapper map. It cannot do this
     // because Proxy's trace for CrossCompartmentWrappers asserts presence in
     // the map. And we can reach its trace function despite having finished the
@@ -302,27 +302,27 @@ void
 js::TraceRuntime(JSTracer* trc)
 {
     MOZ_ASSERT(!trc->isMarkingTracer());
 
     JSRuntime* rt = trc->runtime();
     rt->gc.evictNursery();
     AutoPrepareForTracing prep(rt->mainContextFromOwnThread());
     gcstats::AutoPhase ap(rt->gc.stats(), gcstats::PhaseKind::TRACE_HEAP);
-    rt->gc.traceRuntime(trc, prep.session);
+    rt->gc.traceRuntime(trc, prep);
 }
 
 void
 js::gc::GCRuntime::traceRuntime(JSTracer* trc, AutoTraceSession& session)
 {
     MOZ_ASSERT(!rt->isBeingDestroyed());
 
     gcstats::AutoPhase ap(stats(), gcstats::PhaseKind::MARK_ROOTS);
 
-    traceRuntimeAtoms(trc, session.lock());
+    traceRuntimeAtoms(trc, session);
     traceRuntimeCommon(trc, TraceRuntime);
 }
 
 void
 js::gc::GCRuntime::traceRuntimeAtoms(JSTracer* trc, const AutoAccessAtomsZone& access)
 {
     gcstats::AutoPhase ap(stats(), gcstats::PhaseKind::MARK_RUNTIME_DATA);
     TracePermanentAtoms(trc);
@@ -444,19 +444,19 @@ js::gc::GCRuntime::finishRoots()
 
 #ifdef DEBUG
     // The nsWrapperCache may not be empty before our shutdown GC, so we have
     // to skip that table when verifying that we are fully unrooted.
     auto prior = grayRootTracer;
     grayRootTracer = Callback<JSTraceDataOp>(nullptr, nullptr);
 
     AssertNoRootsTracer trc(rt, TraceWeakMapKeysValues);
-    AutoPrepareForTracing prep(TlsContext.get());
+    AutoTraceSession session(rt);
     gcstats::AutoPhase ap(rt->gc.stats(), gcstats::PhaseKind::TRACE_HEAP);
-    traceRuntime(&trc, prep.session);
+    traceRuntime(&trc, session);
 
     // Restore the wrapper tracing so that we leak instead of leaving dangling
     // pointers.
     grayRootTracer = prior;
 #endif // DEBUG
 }
 
 // Append traced things to a buffer on the zone for use later in the GC.
--- a/js/src/gc/Verifier.cpp
+++ b/js/src/gc/Verifier.cpp
@@ -216,17 +216,17 @@ gc::GCRuntime::startVerifyPreBarriers()
         goto oom;
 
     /* Create the root node. */
     trc->curnode = MakeNode(trc, nullptr, JS::TraceKind(0));
 
     incrementalState = State::MarkRoots;
 
     /* Make all the roots be edges emanating from the root node. */
-    traceRuntime(trc, prep.session);
+    traceRuntime(trc, prep);
 
     VerifyNode* node;
     node = trc->curnode;
     if (trc->edgeptr == trc->term)
         goto oom;
 
     /* For each edge, make a node for it if one doesn't already exist. */
     while ((char*)node < trc->edgeptr) {
@@ -650,17 +650,17 @@ CheckHeapTracer::check(AutoTraceSession&
     if (failures)
         fprintf(stderr, "Heap check: %zu failure(s)\n", failures);
     MOZ_RELEASE_ASSERT(failures == 0);
 }
 
 void
 js::gc::CheckHeapAfterGC(JSRuntime* rt)
 {
-    AutoTraceSession session(rt, JS::HeapState::Tracing);
+    AutoTraceSession session(rt);
     CheckHeapTracer tracer(rt);
     if (tracer.init())
         tracer.check(session);
 }
 
 #endif /* JSGC_HASH_TABLE_CHECKS */
 
 #if defined(JS_GC_ZEAL) || defined(DEBUG)
@@ -719,17 +719,17 @@ JS_FRIEND_API(bool)
 js::CheckGrayMarkingState(JSRuntime* rt)
 {
     MOZ_ASSERT(!JS::RuntimeHeapIsCollecting());
     MOZ_ASSERT(!rt->gc.isIncrementalGCInProgress());
     if (!rt->gc.areGrayBitsValid())
         return true;
 
     gcstats::AutoPhase ap(rt->gc.stats(), gcstats::PhaseKind::TRACE_HEAP);
-    AutoTraceSession session(rt, JS::HeapState::Tracing);
+    AutoTraceSession session(rt);
     CheckGrayMarkingTracer tracer(rt);
     if (!tracer.init())
         return true; // Ignore failure
 
     return tracer.check(session);
 }
 
 #endif // defined(JS_GC_ZEAL) || defined(DEBUG)
--- a/js/src/jsfriendapi.cpp
+++ b/js/src/jsfriendapi.cpp
@@ -1247,19 +1247,19 @@ js::DumpHeap(JSContext* cx, FILE* fp, js
     if (nurseryBehaviour == js::CollectNurseryBeforeDump)
         cx->runtime()->gc.evictNursery(JS::gcreason::API);
 
     DumpHeapTracer dtrc(fp, cx);
 
     fprintf(dtrc.output, "# Roots.\n");
     {
         JSRuntime* rt = cx->runtime();
-        js::gc::AutoPrepareForTracing prep(cx);
+        js::gc::AutoTraceSession session(rt);
         gcstats::AutoPhase ap(rt->gc.stats(), gcstats::PhaseKind::TRACE_HEAP);
-        rt->gc.traceRuntime(&dtrc, prep.session);
+        rt->gc.traceRuntime(&dtrc, session);
     }
 
     fprintf(dtrc.output, "# Weak maps.\n");
     WeakMapBase::traceAllMappings(&dtrc);
 
     fprintf(dtrc.output, "==========\n");
 
     dtrc.prefix = "> ";
--- a/js/src/vm/BytecodeUtil.cpp
+++ b/js/src/vm/BytecodeUtil.cpp
@@ -2891,16 +2891,17 @@ static bool
 GenerateLcovInfo(JSContext* cx, JS::Realm* realm, GenericPrinter& out)
 {
     JSRuntime* rt = cx->runtime();
 
     // Collect the list of scripts which are part of the current realm.
     {
         js::gc::AutoPrepareForTracing apft(cx);
     }
+
     Rooted<ScriptVector> topScripts(cx, ScriptVector(cx));
     for (ZonesIter zone(rt, SkipAtoms); !zone.done(); zone.next()) {
         for (auto script = zone->cellIter<JSScript>(); !script.done(); script.next()) {
             if (script->realm() != realm ||
                 !script->isTopLevel() ||
                 !script->filename())
             {
                 continue;
--- a/js/src/vm/Runtime.h
+++ b/js/src/vm/Runtime.h
@@ -56,16 +56,20 @@ namespace js {
 
 class AutoAssertNoContentJS;
 class AutoKeepAtoms;
 class EnterDebuggeeNoExecute;
 #ifdef JS_TRACE_LOGGING
 class TraceLoggerThread;
 #endif
 
+namespace gc {
+class AutoHeapSession;
+}
+
 } // namespace js
 
 struct DtoaState;
 
 #ifdef JS_SIMULATOR_ARM64
 namespace vixl {
 class Simulator;
 }
@@ -953,17 +957,17 @@ struct JSRuntime : public js::MallocProv
     }
     void setStackFormat(js::StackFormat format) {
         MOZ_ASSERT(!parentRuntime);
         MOZ_ASSERT(format != js::StackFormat::Default);
         stackFormat_ = format;
     }
 
     // For inherited heap state accessors.
-    friend class js::gc::AutoTraceSession;
+    friend class js::gc::AutoHeapSession;
     friend class JS::AutoEnterCycleCollection;
 
   private:
     js::MainThreadData<js::RuntimeCaches> caches_;
   public:
     js::RuntimeCaches& caches() { return caches_.ref(); }
 
     // List of all the live wasm::Instances in the runtime. Equal to the union