Bug 1171612 - Use C++11 features to make Statistics module nicer; r=sfink
authorTerrence Cole <terrence@mozilla.com>
Thu, 04 Jun 2015 11:49:31 -0700
changeset 248009 53d75ce7f7fd831fb8c889e579a45b140cc04cbd
parent 248008 46871501aaa3c1ce2347a80d0be394a0792caf12
child 248010 a3e12ac67218200b44f0b9fe0ef484583a40d0c0
push id60878
push usertcole@mozilla.com
push dateWed, 10 Jun 2015 21:46:33 +0000
treeherdermozilla-inbound@53d75ce7f7fd [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssfink
bugs1171612
milestone41.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 1171612 - Use C++11 features to make Statistics module nicer; r=sfink
js/src/gc/Statistics.cpp
js/src/gc/Statistics.h
--- a/js/src/gc/Statistics.cpp
+++ b/js/src/gc/Statistics.cpp
@@ -2,16 +2,17 @@
  * vim: set ts=8 sts=4 et sw=4 tw=99:
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "gc/Statistics.h"
 
 #include "mozilla/ArrayUtils.h"
+#include "mozilla/IntegerRange.h"
 #include "mozilla/PodOperations.h"
 #include "mozilla/UniquePtr.h"
 
 #include <ctype.h>
 #include <stdarg.h>
 #include <stdio.h>
 
 #include "jsprf.h"
@@ -22,16 +23,17 @@
 #include "vm/Debugger.h"
 #include "vm/HelperThreads.h"
 #include "vm/Runtime.h"
 
 using namespace js;
 using namespace js::gc;
 using namespace js::gcstats;
 
+using mozilla::MakeRange;
 using mozilla::PodArrayZero;
 using mozilla::PodZero;
 
 /* Except for the first and last, slices of less than 10ms are not reported. */
 static const int64_t SLICE_MIN_REPORT_TIME = 10 * PRMJ_USEC_PER_MSEC;
 
 /*
  * If this fails, then you can either delete this assertion and allow all
@@ -180,25 +182,25 @@ static const PhaseInfo phases[] = {
     { PHASE_LIMIT, nullptr, PHASE_NO_PARENT }
 };
 
 static ExtraPhaseInfo phaseExtra[PHASE_LIMIT] = { { 0, 0 } };
 
 // Mapping from all nodes with a multi-parented child to a Vector of all
 // multi-parented children and their descendants. (Single-parented children will
 // not show up in this list.)
-static mozilla::Vector<Phase> dagDescendants[Statistics::MAX_MULTIPARENT_PHASES + 1];
+static mozilla::Vector<Phase> dagDescendants[Statistics::NumTimingArrays];
 
 struct AllPhaseIterator {
     int current;
     int baseLevel;
     size_t activeSlot;
     mozilla::Vector<Phase>::Range descendants;
 
-    explicit AllPhaseIterator(Statistics::PhaseTimeTable table)
+    explicit AllPhaseIterator(const Statistics::PhaseTimeTable table)
       : current(0)
       , baseLevel(0)
       , activeSlot(PHASE_DAG_NONE)
       , descendants(dagDescendants[PHASE_DAG_NONE].all()) /* empty range */
     {
     }
 
     void get(Phase* phase, size_t* dagSlot, size_t* level = nullptr) {
@@ -288,17 +290,17 @@ Join(const FragmentVector& fragments, co
             cursor += separatorLength;
         }
     }
 
     return UniqueChars(joined);
 }
 
 static int64_t
-SumChildTimes(size_t phaseSlot, Phase phase, Statistics::PhaseTimeTable phaseTimes)
+SumChildTimes(size_t phaseSlot, Phase phase, const Statistics::PhaseTimeTable phaseTimes)
 {
     // Sum the contributions from single-parented children.
     int64_t total = 0;
     for (unsigned i = 0; i < PHASE_LIMIT; i++) {
         if (phases[i].parent == phase)
             total += phaseTimes[phaseSlot][i];
     }
 
@@ -393,17 +395,17 @@ Statistics::formatCompactSummaryMessage(
         if (!fragments.append(make_string_copy(buffer)))
             return UniqueChars(nullptr);
     }
 
     return Join(fragments);
 }
 
 UniqueChars
-Statistics::formatCompactSlicePhaseTimes(PhaseTimeTable phaseTimes) const
+Statistics::formatCompactSlicePhaseTimes(const PhaseTimeTable phaseTimes) const
 {
     static const int64_t MaxUnaccountedTimeUS = 100;
 
     FragmentVector fragments;
     char buffer[128];
     for (AllPhaseIterator iter(phaseTimes); !iter.done(); iter.advance()) {
         Phase phase;
         size_t dagSlot;
@@ -519,17 +521,17 @@ Statistics::formatDetailedSliceDescripti
                 ExplainReason(slice.reason),
                 slice.resetReason ? "yes - " : "no", slice.resetReason ? slice.resetReason : "",
                 uint64_t(slice.endFaults - slice.startFaults),
                 t(slice.duration()), budgetDescription, t(slice.start - slices[0].start));
     return make_string_copy(buffer);
 }
 
 UniqueChars
