Bug 1129314 - On OOM, abort the stats collection entirely, r=terrence
authorSteve Fink <sfink@mozilla.com>
Thu, 05 Feb 2015 10:43:51 -0800
changeset 227957 ad796c7bd98a82e7c70a9fe61acbfe1078e67f70
parent 227956 e5f4d89fe8a293c8d02a53383b2960ab504a53c6
child 227958 7eb2c4dc10f2f0bdd6d60e00ded65ca1966651bd
push id28249
push userphilringnalda@gmail.com
push dateSat, 07 Feb 2015 16:46:17 +0000
treeherdermozilla-central@94f30665f300 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersterrence
bugs1129314
milestone38.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 1129314 - On OOM, abort the stats collection entirely, r=terrence
js/src/gc/Statistics.cpp
js/src/gc/Statistics.h
--- a/js/src/gc/Statistics.cpp
+++ b/js/src/gc/Statistics.cpp
@@ -475,16 +475,18 @@ Statistics::sccDurations(int64_t *total,
         *total += sccTimes[i];
         *maxPause = Max(*maxPause, sccTimes[i]);
     }
 }
 
 bool
 Statistics::formatData(StatisticsSerializer &ss, uint64_t timestamp)
 {
+    MOZ_ASSERT(!aborted);
+
     int64_t total, longest;
     gcDuration(&total, &longest);
 
     int64_t sccTotal, sccLongest;
     sccDurations(&sccTotal, &sccLongest);
 
     double mmu20 = computeMMU(20 * PRMJ_USEC_PER_MSEC);
     double mmu50 = computeMMU(50 * PRMJ_USEC_PER_MSEC);
@@ -768,17 +770,17 @@ Statistics::Statistics(JSRuntime *rt)
     nonincrementalReason(nullptr),
     timedGCStart(0),
     preBytes(0),
     maxPauseInInterval(0),
     phaseNestingDepth(0),
     activeDagSlot(PHASE_DAG_NONE),
     suspendedPhaseNestingDepth(0),
     sliceCallback(nullptr),
-    abortSlices(false)
+    aborted(false)
 {
     PodArrayZero(phaseTotals);
     PodArrayZero(counts);
     PodArrayZero(phaseStartTimes);
     for (size_t d = 0; d < MAX_MULTIPARENT_PHASES + 1; d++)
         PodArrayZero(phaseTimes[d]);
 
     static bool initialized = false;
@@ -894,16 +896,23 @@ SumPhase(Phase phase, int64_t (*times)[P
     for (size_t i = 0; i < Statistics::MAX_MULTIPARENT_PHASES + 1; i++)
         sum += times[i][phase];
     return sum;
 }
 
 void
 Statistics::printStats()
 {
+    if (aborted) {
+        if (fullFormat)
+            fprintf(fp, "OOM during GC statistics collection. The report is unavailable for this GC.\n");
+        fflush(fp);
+        return;
+    }
+
     if (fullFormat) {
         UniqueChars msg = formatDetailedMessage();
         if (msg)
             fprintf(fp, "GC(T+%.3fs) %s\n", t(slices[0].start - startupTime) / 1000.0, msg.get());
     } else {
         int64_t total, longest;
         gcDuration(&total, &longest);
 
@@ -950,45 +959,47 @@ Statistics::endGC()
     runtime->addTelemetry(JS_TELEMETRY_GC_SWEEP_MS, t(phaseTimes[PHASE_DAG_NONE][PHASE_SWEEP]));
     runtime->addTelemetry(JS_TELEMETRY_GC_MARK_ROOTS_MS, t(markRootsTotal));
     runtime->addTelemetry(JS_TELEMETRY_GC_MARK_GRAY_MS, t(phaseTimes[PHASE_DAG_NONE][PHASE_SWEEP_MARK_GRAY]));
     runtime->addTelemetry(JS_TELEMETRY_GC_NON_INCREMENTAL, !!nonincrementalReason);
     runtime->addTelemetry(JS_TELEMETRY_GC_INCREMENTAL_DISABLED, !runtime->gc.isIncrementalGCAllowed());
     runtime->addTelemetry(JS_TELEMETRY_GC_SCC_SWEEP_TOTAL_MS, t(sccTotal));
     runtime->addTelemetry(JS_TELEMETRY_GC_SCC_SWEEP_MAX_PAUSE_MS, t(sccLongest));
 
-    double mmu50 = computeMMU(50 * PRMJ_USEC_PER_MSEC);
-    runtime->addTelemetry(JS_TELEMETRY_GC_MMU_50, mmu50 * 100);
+    if (!aborted) {
+        double mmu50 = computeMMU(50 * PRMJ_USEC_PER_MSEC);
+        runtime->addTelemetry(JS_TELEMETRY_GC_MMU_50, mmu50 * 100);
+    }
 
     if (fp)
         printStats();
 
     // Clear the timers at the end of a GC because we accumulate time in
     // between GCs for some (which come before PHASE_GC_BEGIN in the list.)
     PodZero(&phaseStartTimes[PHASE_GC_BEGIN], PHASE_LIMIT - PHASE_GC_BEGIN);
     for (size_t d = PHASE_DAG_NONE; d < MAX_MULTIPARENT_PHASES + 1; d++)
         PodZero(&phaseTimes[d][PHASE_GC_BEGIN], PHASE_LIMIT - PHASE_GC_BEGIN);
 
-    abortSlices = false;
+    aborted = false;
 }
 
 void
 Statistics::beginSlice(const ZoneGCStats &zoneStats, JSGCInvocationKind gckind,
                        JS::gcreason::Reason reason)
 {
     this->zoneStats = zoneStats;
 
     bool first = !runtime->gc.isIncrementalGCInProgress();
     if (first)
         beginGC(gckind);
 
     SliceData data(reason, PRMJ_Now(), GetPageFaultCount());
     if (!slices.append(data)) {
         // OOM testing fails if we CrashAtUnhandlableOOM here.
-        abortSlices = true;
+        aborted = true;
         return;
     }
 
     runtime->addTelemetry(JS_TELEMETRY_GC_REASON, reason);
 
     // Slice callbacks should only fire for the outermost level
     if (++gcDepth == 1) {
         bool wasFullGC = zoneStats.isCollectingAllZones();
@@ -996,17 +1007,17 @@ Statistics::beginSlice(const ZoneGCStats
             (*sliceCallback)(runtime, first ? JS::GC_CYCLE_BEGIN : JS::GC_SLICE_BEGIN,
                              JS::GCDescription(!wasFullGC));
     }
 }
 
 void
 Statistics::endSlice()
 {
-    if (!abortSlices) {
+    if (!aborted) {
         slices.back().end = PRMJ_Now();
         slices.back().endFaults = GetPageFaultCount();
 
         runtime->addTelemetry(JS_TELEMETRY_GC_SLICE_MS, t(slices.back().end - slices.back().start));
         runtime->addTelemetry(JS_TELEMETRY_GC_RESET, !!slices.back().resetReason);
     }
 
     bool last = !runtime->gc.isIncrementalGCInProgress();
@@ -1156,19 +1167,16 @@ Statistics::endSCC(unsigned scc, int64_t
  * that means that, for any 50ms window of time, at least 80% of the window is
  * devoted to the mutator. In other words, the GC is running for at most 20% of
  * the window, or 10ms. The GC can run multiple slices during the 50ms window
  * as long as the total time it spends is at most 10ms.
  */
 double
 Statistics::computeMMU(int64_t window)
 {
-    if (abortSlices)
-        return 0.0;
-
     MOZ_ASSERT(!slices.empty());
 
     int64_t gc = slices[0].end - slices[0].start;
     int64_t gcMax = gc;
 
     if (gc >= window)
         return 0.0;
 
--- a/js/src/gc/Statistics.h
+++ b/js/src/gc/Statistics.h
@@ -164,17 +164,20 @@ struct Statistics
 
     void beginSlice(const ZoneGCStats &zoneStats, JSGCInvocationKind gckind,
                     JS::gcreason::Reason reason);
     void endSlice();
 
     void startTimingMutator();
     bool stopTimingMutator(double &mutator_ms, double &gc_ms);
 
-    void reset(const char *reason) { slices.back().resetReason = reason; }
+    void reset(const char *reason) {
+        if (!aborted)
+            slices.back().resetReason = reason;
+    }
     void nonincremental(const char *reason) { nonincrementalReason = reason; }
 
     void count(Stat s) {
         MOZ_ASSERT(s < STAT_LIMIT);
         counts[s]++;
     }
 
     int64_t beginSCC();
@@ -276,20 +279,20 @@ struct Statistics
     size_t suspendedPhaseNestingDepth;
 
     /* Sweep times for SCCs of compartments. */
     Vector<int64_t, 0, SystemAllocPolicy> sccTimes;
 
     JS::GCSliceCallback sliceCallback;
 
     /*
-     * True if we saw an OOM while allocating slices. Slices will not be
-     * individually recorded for the remainder of this GC.
+     * True if we saw an OOM while allocating slices. The statistics for this
+     * GC will be invalid.
      */
-    bool abortSlices;
+    bool aborted;
 
     void beginGC(JSGCInvocationKind kind);
     void endGC();
 
     void recordPhaseEnd(Phase phase);
 
     void gcDuration(int64_t *total, int64_t *maxPause);
     void sccDurations(int64_t *total, int64_t *maxPause);