Bug 1574986 - Report count of GC slices r=jonco
authorSteve Fink <sfink@mozilla.com>
Wed, 21 Aug 2019 10:16:26 +0000
changeset 489210 883e6437a6eade66c61f24737f2205a40961da4f
parent 489209 70cbd9da42a648bcabfe970f219880dee25144fc
child 489211 d326f257b043eeaaa6da29ef2015c996c5ad58ca
push id36466
push useraciure@mozilla.com
push dateWed, 21 Aug 2019 21:53:43 +0000
treeherdermozilla-central@987927f2da72 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjonco
bugs1574986
milestone70.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 1574986 - Report count of GC slices r=jonco Depends on D42534 Differential Revision: https://phabricator.services.mozilla.com/D42535
js/src/devtools/gc-ubench/harness.js
js/src/gc/GC.cpp
js/src/gc/GCRuntime.h
--- a/js/src/devtools/gc-ubench/harness.js
+++ b/js/src/devtools/gc-ubench/harness.js
@@ -3,16 +3,17 @@
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 // Per-frame time sampling infra. Also GC'd: hopefully will not perturb things too badly.
 var numSamples = 500;
 var delays = new Array(numSamples);
 var gcs = new Array(numSamples);
 var minorGCs = new Array(numSamples);
 var majorGCs = new Array(numSamples);
