Bug 1154441 - imported patch budget, r=terrence
authorSteve Fink <sfink@mozilla.com>
Fri, 10 Apr 2015 13:58:06 -0700
changeset 270505 4160e3afd2900a971516f856047694e37f0bd120
parent 270504 e08e45fd8922bce8cdd445e921ca66c58acc1083
child 270506 1ae18f4c151fb6d8fc5fad0dd1bc7895e08a34ff
push id4830
push userjlund@mozilla.com
push dateMon, 29 Jun 2015 20:18:48 +0000
treeherdermozilla-beta@4c2175bb0420 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersterrence
bugs1154441
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 1154441 - imported patch budget, r=terrence
js/public/SliceBudget.h
js/src/gc/Statistics.cpp
js/src/gc/Statistics.h
js/src/jsgc.cpp
--- a/js/public/SliceBudget.h
+++ b/js/public/SliceBudget.h
@@ -28,16 +28,22 @@ struct JS_PUBLIC_API(WorkBudget)
 /*
  * This class records how much work has been done in a given collection slice,
  * so that we can return before pausing for too long. Some slices are allowed
  * to run for unlimited time, and others are bounded. To reduce the number of
  * gettimeofday calls, we only check the time every 1000 operations.
  */
 struct JS_PUBLIC_API(SliceBudget)
 {
+    // Memory of the originally requested budget. If isUnlimited, neither of
+    // these are in use. If deadline==0, then workBudget is valid. Otherwise
+    // timeBudget is valid.
+    TimeBudget timeBudget;
+    WorkBudget workBudget;
+
     int64_t deadline; /* in microseconds */
     intptr_t counter;
 
     static const intptr_t CounterReset = 1000;
 
     static const int64_t Unlimited = -1;
 
     /* Use to create an unlimited budget. */
@@ -59,20 +65,22 @@ struct JS_PUBLIC_API(SliceBudget)
     }
 
     bool isOverBudget() {
         if (counter > 0)
             return false;
         return checkOverBudget();
     }
 
-    bool isUnlimited() {
+    bool isUnlimited() const {
         return deadline == unlimitedDeadline;
     }
 
+    int describe(char* buffer, size_t maxlen) const;
+
   private:
     bool checkOverBudget();
 
     static const int64_t unlimitedDeadline = INT64_MAX;
     static const intptr_t unlimitedStartCounter = INTPTR_MAX;
 };
 
 } // namespace js
