Bug 1637667 - Switch into gray marking stats phase when marking gray things r=sfink
authorJon Coppeard <jcoppeard@mozilla.com>
Fri, 15 May 2020 07:16:57 +0000
changeset 530230 5e5a36b8ce9e6fa8bc8c1fe6d0b36a8fc98d18b7
parent 530229 8ef5cc9805506a2c8c568e544770f568d5e2440d
child 530231 6b265b68d408ccc95b8877360e84adff667aa27b
push id37420
push usernerli@mozilla.com
push dateFri, 15 May 2020 21:52:36 +0000
treeherdermozilla-central@f340bbb582d1 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssfink
bugs1637667
milestone78.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 1637667 - Switch into gray marking stats phase when marking gray things r=sfink This makes GCMaker::markUntilBudgetExhaused switch into the appropriate stats phase when marking gray things. We have to pass a parameter to make this conditional because we don't do this type of report off main thread and this happens when we mark on a background thread during sweeping. I rearranged the code so that we don't ever encounter gray things in that case by doing the marking on the main thread in cases where we would have joined the background task immediately anyway. We still need to skip reporting for marking done by processMarkQueue() but that's a debugging feature so this doesn't matter. I had to move the SWEEP_MARK_GRAY_WEAK phase under SWEEP_MARK_WEAK but that does make some sense. Differential Revision: https://phabricator.services.mozilla.com/D75270
js/src/gc/GC.cpp
js/src/gc/GCMarker.h
js/src/gc/GCRuntime.h
js/src/gc/GenerateStatsPhases.py
js/src/gc/Marking.cpp
js/src/gc/Statistics.cpp
js/src/gc/Verifier.cpp
--- a/js/src/gc/GC.cpp
+++ b/js/src/gc/GC.cpp
@@ -898,16 +898,17 @@ GCRuntime::GCRuntime(JSRuntime* rt)
       isFull(false),
       incrementalState(gc::State::NotActive),
       initialState(gc::State::NotActive),
 #ifdef JS_GC_ZEAL
       useZeal(false),
 #endif
       lastMarkSlice(false),
       safeToYield(true),
+      markOnBackgroundThreadDuringSweeping(false),
       sweepOnBackgroundThread(false),
       requestSliceAfterBackgroundTask(false),
       lifoBlocksToFree((size_t)JSContext::TEMP_LIFO_ALLOC_PRIMARY_CHUNK_SIZE),
       lifoBlocksToFreeAfterMinorGC(
           (size_t)JSContext::TEMP_LIFO_ALLOC_PRIMARY_CHUNK_SIZE),
       sweepGroupIndex(0),
       sweepGroups(nullptr),
       currentSweepGroup(nullptr),