+var slices = new Array(numSamples);
 var gcBytes = new Array(numSamples);
 var mallocBytes = new Array(numSamples);
 var sampleIndex = 0;
 var sampleTime = 16; // ms
 var gHistogram = new Map(); // {ms: count}
 
 var stroke = {
     'gcslice': 'rgb(255,100,0)',
@@ -162,39 +163,34 @@ LatencyGraph.prototype.draw = function (
     ctx.stroke();
 
     // Draw vertical lines marking minor and major GCs
     if (features.showingGCs) {
         const { width, height } = ctx.canvas;
 
         ctx.strokeStyle = stroke.gcslice;
         var idx = sampleIndex % numSamples;
-        const count = {major: majorGCs[idx], minor: 0, any: gcs[idx]};
+        const count = {
+            major: majorGCs[idx],
+            minor: 0,
+            slice: slices[idx]
+        };
         for (var i = 0; i < numSamples; i++) {
-            // A minor GC bumps count.minor and count.any. A major GC slice
-            // bumps count.any only (though it might run a minor GC too.) The
-            // first slice of a major GC bumps count.major and count.any, and
-            // will also do a minor GC and so ends up bumping count.minor as
-            // well.
-            //
-            // So there's no way to distinguish a minor GC from a major GC
-            // slice that also runs a minor GC! Both bump count.any and
-            // count.minor and not count.major.
             idx = (sampleIndex + i) % numSamples;
             const isMajorStart = count.major < majorGCs[idx];
-            if (count.any < gcs[idx]) {
+            if (count.slice < slices[idx]) {
                 if (isMajorStart) ctx.strokeStyle = stroke.initialMajor;
                 ctx.beginPath();
                 ctx.moveTo(this.xpos(idx), 0);
                 ctx.lineTo(this.xpos(idx), this.layout.xAxisLabel_Y);
                 ctx.stroke();
                 if (isMajorStart) ctx.strokeStyle = stroke.gcslice;
             }
-            count.any = gcs[idx];
             count.major = majorGCs[idx];
+            count.slice = slices[idx];
         }
 
         ctx.strokeStyle = stroke.minor;
         idx = sampleIndex % numSamples;
         count.minor = gcs[idx];
         for (var i = 0; i < numSamples; i++) {
             idx = (sampleIndex + i) % numSamples;
             if (count.minor < minorGCs[idx]) {
@@ -363,16 +359,24 @@ function handler(timestamp)
         var idx = sampleIndex % numSamples;
         delays[idx] = delay;
         if (features.trackingSizes)
             gcBytes[idx] = performance.mozMemory.gcBytes;
         if (features.showingGCs) {
             gcs[idx] = performance.mozMemory.gcNumber;
             minorGCs[idx] = performance.mozMemory.minorGCCount;
             majorGCs[idx] = performance.mozMemory.majorGCCount;
+
+            // Previous versions lacking sliceCount will fall back to assuming
+            // any GC activity was a major GC slice, even though that
+            // incorrectly includes minor GCs. Although this file is versioned
+            // with the code that implements the new sliceCount field, it is
+            // common to load the gc-ubench index.html with different browser
+            // versions.
+            slices[idx] = performance.mozMemory.sliceCount || performance.mozMemory.gcNumber;
         }
     }
 
     latencyGraph.draw();
     if (memoryGraph)
         memoryGraph.draw();
     window.requestAnimationFrame(handler);
 }
--- a/js/src/gc/GC.cpp
+++ b/js/src/gc/GC.cpp
@@ -981,16 +981,17 @@ GCRuntime::GCRuntime(JSRuntime* rt)
       cleanUpEverything(false),
       grayBufferState(GCRuntime::GrayBufferState::Unused),
       grayBitsValid(false),
       majorGCTriggerReason(JS::GCReason::NO_REASON),
       fullGCForAtomsRequested_(false),
       minorGCNumber(0),
       majorGCNumber(0),
       number(0),
+      sliceNumber(0),
       isFull(false),
       incrementalState(gc::State::NotActive),
       initialState(gc::State::NotActive),
 #ifdef JS_GC_ZEAL
       useZeal(false),
 #endif
       lastMarkSlice(false),
       safeToYield(true),
@@ -7079,16 +7080,18 @@ void GCRuntime::incrementalSlice(SliceBu
     /*
      * Yields between slices occurs at predetermined points in these modes;
      * the budget is not used.
      */
     stats().writeLogMessage("Using unlimited budget for two-slice zeal mode");
     budget.makeUnlimited();
   }
 
+  incGcSliceNumber();
+
   switch (incrementalState) {
     case State::NotActive:
       incMajorGcNumber();
       invocationKind = gckind.valueOr(GC_NORMAL);
       initialReason = reason;
       cleanUpEverything = ShouldCleanUpEverything(reason, invocationKind);
       sweepOnBackgroundThread = ShouldSweepOnBackgroundThread(reason);
       isCompacting = shouldCompact();
@@ -8888,16 +8891,22 @@ static bool MajorGCCountGetter(JSContext
 }
 
 static bool MinorGCCountGetter(JSContext* cx, unsigned argc, Value* vp) {
   CallArgs args = CallArgsFromVp(argc, vp);
   args.rval().setNumber(double(cx->runtime()->gc.minorGCCount()));
   return true;
 }
 
+static bool GCSliceCountGetter(JSContext* cx, unsigned argc, Value* vp) {
+  CallArgs args = CallArgsFromVp(argc, vp);
+  args.rval().setNumber(double(cx->runtime()->gc.gcSliceCount()));
+  return true;
+}
+
 static bool ZoneGCBytesGetter(JSContext* cx, unsigned argc, Value* vp) {
   CallArgs args = CallArgsFromVp(argc, vp);
   args.rval().setNumber(double(cx->zone()->zoneSize.gcBytes()));
   return true;
 }
 
 static bool ZoneGCTriggerBytesGetter(JSContext* cx, unsigned argc, Value* vp) {
   CallArgs args = CallArgsFromVp(argc, vp);
@@ -8959,17 +8968,18 @@ JSObject* NewMemoryInfoObject(JSContext*
   struct NamedGetter {
     const char* name;
     JSNative getter;
   } getters[] = {{"gcBytes", GCBytesGetter},
                  {"gcMaxBytes", GCMaxBytesGetter},
                  {"gcIsHighFrequencyMode", GCHighFreqGetter},
                  {"gcNumber", GCNumberGetter},
                  {"majorGCCount", MajorGCCountGetter},
-                 {"minorGCCount", MinorGCCountGetter}};
+                 {"minorGCCount", MinorGCCountGetter},
+                 {"sliceCount", GCSliceCountGetter}};
 
   for (auto pair : getters) {
 #ifdef JS_MORE_DETERMINISTIC
     JSNative getter = DummyGetter;
 #else
     JSNative getter = pair.getter;
 #endif
     if (!JS_DefineProperty(cx, obj, pair.name, getter, nullptr,
--- a/js/src/gc/GCRuntime.h
+++ b/js/src/gc/GCRuntime.h
@@ -404,16 +404,19 @@ class GCRuntime {
   void incMinorGcNumber() {
     ++minorGCNumber;
     ++number;
   }
 
   uint64_t majorGCCount() const { return majorGCNumber; }
   void incMajorGcNumber() { ++majorGCNumber; }
 
+  uint64_t gcSliceCount() const { return sliceNumber; }
+  void incGcSliceNumber() { ++sliceNumber; }
+
   int64_t defaultSliceBudgetMS() const { return defaultTimeBudgetMS_; }
 
   bool isIncrementalGc() const { return isIncremental; }
   bool isFullGc() const { return isFull; }
   bool isCompactingGc() const { return isCompacting; }
 
   bool areGrayBitsValid() const { return grayBitsValid; }
   void setGrayBitsInvalid() { grayBitsValid = false; }
@@ -827,16 +830,19 @@ class GCRuntime {
   MainThreadData<uint64_t> minorGCNumber;
 
   /* Incremented at the start of every major GC. */
   MainThreadData<uint64_t> majorGCNumber;
 
   /* Incremented on every GC slice or minor collection. */
   MainThreadData<uint64_t> number;
 
+  /* Incremented on every GC slice. */
+  MainThreadData<uint64_t> sliceNumber;
+
   /* Whether the currently running GC can finish in multiple slices. */
   MainThreadData<bool> isIncremental;
 
   /* Whether all zones are being collected in first GC slice. */
   MainThreadData<bool> isFull;
 
   /* Whether the heap will be compacted at the end of GC. */
   MainThreadData<bool> isCompacting;