--- a/js/src/gc/Statistics.cpp
+++ b/js/src/gc/Statistics.cpp
@@ -524,23 +524,27 @@ Statistics::formatData(StatisticsSeriali
         for (size_t i = 0; i < slices.length(); i++) {
             int64_t width = slices[i].duration();
             if (i != 0 && i != slices.length() - 1 && width < SLICE_MIN_REPORT_TIME &&
                 !slices[i].resetReason && !ss.isJSON())
             {
                 continue;
             }
 
+            char budgetDescription[200];
+            slices[i].budget.describe(budgetDescription, sizeof(budgetDescription) - 1);
+
             ss.beginObject(nullptr);
             ss.extra("    ");
             ss.appendNumber("Slice", "%d", "", i);
             ss.appendDecimal("Pause", "", t(width));
             ss.extra(" (");
             ss.appendDecimal("When", "ms", t(slices[i].start - slices[0].start));
             ss.appendString("Reason", ExplainReason(slices[i].reason));
+            ss.appendString("Budget", budgetDescription);
             if (ss.isJSON()) {
                 ss.appendDecimal("Page Faults", "",
                                  double(slices[i].endFaults - slices[i].startFaults));
 
                 ss.appendNumber("Start Timestamp", "%llu", "", (unsigned long long)slices[i].start);
                 ss.appendNumber("End Timestamp", "%llu", "", (unsigned long long)slices[i].end);
             }
             if (slices[i].resetReason)
@@ -624,31 +628,34 @@ Statistics::formatDescription()
                                                                      counts[STAT_DESTROY_CHUNK],
                 double(ArenaSize * counts[STAT_ARENA_RELOCATED]) / bytesPerMiB);
     return make_string_copy(buffer);
 }
 
 UniqueChars
 Statistics::formatSliceDescription(unsigned i, const SliceData& slice)
 {
+    char budgetDescription[200];
+    slice.budget.describe(budgetDescription, sizeof(budgetDescription) - 1);
+
     const char* format =
 "\
   ---- Slice %u ----\n\
     Reason: %s\n\
     Reset: %s%s\n\
     Page Faults: %ld\n\
-    Pause: %.3fms  (@ %.3fms)\n\
+    Pause: %.3fms of %s budget (@ %.3fms)\n\
 ";
     char buffer[1024];
     memset(buffer, 0, sizeof(buffer));
     JS_snprintf(buffer, sizeof(buffer), format, i,
                 ExplainReason(slice.reason),
                 slice.resetReason ? "yes - " : "no", slice.resetReason ? slice.resetReason : "",
                 uint64_t(slice.endFaults - slice.startFaults),
-                t(slice.duration()), t(slice.start - slices[0].start));
+                t(slice.duration()), budgetDescription, t(slice.start - slices[0].start));
     return make_string_copy(buffer);
 }
 
 UniqueChars
 Statistics::formatTotals()
 {
     int64_t total, longest;
     gcDuration(&total, &longest);
@@ -980,25 +987,25 @@ Statistics::endGC()
     for (size_t d = PHASE_DAG_NONE; d < MAX_MULTIPARENT_PHASES + 1; d++)
         PodZero(&phaseTimes[d][PHASE_GC_BEGIN], PHASE_LIMIT - PHASE_GC_BEGIN);
 
     aborted = false;
 }
 
 void
 Statistics::beginSlice(const ZoneGCStats& zoneStats, JSGCInvocationKind gckind,
-                       JS::gcreason::Reason reason)
+                       SliceBudget budget, JS::gcreason::Reason reason)
 {
     this->zoneStats = zoneStats;
 
     bool first = !runtime->gc.isIncrementalGCInProgress();
     if (first)
         beginGC(gckind);
 
-    SliceData data(reason, PRMJ_Now(), GetPageFaultCount());
+    SliceData data(budget, reason, PRMJ_Now(), GetPageFaultCount());
     if (!slices.append(data)) {
         // OOM testing fails if we CrashAtUnhandlableOOM here.
         aborted = true;
         return;
     }
 
     runtime->addTelemetry(JS_TELEMETRY_GC_REASON, reason);
 
--- a/js/src/gc/Statistics.h
+++ b/js/src/gc/Statistics.h
@@ -157,17 +157,17 @@ struct Statistics
     explicit Statistics(JSRuntime* rt);
     ~Statistics();
 
     void beginPhase(Phase phase);
     void endPhase(Phase phase);
     void endParallelPhase(Phase phase, const GCParallelTask* task);
 
     void beginSlice(const ZoneGCStats& zoneStats, JSGCInvocationKind gckind,
-                    JS::gcreason::Reason reason);
+                    SliceBudget budget, JS::gcreason::Reason reason);
     void endSlice();
 
     void startTimingMutator();
     bool stopTimingMutator(double& mutator_ms, double& gc_ms);
 
     void reset(const char* reason) {
         if (!aborted)
             slices.back().resetReason = reason;
@@ -201,23 +201,26 @@ struct Statistics
         if (phaseNestingDepth == 1)
             return phaseNesting[0] == PHASE_MUTATOR ? PHASE_NONE : phaseNesting[0];
         return phaseNesting[phaseNestingDepth - 1];
     }
 
     static const size_t MAX_NESTING = 20;
 
     struct SliceData {
-        SliceData(JS::gcreason::Reason reason, int64_t start, size_t startFaults)
-          : reason(reason), resetReason(nullptr), start(start), startFaults(startFaults)
+        SliceData(SliceBudget budget, JS::gcreason::Reason reason, int64_t start, size_t startFaults)
+          : budget(budget), reason(reason),
+            resetReason(nullptr),
+            start(start), startFaults(startFaults)
         {
             for (size_t i = 0; i < MAX_MULTIPARENT_PHASES + 1; i++)
                 mozilla::PodArrayZero(phaseTimes[i]);
         }
 
+        SliceBudget budget;
         JS::gcreason::Reason reason;
         const char* resetReason;
         int64_t start, end;
         size_t startFaults, endFaults;
         int64_t phaseTimes[MAX_MULTIPARENT_PHASES + 1][PHASE_LIMIT];
 
         int64_t duration() const { return end - start; }
     };