@@ -4286,18 +4287,18 @@ void GCRuntime::updateMemoryCountersOnGC
   // Update memory counters for the zones we are collecting.
   for (GCZonesIter zone(this, WithAtoms); !zone.done(); zone.next()) {
     zone->updateMemoryCountersOnGCStart();
   }
 }
 
 template <class ZoneIterT>
 IncrementalProgress GCRuntime::markWeakReferences(
-    gcstats::PhaseKind phase, SliceBudget& incrementalBudget) {
-  gcstats::AutoPhase ap1(stats(), phase);
+    SliceBudget& incrementalBudget) {
+  gcstats::AutoPhase ap1(stats(), gcstats::PhaseKind::SWEEP_MARK_WEAK);
 
   auto unlimited = SliceBudget::unlimited();
   SliceBudget& budget =
       marker.incrementalWeakMapMarkingEnabled ? incrementalBudget : unlimited;
 
   // We may have already entered weak marking mode.
   if (!marker.isWeakMarking() && marker.enterWeakMarkingMode()) {
     for (ZoneIterT zone(this); !zone.done(); zone.next()) {
@@ -4340,18 +4341,18 @@ IncrementalProgress GCRuntime::markWeakR
     markedAny |= jit::JitRuntime::MarkJitcodeGlobalTableIteratively(&marker);
   }
   MOZ_ASSERT(marker.isDrained());
 
   return Finished;
 }
 
 IncrementalProgress GCRuntime::markWeakReferencesInCurrentGroup(
-    gcstats::PhaseKind phase, SliceBudget& budget) {
-  return markWeakReferences<SweepGroupZonesIter>(phase, budget);
+    SliceBudget& budget) {
+  return markWeakReferences<SweepGroupZonesIter>(budget);
 }
 
 template <class ZoneIterT>
 void GCRuntime::markGrayRoots(gcstats::PhaseKind phase) {
   MOZ_ASSERT(marker.markColor() == MarkColor::Gray);
 
   gcstats::AutoPhase ap(stats(), phase);
   if (hasValidGrayRootsBuffer()) {
@@ -4361,19 +4362,19 @@ void GCRuntime::markGrayRoots(gcstats::P
   } else {
     MOZ_ASSERT(!isIncremental);
     traceEmbeddingGrayRoots(&marker);
     Compartment::traceIncomingCrossCompartmentEdgesForZoneGC(
         &marker, Compartment::GrayEdges);
   }
 }
 
-IncrementalProgress GCRuntime::markAllWeakReferences(gcstats::PhaseKind phase,
-                                                     SliceBudget& budget) {
-  return markWeakReferences<GCZonesIter>(phase, budget);
+IncrementalProgress GCRuntime::markAllWeakReferences() {
+  SliceBudget budget = SliceBudget::unlimited();
+  return markWeakReferences<GCZonesIter>(budget);
 }
 
 void GCRuntime::markAllGrayReferences(gcstats::PhaseKind phase) {
   markGrayRoots<GCZonesIter>(phase);
   drainMarkStack();
 }
 
 void GCRuntime::dropStringWrappers() {
@@ -4852,20 +4853,18 @@ static inline void MaybeCheckWeakMapMark
     }
   }
 
 #endif
 }
 
 IncrementalProgress GCRuntime::markGrayReferencesInCurrentGroup(
     JSFreeOp* fop, SliceBudget& budget) {
-  // Wait for sweepMarkTask finishes sweep-marking.
-  if (joinSweepMarkTask() == NotFinished) {
-    return NotFinished;
-  }
+  MOZ_ASSERT(!markOnBackgroundThreadDuringSweeping);
+  MOZ_ASSERT(marker.isDrained());
 
   MOZ_ASSERT(marker.markColor() == MarkColor::Black);
 
   if (hasMarkedGrayRoots) {
     return Finished;
   }
 
   MOZ_ASSERT(cellsToAssertNotGray.ref().empty());
@@ -4897,47 +4896,42 @@ IncrementalProgress GCRuntime::markGrayR
   hasMarkedGrayRoots = true;
 
 #ifdef JS_GC_ZEAL
   if (shouldYieldForZeal(ZealMode::YieldWhileGrayMarking)) {
     return NotFinished;
   }
 #endif
 
-  if (markUntilBudgetExhausted(budget, gcstats::PhaseKind::SWEEP_MARK_GRAY) ==
-      NotFinished) {
+  if (markUntilBudgetExhausted(budget) == NotFinished) {
     return NotFinished;
   }
   marker.setMainStackColor(MarkColor::Black);
   return Finished;
 }
 
 IncrementalProgress GCRuntime::endMarkingSweepGroup(JSFreeOp* fop,
                                                     SliceBudget& budget) {
-  // Wait for sweepMarkTask finishes sweep-marking.
-  if (joinSweepMarkTask() == NotFinished) {
-    return NotFinished;
-  }
+  MOZ_ASSERT(!markOnBackgroundThreadDuringSweeping);
+  MOZ_ASSERT(marker.isDrained());
 
   MOZ_ASSERT(marker.markColor() == MarkColor::Black);
   MOZ_ASSERT(!HasIncomingCrossCompartmentPointers(rt));
 
   gcstats::AutoPhase ap(stats(), gcstats::PhaseKind::SWEEP_MARK);
 
-  if (markWeakReferencesInCurrentGroup(gcstats::PhaseKind::SWEEP_MARK_WEAK,
-                                       budget) == NotFinished) {
+  if (markWeakReferencesInCurrentGroup(budget) == NotFinished) {
     return NotFinished;
   }
 
   AutoSetMarkColor setColorGray(marker, MarkColor::Gray);
   marker.setMainStackColor(MarkColor::Gray);
 
   // Mark transitively inside the current compartment group.
-  if (markWeakReferencesInCurrentGroup(gcstats::PhaseKind::SWEEP_MARK_GRAY_WEAK,
-                                       budget) == NotFinished) {
+  if (markWeakReferencesInCurrentGroup(budget) == NotFinished) {
     return NotFinished;
   }
 
   MOZ_ASSERT(marker.isDrained());
 
   // We must not yield after this point before we start sweeping the group.
   safeToYield = false;
 
@@ -5377,16 +5371,17 @@ IncrementalProgress GCRuntime::beginSwee
     }
 
     zone->arenas.queueForegroundThingsForSweep();
   }
 
   MOZ_ASSERT(!sweepZone);
 
   safeToYield = true;
+  markOnBackgroundThreadDuringSweeping = CanUseExtraThreads();
 
   return Finished;
 }
 
 #ifdef JS_GC_ZEAL
 bool GCRuntime::shouldYieldForZeal(ZealMode mode) {
   bool yield = useZeal && isIncremental && hasZealMode(mode);
 
@@ -5403,16 +5398,20 @@ bool GCRuntime::shouldYieldForZeal(ZealM
 IncrementalProgress GCRuntime::endSweepingSweepGroup(JSFreeOp* fop,
                                                      SliceBudget& budget) {
   // This is to prevent TSan data race, sweepMarkTask will check if the GC state
   // is Marking, but later below we will change GC state to Finished.
   if (joinSweepMarkTask() == NotFinished) {
     return NotFinished;
   }
 
+  // Disable background marking during sweeping until we start sweeping the next
+  // zone group.
+  markOnBackgroundThreadDuringSweeping = false;
+
   {
     gcstats::AutoPhase ap(stats(), gcstats::PhaseKind::FINALIZE_END);
     JSFreeOp fop(rt);
     callFinalizeCallbacks(&fop, JSFINALIZE_GROUP_END);
   }
 
   /* Free LIFO blocks on a background thread if possible. */
   startBackgroundFree();
@@ -5456,16 +5455,17 @@ void GCRuntime::beginSweepPhase(JS::GCRe
    * 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.
    */
 
   MOZ_ASSERT(!abortSweepAfterCurrentGroup);
+  MOZ_ASSERT(!markOnBackgroundThreadDuringSweeping);
 
   releaseHeldRelocatedArenas();
 
 #ifdef JS_GC_ZEAL
   computeNonIncrementalMarkingForValidation(session);
 #endif
 
   gcstats::AutoPhase ap(stats(), gcstats::PhaseKind::SWEEP);
@@ -5512,44 +5512,41 @@ bool ArenaLists::foregroundFinalize(JSFr
   ArenaList finalized = sweepList.toArenaList();
   arenaLists(thingKind) =
       finalized.insertListWithCursorAtEnd(arenaLists(thingKind));
 
   return true;
 }
 
 void js::gc::SweepMarkTask::run() {
-  gc->sweepMarkResult = gc->markUntilBudgetExhausted(this->budget);
+  // Time reporting is handled separately for parallel tasks.
+  gc->sweepMarkResult =
+      gc->markUntilBudgetExhausted(this->budget, GCMarker::DontReportMarkTime);
 }
 
 IncrementalProgress GCRuntime::joinSweepMarkTask() {
   joinTask(sweepMarkTask, gcstats::PhaseKind::SWEEP_MARK);
 
   return sweepMarkTaskStarted ? sweepMarkResult : Finished;
 }
 
 IncrementalProgress GCRuntime::markUntilBudgetExhausted(
-    SliceBudget& sliceBudget, gcstats::PhaseKind phase) {
-  gcstats::AutoPhase ap(stats(), phase);
-  return markUntilBudgetExhausted(sliceBudget);
-}
-
-IncrementalProgress GCRuntime::markUntilBudgetExhausted(
-    SliceBudget& sliceBudget) {
+    SliceBudget& sliceBudget, GCMarker::ShouldReportMarkTime reportTime) {
   // Run a marking slice and return whether the stack is now empty.
 
 #ifdef DEBUG
   AutoSetThreadIsMarking threadIsMarking;
 #endif  // DEBUG
 
   if (marker.processMarkQueue() == GCMarker::QueueYielded) {
     return NotFinished;
   }
 
-  return marker.markUntilBudgetExhausted(sliceBudget) ? Finished : NotFinished;
+  return marker.markUntilBudgetExhausted(sliceBudget, reportTime) ? Finished
+                                                                  : NotFinished;
 }
 
 void GCRuntime::drainMarkStack() {
   auto unlimited = SliceBudget::unlimited();
   MOZ_RELEASE_ASSERT(marker.markUntilBudgetExhausted(unlimited));
 }
 
 static void SweepThing(JSFreeOp* fop, Shape* shape) {
@@ -6106,35 +6103,49 @@ bool GCRuntime::initSweepActions() {
 
   return sweepActions != nullptr;
 }
 
 IncrementalProgress GCRuntime::performSweepActions(SliceBudget& budget) {
   gcstats::AutoPhase ap(stats(), gcstats::PhaseKind::SWEEP);
   JSFreeOp fop(rt);
 
-  // Kick off a parallel task to drain the mark stack, except in the first sweep
-  // slice where we must not yield to the mutator until we've starting sweeping
-  // a sweep group. The stack must be empty in that case.
+  // Drain the mark stack, possibly in a parallel task if we're in a part of
+  // sweeping that allows it.
+  //
+  // In the first sweep slice where we must not yield to the mutator until we've
+  // starting sweeping a sweep group but in that case the stack must be empty
+  // already.
+
   MOZ_ASSERT(initialState <= State::Sweep);
   MOZ_ASSERT_IF(initialState != State::Sweep, marker.isDrained());
   MOZ_ASSERT(!sweepMarkTaskStarted);
+
   if (initialState == State::Sweep && !marker.isDrained()) {
-    AutoLockHelperThreadState lock;
-    MOZ_ASSERT(sweepMarkTask.isIdle(lock));
-    sweepMarkTask.setBudget(budget);
-    sweepMarkTask.startOrRunIfIdle(lock);
-    sweepMarkTaskStarted = true;
-  }
+    if (markOnBackgroundThreadDuringSweeping) {
+      AutoLockHelperThreadState lock;
+      MOZ_ASSERT(sweepMarkTask.isIdle(lock));
+      sweepMarkTask.setBudget(budget);
+      sweepMarkTask.startOrRunIfIdle(lock);
+      sweepMarkTaskStarted = true;
+    } else {
+      gcstats::AutoPhase ap(stats(), gcstats::PhaseKind::SWEEP_MARK);
+      if (markUntilBudgetExhausted(budget) == NotFinished) {
+        return NotFinished;
+      }
+    }
+  }
+
+  // Then continue running sweep actions.
 
   SweepAction::Args args{this, &fop, budget};
   IncrementalProgress progress = sweepActions->run(args);
 
   if (sweepMarkTaskStarted) {
-    joinTask(sweepMarkTask, gcstats::PhaseKind::SWEEP_MARK);
+    joinSweepMarkTask();
     sweepMarkTaskStarted = false;
     if (sweepMarkResult == NotFinished) {
       progress = NotFinished;
     }
   }
 
   MOZ_ASSERT_IF(progress == NotFinished, isIncremental);
   return progress;
@@ -6159,16 +6170,18 @@ bool GCRuntime::allCCVisibleZonesWereCol
       return false;
     }
   }
 
   return true;
 }
 
 void GCRuntime::endSweepPhase(bool destroyingRuntime) {
+  MOZ_ASSERT(!markOnBackgroundThreadDuringSweeping);
+
   sweepActions->assertFinished();
 
   gcstats::AutoPhase ap(stats(), gcstats::PhaseKind::SWEEP);
   JSFreeOp fop(rt);
 
   MOZ_ASSERT_IF(destroyingRuntime, !sweepOnBackgroundThread);
 
   {
@@ -6605,19 +6618,21 @@ void GCRuntime::incrementalSlice(SliceBu
         break;
       }
 
       [[fallthrough]];
 
     case State::Mark:
       rt->mainContextFromOwnThread()->traceWrapperGCRooters(&marker);
 
-      if (markUntilBudgetExhausted(budget, gcstats::PhaseKind::MARK) ==
-          NotFinished) {
-        break;
+      {
+        gcstats::AutoPhase ap(stats(), gcstats::PhaseKind::MARK);
+        if (markUntilBudgetExhausted(budget) == NotFinished) {
+          break;
+        }
       }
 
       MOZ_ASSERT(marker.isDrained());
 
       /*
        * There are a number of reasons why we break out of collection here,
        * either ending the slice or to run a new interation of the loop in
        * GCRuntime::collect()
--- a/js/src/gc/GCMarker.h
+++ b/js/src/gc/GCMarker.h
@@ -343,17 +343,22 @@ class GCMarker : public JSTracer {
   // yield timing.
   enum MarkQueueProgress {
     QueueYielded,   // End this incremental GC slice, if possible
     QueueComplete,  // Done with the queue
     QueueSuspended  // Continue the GC without ending the slice
   };
   MarkQueueProgress processMarkQueue();
 
-  MOZ_MUST_USE bool markUntilBudgetExhausted(SliceBudget& budget);
+  enum ShouldReportMarkTime : bool {
+    ReportMarkTime = true,
+    DontReportMarkTime = false
+  };
+  MOZ_MUST_USE bool markUntilBudgetExhausted(
+      SliceBudget& budget, ShouldReportMarkTime reportTime = ReportMarkTime);
 
   void setGCMode(JSGCMode mode) {
     // Ignore failure to resize the stack and keep using the existing stack.
     mozilla::Unused << stack.setCapacityForMode(mode, gc::MarkStack::MainStack);
   }
 
   size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const;
 
--- a/js/src/gc/GCRuntime.h
+++ b/js/src/gc/GCRuntime.h
@@ -730,30 +730,27 @@ class GCRuntime {
   void traceEmbeddingBlackRoots(JSTracer* trc);
   void traceEmbeddingGrayRoots(JSTracer* trc);
   void markFinalizationRegistryRoots(JSTracer* trc);
   void checkNoRuntimeRoots(AutoGCSession& session);
   void maybeDoCycleCollection();
   void findDeadCompartments();
 
   friend class SweepMarkTask;
-  IncrementalProgress markUntilBudgetExhausted(SliceBudget& sliceBudget,
-                                               gcstats::PhaseKind phase);
-  IncrementalProgress markUntilBudgetExhausted(SliceBudget& sliceBudget);
+  IncrementalProgress markUntilBudgetExhausted(
+      SliceBudget& sliceBudget,
+      GCMarker::ShouldReportMarkTime reportTime = GCMarker::ReportMarkTime);
   void drainMarkStack();
   template <class ZoneIterT>
-  IncrementalProgress markWeakReferences(gcstats::PhaseKind phase,
-                                         SliceBudget& budget);
-  IncrementalProgress markWeakReferencesInCurrentGroup(gcstats::PhaseKind phase,
-                                                       SliceBudget& budget);
+  IncrementalProgress markWeakReferences(SliceBudget& budget);
+  IncrementalProgress markWeakReferencesInCurrentGroup(SliceBudget& budget);
   template <class ZoneIterT>
   void markGrayRoots(gcstats::PhaseKind phase);
   void markBufferedGrayRoots(JS::Zone* zone);
-  IncrementalProgress markAllWeakReferences(gcstats::PhaseKind phase,
-                                            SliceBudget& budget);
+  IncrementalProgress markAllWeakReferences();
   void markAllGrayReferences(gcstats::PhaseKind phase);
 
   void beginSweepPhase(JS::GCReason reason, AutoGCSession& session);
   void dropStringWrappers();
   void groupZonesForSweeping(JS::GCReason reason);
   MOZ_MUST_USE bool findSweepGroupEdges();
   void getNextSweepGroup();
   IncrementalProgress markGrayReferencesInCurrentGroup(JSFreeOp* fop,
@@ -1012,16 +1009,21 @@ class GCRuntime {
 #endif
 
   /* Indicates that the last incremental slice exhausted the mark stack. */
   MainThreadData<bool> lastMarkSlice;
 
   // Whether it's currently safe to yield to the mutator in an incremental GC.
   MainThreadData<bool> safeToYield;
 
+  // Whether to do any marking caused by barriers on a background thread during
+  // incremental sweeping, while also sweeping zones which have finished
+  // marking.
+  MainThreadData<bool> markOnBackgroundThreadDuringSweeping;
+
   /* Whether any sweeping will take place in the separate GC helper thread. */
   MainThreadData<bool> sweepOnBackgroundThread;
 
   /* Singly linked list of zones to be swept in the background. */
   HelperThreadLockData<ZoneList> backgroundSweepZones;
 
   /*
    * Whether to trigger a GC slice after a background task is complete, so that
--- a/js/src/gc/GenerateStatsPhases.py
+++ b/js/src/gc/GenerateStatsPhases.py
@@ -101,20 +101,21 @@ PhaseKindGraphRoots = [
     ]),
     PhaseKind("MARK", "Mark", 6, [
         MarkRootsPhaseKind,
         PhaseKind("MARK_DELAYED", "Mark Delayed", 8)
     ]),
     PhaseKind("SWEEP", "Sweep", 9, [
         PhaseKind("SWEEP_MARK", "Mark During Sweeping", 10, [
             PhaseKind("SWEEP_MARK_INCOMING_BLACK", "Mark Incoming Black Pointers", 12),
-            PhaseKind("SWEEP_MARK_WEAK", "Mark Weak", 13),
+            PhaseKind("SWEEP_MARK_WEAK", "Mark Weak", 13, [
+                PhaseKind("SWEEP_MARK_GRAY_WEAK", "Mark Gray and Weak", 16)
+            ]),
             PhaseKind("SWEEP_MARK_INCOMING_GRAY", "Mark Incoming Gray Pointers", 14),
             PhaseKind("SWEEP_MARK_GRAY", "Mark Gray", 15),
-            PhaseKind("SWEEP_MARK_GRAY_WEAK", "Mark Gray and Weak", 16),
         ]),
         PhaseKind("FINALIZE_START", "Finalize Start Callbacks", 17, [
             PhaseKind("WEAK_ZONES_CALLBACK", "Per-Slice Weak Callback", 57),
             PhaseKind("WEAK_COMPARTMENT_CALLBACK", "Per-Compartment Weak Callback", 58)
         ]),
         PhaseKind("UPDATE_ATOMS_BITMAP", "Sweep Atoms Bitmap", 68),
         PhaseKind("SWEEP_ATOMS_TABLE", "Sweep Atoms Table", 18),
         PhaseKind("SWEEP_COMPARTMENTS", "Sweep Compartments", 20, [
--- a/js/src/gc/Marking.cpp
+++ b/js/src/gc/Marking.cpp
@@ -1747,17 +1747,18 @@ GCMarker::MarkQueueProgress GCMarker::pr
           queuePos--;
           return QueueSuspended;
         }
         if (js::StringEqualsLiteral(str, "abort-weak-marking-mode")) {
           abortLinearWeakMarking();
         }
       } else if (js::StringEqualsLiteral(str, "drain")) {
         auto unlimited = SliceBudget::unlimited();
-        MOZ_RELEASE_ASSERT(markUntilBudgetExhausted(unlimited));
+        MOZ_RELEASE_ASSERT(
+            markUntilBudgetExhausted(unlimited, DontReportMarkTime));
       } else if (js::StringEqualsLiteral(str, "set-color-gray")) {
         queueMarkColor = mozilla::Some(MarkColor::Gray);
         if (gcrt.state() != State::Sweep) {
           // Cannot mark gray yet, so continue with the GC.
           queuePos--;
           return QueueSuspended;
         }
         setMarkColor(MarkColor::Gray);
@@ -1769,17 +1770,31 @@ GCMarker::MarkQueueProgress GCMarker::pr
       }
     }
   }
 #endif
 
   return QueueComplete;
 }
 
-bool GCMarker::markUntilBudgetExhausted(SliceBudget& budget) {
+static gcstats::PhaseKind GrayMarkingPhaseForCurrentPhase(
+    const gcstats::Statistics& stats) {
+  using namespace gcstats;
+  switch (stats.currentPhaseKind()) {
+    case PhaseKind::SWEEP_MARK:
+      return PhaseKind::SWEEP_MARK_GRAY;
+    case PhaseKind::SWEEP_MARK_WEAK:
+      return PhaseKind::SWEEP_MARK_GRAY_WEAK;
+    default:
+      MOZ_CRASH("Unexpected current phase");
+  }
+}
+
+bool GCMarker::markUntilBudgetExhausted(SliceBudget& budget,
+                                        ShouldReportMarkTime reportTime) {
 #ifdef DEBUG
   MOZ_ASSERT(!strictCompartmentChecking);
   strictCompartmentChecking = true;
   auto acc = mozilla::MakeScopeExit([&] { strictCompartmentChecking = false; });
 #endif
 
   if (budget.isOverBudget()) {
     return false;
@@ -1797,16 +1812,22 @@ bool GCMarker::markUntilBudgetExhausted(
       MOZ_ASSERT(markColor() == MarkColor::Black);
       processMarkStackTop(budget);
       if (budget.isOverBudget()) {
         return false;
       }
     }
 
     if (hasGrayEntries()) {
+      mozilla::Maybe<gcstats::AutoPhase> ap;
+      if (reportTime) {
+        auto& stats = runtime()->gc.stats();
+        ap.emplace(stats, GrayMarkingPhaseForCurrentPhase(stats));
+      }
+
       AutoSetMarkColor autoSetGray(*this, MarkColor::Gray);
       do {
         processMarkStackTop(budget);
         if (budget.isOverBudget()) {
           return false;
         }
       } while (hasGrayEntries());
     }
--- a/js/src/gc/Statistics.cpp
+++ b/js/src/gc/Statistics.cpp
@@ -1062,29 +1062,30 @@ void Statistics::sendGCTelemetry() {
   JSRuntime* runtime = gc->rt;
   runtime->addTelemetry(JS_TELEMETRY_GC_IS_ZONE_GC,
                         !zoneStats.isFullCollection());
   TimeDuration prepareTotal = SumPhase(PhaseKind::PREPARE, phaseTimes);
   TimeDuration markTotal = SumPhase(PhaseKind::MARK, phaseTimes);
   TimeDuration markRootsTotal = SumPhase(PhaseKind::MARK_ROOTS, phaseTimes);
   TimeDuration markWeakTotal = phaseTimes[Phase::SWEEP_MARK_WEAK] +
                                phaseTimes[Phase::SWEEP_MARK_GRAY_WEAK];
+  TimeDuration markGrayTotal = phaseTimes[Phase::SWEEP_MARK_GRAY] +
+                               phaseTimes[Phase::SWEEP_MARK_GRAY_WEAK];
   size_t markCount = gc->marker.getMarkCount();
   double markRate = markCount / t(markTotal);
   runtime->addTelemetry(JS_TELEMETRY_GC_PREPARE_MS, t(prepareTotal));
   runtime->addTelemetry(JS_TELEMETRY_GC_MARK_MS, t(markTotal));
   runtime->addTelemetry(JS_TELEMETRY_GC_MARK_RATE, markRate);
   runtime->addTelemetry(JS_TELEMETRY_GC_SWEEP_MS, t(phaseTimes[Phase::SWEEP]));
   if (gc->didCompactZones()) {
     runtime->addTelemetry(JS_TELEMETRY_GC_COMPACT_MS,
                           t(phaseTimes[Phase::COMPACT]));
   }
   runtime->addTelemetry(JS_TELEMETRY_GC_MARK_ROOTS_MS, t(markRootsTotal));
-  runtime->addTelemetry(JS_TELEMETRY_GC_MARK_GRAY_MS,
-                        t(phaseTimes[Phase::SWEEP_MARK_GRAY]));
+  runtime->addTelemetry(JS_TELEMETRY_GC_MARK_GRAY_MS, t(markGrayTotal));
   runtime->addTelemetry(JS_TELEMETRY_GC_MARK_WEAK_MS, t(markWeakTotal));
   runtime->addTelemetry(JS_TELEMETRY_GC_NON_INCREMENTAL, nonincremental());
   if (nonincremental()) {
     runtime->addTelemetry(JS_TELEMETRY_GC_NON_INCREMENTAL_REASON,
                           uint32_t(nonincrementalReason_));
   }
 
 #ifdef DEBUG
--- a/js/src/gc/Verifier.cpp
+++ b/js/src/gc/Verifier.cpp
@@ -601,30 +601,28 @@ void js::gc::MarkingValidator::nonIncrem
     gc->drainMarkStack();
   }
 
   gc->incrementalState = State::Sweep;
   {
     gcstats::AutoPhase ap1(gc->stats(), gcstats::PhaseKind::SWEEP);
     gcstats::AutoPhase ap2(gc->stats(), gcstats::PhaseKind::SWEEP_MARK);
 
-    auto unlimited = SliceBudget::unlimited();
-    gc->markAllWeakReferences(gcstats::PhaseKind::SWEEP_MARK_WEAK, unlimited);
+    gc->markAllWeakReferences();
 
     /* Update zone state for gray marking. */
     for (GCZonesIter zone(gc); !zone.done(); zone.next()) {
       zone->changeGCState(Zone::MarkBlackOnly, Zone::MarkBlackAndGray);
     }
 
     AutoSetMarkColor setColorGray(*gcmarker, MarkColor::Gray);
     gcmarker->setMainStackColor(MarkColor::Gray);
 
     gc->markAllGrayReferences(gcstats::PhaseKind::SWEEP_MARK_GRAY);
-    gc->markAllWeakReferences(gcstats::PhaseKind::SWEEP_MARK_GRAY_WEAK,
-                              unlimited);
+    gc->markAllWeakReferences();
     gc->marker.setMainStackColor(MarkColor::Black);
 
     /* Restore zone state. */
     for (GCZonesIter zone(gc); !zone.done(); zone.next()) {
       zone->changeGCState(Zone::MarkBlackAndGray, Zone::MarkBlackOnly);
     }
     MOZ_ASSERT(gc->marker.isDrained());
   }