-Statistics::formatDetailedPhaseTimes(PhaseTimeTable phaseTimes)
+Statistics::formatDetailedPhaseTimes(const PhaseTimeTable phaseTimes)
 {
     static const char* LevelToIndent[] = { "", "  ", "    ", "      " };
     static const int64_t MaxUnaccountedChildTimeUS = 50;
 
     FragmentVector fragments;
     char buffer[128];
     for (AllPhaseIterator iter(phaseTimes); !iter.done(); iter.advance()) {
         Phase phase;
@@ -706,17 +708,17 @@ FilterJsonKey(const char*const buffer)
         else if (isupper(*c))
             *c = tolower(*c);
         ++c;
     }
     return UniqueChars(mut);
 }
 
 UniqueChars
-Statistics::formatJsonPhaseTimes(PhaseTimeTable phaseTimes)
+Statistics::formatJsonPhaseTimes(const PhaseTimeTable phaseTimes)
 {
     FragmentVector fragments;
     char buffer[128];
     for (AllPhaseIterator iter(phaseTimes); !iter.done(); iter.advance()) {
         Phase phase;
         size_t dagSlot;
         iter.get(&phase, &dagSlot);
 
@@ -744,17 +746,17 @@ Statistics::Statistics(JSRuntime* rt)
     activeDagSlot(PHASE_DAG_NONE),
     suspendedPhaseNestingDepth(0),
     sliceCallback(nullptr),
     aborted(false)
 {
     PodArrayZero(phaseTotals);
     PodArrayZero(counts);
     PodArrayZero(phaseStartTimes);
-    for (size_t d = 0; d < MAX_MULTIPARENT_PHASES + 1; d++)
+    for (auto d : MakeRange(NumTimingArrays))
         PodArrayZero(phaseTimes[d]);
 
     static bool initialized = false;
     if (!initialized) {
         initialized = true;
 
         for (size_t i = 0; i < PHASE_LIMIT; i++)
             MOZ_ASSERT(phases[i].index == i);
@@ -773,17 +775,17 @@ Statistics::Statistics(JSRuntime* rt)
             Phase child = dagChildEdges[i].child;
             MOZ_ASSERT(phases[child].parent == PHASE_MULTI_PARENTS);
             int j = child;
             do {
                 dagDescendants[phaseExtra[parent].dagSlot].append(Phase(j));
                 j++;
             } while (j != PHASE_LIMIT && phases[j].parent != PHASE_MULTI_PARENTS);
         }
-        MOZ_ASSERT(dagSlot <= MAX_MULTIPARENT_PHASES);
+        MOZ_ASSERT(dagSlot <= MaxMultiparentPhases - 1);
 
         // Fill in the depth of each node in the tree. Multi-parented nodes
         // have depth 0.
         mozilla::Vector<Phase> stack;
         stack.append(PHASE_LIMIT); // Dummy entry to avoid special-casing the first node
         for (int i = 0; i < PHASE_LIMIT; i++) {
             if (phases[i].parent == PHASE_NO_PARENT ||
                 phases[i].parent == PHASE_MULTI_PARENTS)
@@ -841,17 +843,17 @@ Statistics::getMaxGCPauseSinceClear()
 {
     return maxPauseInInterval;
 }
 
 static int64_t
 SumPhase(Phase phase, Statistics::PhaseTimeTable times)
 {
     int64_t sum = 0;
-    for (size_t i = 0; i < Statistics::MAX_MULTIPARENT_PHASES + 1; i++)
+    for (auto i : MakeRange(Statistics::NumTimingArrays))
         sum += times[i][phase];
     return sum;
 }
 
 void
 Statistics::printStats()
 {
     if (aborted) {
@@ -873,17 +875,17 @@ Statistics::beginGC(JSGCInvocationKind k
     nonincrementalReason_ = nullptr;
 
     preBytes = runtime->gc.usage.gcBytes();
 }
 
 void
 Statistics::endGC()
 {
-    for (size_t j = 0; j < MAX_MULTIPARENT_PHASES + 1; j++)
+    for (auto j : MakeRange(NumTimingArrays))
         for (int i = 0; i < PHASE_LIMIT; i++)
             phaseTotals[j][i] += phaseTimes[j][i];
 
     int64_t total, longest;
     gcDuration(&total, &longest);
 
     int64_t sccTotal, sccLongest;
     sccDurations(&sccTotal, &sccLongest);
@@ -908,17 +910,17 @@ Statistics::endGC()
     }
 
     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++)
+    for (size_t d = PHASE_DAG_NONE; d < NumTimingArrays; d++)
         PodZero(&phaseTimes[d][PHASE_GC_BEGIN], PHASE_LIMIT - PHASE_GC_BEGIN);
 
     aborted = false;
 }
 
 void
 Statistics::beginSlice(const ZoneGCStats& zoneStats, JSGCInvocationKind gckind,
                        SliceBudget budget, JS::gcreason::Reason reason)
--- a/js/src/gc/Statistics.h
+++ b/js/src/gc/Statistics.h
@@ -3,16 +3,17 @@
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef gc_Statistics_h
 #define gc_Statistics_h
 
 #include "mozilla/DebugOnly.h"
+#include "mozilla/IntegerRange.h"
 #include "mozilla/PodOperations.h"
 #include "mozilla/UniquePtr.h"
 
 #include "jsalloc.h"
 #include "jsgc.h"
 #include "jspubtd.h"
 
 #include "js/GCAPI.h"
@@ -146,17 +147,21 @@ struct Statistics
      * Note that this wastes quite a bit of space, since we have a whole
      * separate array of timing data containing all the phases. We could be
      * more clever and keep an array of pointers biased by the offset of the
      * multi-parented phase, and thereby preserve the simple
      * timings[slot][PHASE_*] indexing. But the complexity doesn't seem worth
      * the few hundred bytes of savings. If we want to extend things to full
      * DAGs, this decision should be reconsidered.
      */
-    static const size_t MAX_MULTIPARENT_PHASES = 6;
+    static const size_t MaxMultiparentPhases = 6;
+    static const size_t NumTimingArrays = MaxMultiparentPhases + 1;
+
+    /* Create a convenient type for referring to tables of phase times. */
+    using PhaseTimeTable = int64_t[NumTimingArrays][PHASE_LIMIT];
 
     explicit Statistics(JSRuntime* rt);
     ~Statistics();
 
     void beginPhase(Phase phase);
     void endPhase(Phase phase);
     void endParallelPhase(Phase phase, const GCParallelTask* task);
 
@@ -206,39 +211,36 @@ struct Statistics
     static const size_t MAX_NESTING = 20;
 
     struct SliceData {
         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++)
+            for (auto i : mozilla::MakeRange(NumTimingArrays))
                 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];
+        PhaseTimeTable phaseTimes;
 
         int64_t duration() const { return end - start; }
     };
 
     typedef Vector<SliceData, 8, SystemAllocPolicy> SliceDataVector;
     typedef SliceDataVector::ConstRange SliceRange;
 
     SliceRange sliceRange() const { return slices.all(); }
     size_t slicesLength() const { return slices.length(); }
 
-    /* Create a convenient typedef for referring tables of phase times. */
-    typedef int64_t const (*PhaseTimeTable)[PHASE_LIMIT];
-
   private:
     JSRuntime* runtime;
 
     int64_t startupTime;
 
     /* File pointer used for MOZ_GCTIMER output. */
     FILE* fp;
 
@@ -259,20 +261,20 @@ struct Statistics
     /* Most recent time when the given phase started. */
     int64_t phaseStartTimes[PHASE_LIMIT];
 
     /* Bookkeeping for GC timings when timingMutator is true */
     int64_t timedGCStart;
     int64_t timedGCTime;
 
     /* Total time in a given phase for this GC. */
-    int64_t phaseTimes[MAX_MULTIPARENT_PHASES + 1][PHASE_LIMIT];
+    PhaseTimeTable phaseTimes;
 
     /* Total time in a given phase over all GCs. */
-    int64_t phaseTotals[MAX_MULTIPARENT_PHASES + 1][PHASE_LIMIT];
+    PhaseTimeTable phaseTotals;
 
     /* Number of events of this type for this GC. */
     unsigned int counts[STAT_LIMIT];
 
     /* Allocated space before the GC started. */
     size_t preBytes;
 
     /* Records the maximum GC pause in an API-controlled interval (in us). */
@@ -307,26 +309,26 @@ struct Statistics
     void endGC();
 
     void recordPhaseEnd(Phase phase);
 
     void gcDuration(int64_t* total, int64_t* maxPause) const;
     void sccDurations(int64_t* total, int64_t* maxPause);
     void printStats();
 
-    UniqueChars formatCompactSlicePhaseTimes(PhaseTimeTable phaseTimes) const;
+    UniqueChars formatCompactSlicePhaseTimes(const PhaseTimeTable phaseTimes) const;
 
     UniqueChars formatDetailedDescription();
     UniqueChars formatDetailedSliceDescription(unsigned i, const SliceData& slice);
-    UniqueChars formatDetailedPhaseTimes(PhaseTimeTable phaseTimes);
+    UniqueChars formatDetailedPhaseTimes(const PhaseTimeTable phaseTimes);
     UniqueChars formatDetailedTotals();
 
     UniqueChars formatJsonDescription(uint64_t timestamp);
     UniqueChars formatJsonSliceDescription(unsigned i, const SliceData& slice);
-    UniqueChars formatJsonPhaseTimes(PhaseTimeTable phaseTimes);
+    UniqueChars formatJsonPhaseTimes(const PhaseTimeTable phaseTimes);
 
     double computeMMU(int64_t resolution) const;
 };
 
 struct AutoGCSlice
 {
     AutoGCSlice(Statistics& stats, const ZoneGCStats& zoneStats, JSGCInvocationKind gckind,
                 SliceBudget budget, JS::gcreason::Reason reason