@@ -313,22 +316,22 @@ struct Statistics
     UniqueChars formatPhaseTimes(int64_t (*phaseTimes)[PHASE_LIMIT]);
 
     double computeMMU(int64_t resolution);
 };
 
 struct AutoGCSlice
 {
     AutoGCSlice(Statistics& stats, const ZoneGCStats& zoneStats, JSGCInvocationKind gckind,
-                JS::gcreason::Reason reason
+                SliceBudget budget, JS::gcreason::Reason reason
                 MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
       : stats(stats)
     {
         MOZ_GUARD_OBJECT_NOTIFIER_INIT;
-        stats.beginSlice(zoneStats, gckind, reason);
+        stats.beginSlice(zoneStats, gckind, budget, reason);
     }
     ~AutoGCSlice() { stats.endSlice(); }
 
     Statistics& stats;
     MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
 };
 
 struct AutoPhase
--- a/js/src/jsgc.cpp
+++ b/js/src/jsgc.cpp
@@ -2919,41 +2919,55 @@ GCRuntime::refillFreeListInGC(Zone* zone
     MOZ_ASSERT(rt->isHeapMajorCollecting());
     MOZ_ASSERT(!rt->gc.isBackgroundSweeping());
 
     AutoMaybeStartBackgroundAllocation maybeStartBackgroundAllocation;
     return zone->arenas.allocateFromArena(zone, thingKind, maybeStartBackgroundAllocation);
 }
 
 SliceBudget::SliceBudget()
+  : timeBudget(Unlimited), workBudget(Unlimited)
 {
     makeUnlimited();
 }
 
 SliceBudget::SliceBudget(TimeBudget time)
+  : timeBudget(time), workBudget(Unlimited)
 {
     if (time.budget < 0) {
         makeUnlimited();
     } else {
         // Note: TimeBudget(0) is equivalent to WorkBudget(CounterReset).
         deadline = PRMJ_Now() + time.budget * PRMJ_USEC_PER_MSEC;
         counter = CounterReset;
     }
 }
 
 SliceBudget::SliceBudget(WorkBudget work)
+  : timeBudget(Unlimited), workBudget(work)
 {
     if (work.budget < 0) {
         makeUnlimited();
     } else {
         deadline = 0;
         counter = work.budget;
     }
 }
 
+int
+SliceBudget::describe(char* buffer, size_t maxlen) const
+{
+    if (isUnlimited())
+        return JS_snprintf(buffer, maxlen, "unlimited");
+    else if (deadline == 0)
+        return JS_snprintf(buffer, maxlen, "work(%lld)", workBudget.budget);
+    else
+        return JS_snprintf(buffer, maxlen, "%lldms", timeBudget.budget);
+}
+
 bool
 SliceBudget::checkOverBudget()
 {
     bool over = PRMJ_Now() >= deadline;
     if (!over)
         counter = CounterReset;
     return over;
 }
@@ -6089,17 +6103,17 @@ GCRuntime::collect(bool incremental, Sli
 #ifdef JS_GC_ZEAL
     if (deterministicOnly && !IsDeterministicGCReason(reason))
         return;
 #endif
 
     AutoStopVerifyingBarriers av(rt, reason == JS::gcreason::SHUTDOWN_CC ||
                                      reason == JS::gcreason::DESTROY_RUNTIME);
 
-    gcstats::AutoGCSlice agc(stats, scanZonesBeforeGC(), invocationKind, reason);
+    gcstats::AutoGCSlice agc(stats, scanZonesBeforeGC(), invocationKind, budget, reason);
 
     bool repeat = false;
     do {
         /*
          * Let the API user decide to defer a GC if it wants to (unless this
          * is the last context). Invoke the callback regardless.
          */
         if (!isIncrementalGCInProgress()) {
@@ -6201,17 +6215,19 @@ GCRuntime::abortGC()
     JS_AbortIfWrongThread(rt);
 
     MOZ_ALWAYS_TRUE(!rt->isHeapBusy());
     MOZ_ASSERT(!rt->currentThreadHasExclusiveAccess());
     MOZ_ASSERT(!rt->mainThread.suppressGC);
 
     AutoStopVerifyingBarriers av(rt, false);
 
-    gcstats::AutoGCSlice agc(stats, scanZonesBeforeGC(), invocationKind, JS::gcreason::ABORT_GC);
+    SliceBudget unlimited;
+    gcstats::AutoGCSlice agc(stats, scanZonesBeforeGC(), invocationKind,
+                             unlimited, JS::gcreason::ABORT_GC);
 
     evictNursery(JS::gcreason::ABORT_GC);
     AutoDisableStoreBuffer adsb(this);
     AutoTraceSession session(rt, MajorCollecting);
 
     number++;
     resetIncrementalGC("abort");
 }