☠☠ backed out by 7bf6fa55b2c7 ☠ ☠ | |
author | Steve Fink <sfink@mozilla.com> |
Wed, 26 Apr 2017 13:48:09 -0700 | |
changeset 355583 | 4dee851a0d45655260ace1c01eb4dd9e9905942f |
parent 355582 | 86277c75f5f7c5320214b5b99d2ca70af027693c |
child 355584 | 5fe280e53d4f474f5f16ff834e0b9cf55745d746 |
push id | 89703 |
push user | sfink@mozilla.com |
push date | Fri, 28 Apr 2017 18:04:28 +0000 |
treeherder | mozilla-inbound@2cd1662bcc5b [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
bugs | 1322560 |
milestone | 55.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
|
js/public/GCAPI.h | file | annotate | diff | comparison | revisions | |
js/src/gc/Statistics.cpp | file | annotate | diff | comparison | revisions | |
js/src/gc/Statistics.h | file | annotate | diff | comparison | revisions | |
js/src/jsgc.cpp | file | annotate | diff | comparison | revisions | |
js/src/vm/Debugger.cpp | file | annotate | diff | comparison | revisions |
--- a/js/public/GCAPI.h +++ b/js/public/GCAPI.h @@ -8,16 +8,17 @@ #define js_GCAPI_h #include "mozilla/TimeStamp.h" #include "mozilla/Vector.h" #include "js/GCAnnotations.h" #include "js/HeapAPI.h" #include "js/UniquePtr.h" +#include "js/Utility.h" namespace js { namespace gc { class GCRuntime; } // namespace gc namespace gcstats { struct Statistics; } // namespace gcstats @@ -340,16 +341,24 @@ struct JS_PUBLIC_API(GCDescription) { GCDescription(bool isZone, JSGCInvocationKind kind, gcreason::Reason reason) : isZone_(isZone), invocationKind_(kind), reason_(reason) {} char16_t* formatSliceMessage(JSContext* cx) const; char16_t* formatSummaryMessage(JSContext* cx) const; char16_t* formatJSON(JSContext* cx, uint64_t timestamp) const; + mozilla::TimeStamp startTime(JSContext* cx) const; + mozilla::TimeStamp endTime(JSContext* cx) const; + mozilla::TimeStamp lastSliceStart(JSContext* cx) const; + mozilla::TimeStamp lastSliceEnd(JSContext* cx) const; + + JS::UniqueChars sliceToJSON(JSContext* cx) const; + JS::UniqueChars summaryToJSON(JSContext* cx) const; + JS::dbg::GarbageCollectionEvent::Ptr toGCEvent(JSContext* cx) const; }; typedef void (* GCSliceCallback)(JSContext* cx, GCProgress progress, const GCDescription& desc); /** * The GC slice callback is called at the beginning and end of each slice. This
--- a/js/src/gc/Statistics.cpp +++ b/js/src/gc/Statistics.cpp @@ -298,20 +298,20 @@ struct AllPhaseIterator { return phases[current].parent == PHASE_MULTI_PARENTS; } }; void Statistics::gcDuration(TimeDuration* total, TimeDuration* maxPause) const { *total = *maxPause = 0; - for (const SliceData* slice = slices.begin(); slice != slices.end(); slice++) { - *total += slice->duration(); - if (slice->duration() > *maxPause) - *maxPause = slice->duration(); + for (auto& slice : slices_) { + *total += slice.duration(); + if (slice.duration() > *maxPause) + *maxPause = slice.duration(); } if (*maxPause > maxPauseInInterval) maxPauseInInterval = *maxPause; } void Statistics::sccDurations(TimeDuration* total, TimeDuration* maxPause) const { @@ -374,37 +374,37 @@ SumChildTimes(size_t phaseSlot, Phase ph } return total; } UniqueChars Statistics::formatCompactSliceMessage() const { // Skip if we OOM'ed. - if (slices.length() == 0) + if (slices_.length() == 0) return UniqueChars(nullptr); - const size_t index = slices.length() - 1; - const SliceData& slice = slices[index]; + const size_t index = slices_.length() - 1; + const SliceData& slice = slices_.back(); char budgetDescription[200]; slice.budget.describe(budgetDescription, sizeof(budgetDescription) - 1); const char* format = "GC Slice %u - Pause: %.3fms of %s budget (@ %.3fms); Reason: %s; Reset: %s%s; Times: "; char buffer[1024]; SprintfLiteral(buffer, format, index, - t(slice.duration()), budgetDescription, t(slice.start - slices[0].start), + t(slice.duration()), budgetDescription, t(slice.start - slices_[0].start), ExplainReason(slice.reason), slice.wasReset() ? "yes - " : "no", slice.wasReset() ? ExplainAbortReason(slice.resetReason) : ""); FragmentVector fragments; if (!fragments.append(DuplicateString(buffer)) || - !fragments.append(formatCompactSlicePhaseTimes(slices[index].phaseTimes))) + !fragments.append(formatCompactSlicePhaseTimes(slices_[index].phaseTimes))) { return UniqueChars(nullptr); } return Join(fragments); } UniqueChars Statistics::formatCompactSummaryMessage() const @@ -493,21 +493,21 @@ Statistics::formatCompactSlicePhaseTimes UniqueChars Statistics::formatDetailedMessage() const { FragmentVector fragments; if (!fragments.append(formatDetailedDescription())) return UniqueChars(nullptr); - if (slices.length() > 1) { - for (unsigned i = 0; i < slices.length(); i++) { - if (!fragments.append(formatDetailedSliceDescription(i, slices[i]))) + if (!slices_.empty()) { + for (unsigned i = 0; i < slices_.length(); i++) { + if (!fragments.append(formatDetailedSliceDescription(i, slices_[i]))) return UniqueChars(nullptr); - if (!fragments.append(formatDetailedPhaseTimes(slices[i].phaseTimes))) + if (!fragments.append(formatDetailedPhaseTimes(slices_[i].phaseTimes))) return UniqueChars(nullptr); } } if (!fragments.append(formatDetailedTotals())) return UniqueChars(nullptr); if (!fragments.append(formatDetailedPhaseTimes(phaseTimes))) return UniqueChars(nullptr); @@ -538,17 +538,17 @@ Statistics::formatDetailedDescription() SCC Sweep Total (MaxPause): %.3fms (%.3fms)\n\ HeapSize: %.3f MiB\n\ Chunk Delta (magnitude): %+d (%d)\n\ Arenas Relocated: %.3f MiB\n\ "; char buffer[1024]; SprintfLiteral(buffer, format, ExplainInvocationKind(gckind), - ExplainReason(slices[0].reason), + ExplainReason(slices_[0].reason), nonincremental() ? "no - " : "yes", nonincremental() ? ExplainAbortReason(nonincrementalReason_) : "", zoneStats.collectedZoneCount, zoneStats.zoneCount, zoneStats.sweptZoneCount, zoneStats.collectedCompartmentCount, zoneStats.compartmentCount, zoneStats.sweptCompartmentCount, counts[STAT_MINOR_GC], counts[STAT_STOREBUFFER_OVERFLOW], mmu20 * 100., mmu50 * 100., @@ -576,17 +576,17 @@ Statistics::formatDetailedSliceDescripti Pause: %.3fms of %s budget (@ %.3fms)\n\ "; char buffer[1024]; SprintfLiteral(buffer, format, i, ExplainReason(slice.reason), slice.wasReset() ? "yes - " : "no", slice.wasReset() ? ExplainAbortReason(slice.resetReason) : "", gc::StateName(slice.initialState), gc::StateName(slice.finalState), uint64_t(slice.endFaults - slice.startFaults), - t(slice.duration()), budgetDescription, t(slice.start - slices[0].start)); + t(slice.duration()), budgetDescription, t(slice.start - slices_[0].start)); return DuplicateString(buffer); } UniqueChars Statistics::formatDetailedPhaseTimes(const PhaseTimeTable& phaseTimes) const { static const char* LevelToIndent[] = { "", " ", " ", " " }; static const TimeDuration MaxUnaccountedChildTime = TimeDuration::FromMicroseconds(50); @@ -633,42 +633,62 @@ Statistics::formatDetailedTotals() const Max Pause: %.3fms\n\ "; char buffer[1024]; SprintfLiteral(buffer, format, t(total), t(longest)); return DuplicateString(buffer); } UniqueChars -Statistics::formatJsonMessage(uint64_t timestamp) const +Statistics::formatJsonSlice(size_t sliceNum) const { - MOZ_ASSERT(!aborted); - FragmentVector fragments; if (!fragments.append(DuplicateString("{")) || - !fragments.append(formatJsonDescription(timestamp)) || - !fragments.append(DuplicateString("\"slices\":["))) + !fragments.append(formatJsonSliceDescription(sliceNum, slices_[sliceNum])) || + !fragments.append(DuplicateString("\"times\":{")) || + !fragments.append(formatJsonPhaseTimes(slices_[sliceNum].phaseTimes)) || + !fragments.append(DuplicateString("}}"))) { return UniqueChars(nullptr); } - for (unsigned i = 0; i < slices.length(); i++) { - if (!fragments.append(DuplicateString("{")) || - !fragments.append(formatJsonSliceDescription(i, slices[i])) || - !fragments.append(DuplicateString("\"times\":{")) || - !fragments.append(formatJsonPhaseTimes(slices[i].phaseTimes)) || - !fragments.append(DuplicateString("}}")) || - (i < (slices.length() - 1) && !fragments.append(DuplicateString(",")))) - { - return UniqueChars(nullptr); - } + return Join(fragments); +} + +UniqueChars +Statistics::formatJsonMessage(uint64_t timestamp, bool includeSlices) const +{ + if (aborted) + return DuplicateString("{status:\"aborted\"}"); // May return nullptr + + FragmentVector fragments; + + if (!fragments.append(DuplicateString("{")) || + !fragments.append(formatJsonDescription(timestamp))) + { + return UniqueChars(nullptr); } - if (!fragments.append(DuplicateString("],\"totals\":{")) || + if (includeSlices) { + if (!fragments.append(DuplicateString("\"slices\":["))) + return UniqueChars(nullptr); + + for (unsigned i = 0; i < slices_.length(); i++) { + if (!fragments.append(formatJsonSlice(i))) + return UniqueChars(nullptr); + if ((i < (slices_.length() - 1) && !fragments.append(DuplicateString(",")))) + return UniqueChars(nullptr); + } + + if (!fragments.append(DuplicateString("],"))) + return UniqueChars(nullptr); + } + + if (!fragments.append(DuplicateString("\"totals\":{")) || !fragments.append(formatJsonPhaseTimes(phaseTimes)) || !fragments.append(DuplicateString("}}"))) { return UniqueChars(nullptr); } return Join(fragments); } @@ -698,16 +718,17 @@ Statistics::formatJsonDescription(uint64 const double mmu20 = computeMMU(TimeDuration::FromMilliseconds(20)); const double mmu50 = computeMMU(TimeDuration::FromMilliseconds(50)); const char *format = "\"timestamp\":%llu," "\"max_pause\":%llu.%03llu," "\"total_time\":%llu.%03llu," + "\"reason\":\"%s\"," "\"zones_collected\":%d," "\"total_zones\":%d," "\"total_compartments\":%d," "\"minor_gcs\":%d," "\"store_buffer_overflows\":%d," "\"mmu_20ms\":%d," "\"mmu_50ms\":%d," "\"scc_sweep_total\":%llu.%03llu," @@ -716,16 +737,17 @@ Statistics::formatJsonDescription(uint64 "\"allocated\":%u," "\"added_chunks\":%d," "\"removed_chunks\":%d,"; char buffer[1024]; SprintfLiteral(buffer, format, (unsigned long long)timestamp, longestParts.quot, longestParts.rem, totalParts.quot, totalParts.rem, + ExplainReason(slices_[0].reason), zoneStats.collectedZoneCount, zoneStats.zoneCount, zoneStats.compartmentCount, counts[STAT_MINOR_GC], counts[STAT_STOREBUFFER_OVERFLOW], int(mmu20 * 100), int(mmu50 * 100), sccTotalParts.quot, sccTotalParts.rem, @@ -737,17 +759,17 @@ Statistics::formatJsonDescription(uint64 return DuplicateString(buffer); } UniqueChars Statistics::formatJsonSliceDescription(unsigned i, const SliceData& slice) const { TimeDuration duration = slice.duration(); lldiv_t durationParts = SplitDurationMS(duration); - TimeDuration when = slice.start - slices[0].start; + TimeDuration when = slice.start - slices_[0].start; lldiv_t whenParts = SplitDurationMS(when); char budgetDescription[200]; slice.budget.describe(budgetDescription, sizeof(budgetDescription) - 1); int64_t pageFaults = slice.endFaults - slice.startFaults; TimeStamp originTime = TimeStamp::ProcessCreation(); const char* format = "\"slice\":%d," @@ -1034,17 +1056,17 @@ Statistics::printStats() } } fflush(fp); } void Statistics::beginGC(JSGCInvocationKind kind) { - slices.clearAndFree(); + slices_.clearAndFree(); sccTimes.clearAndFree(); gckind = kind; nonincrementalReason_ = gc::AbortReason::None; preBytes = runtime->gc.usage.gcBytes(); } void @@ -1120,21 +1142,21 @@ Statistics::beginSlice(const ZoneGCStats SliceBudget budget, JS::gcreason::Reason reason) { this->zoneStats = zoneStats; bool first = !runtime->gc.isIncrementalGCInProgress(); if (first) beginGC(gckind); - if (!slices.emplaceBack(budget, - reason, - TimeStamp::Now(), - GetPageFaultCount(), - runtime->gc.state())) + if (!slices_.emplaceBack(budget, + reason, + TimeStamp::Now(), + GetPageFaultCount(), + runtime->gc.state())) { // If we are OOM, set a flag to indicate we have missing slice data. aborted = true; return; } runtime->addTelemetry(JS_TELEMETRY_GC_REASON, reason); @@ -1145,56 +1167,56 @@ Statistics::beginSlice(const ZoneGCStats first ? JS::GC_CYCLE_BEGIN : JS::GC_SLICE_BEGIN, JS::GCDescription(!wasFullGC, gckind, reason)); } void Statistics::endSlice() { if (!aborted) { - slices.back().end = TimeStamp::Now(); - slices.back().endFaults = GetPageFaultCount(); - slices.back().finalState = runtime->gc.state(); + slices_.back().end = TimeStamp::Now(); + slices_.back().endFaults = GetPageFaultCount(); + slices_.back().finalState = runtime->gc.state(); - TimeDuration sliceTime = slices.back().end - slices.back().start; + TimeDuration sliceTime = slices_.back().end - slices_.back().start; runtime->addTelemetry(JS_TELEMETRY_GC_SLICE_MS, t(sliceTime)); - runtime->addTelemetry(JS_TELEMETRY_GC_RESET, slices.back().wasReset()); - if (slices.back().wasReset()) - runtime->addTelemetry(JS_TELEMETRY_GC_RESET_REASON, uint32_t(slices.back().resetReason)); + runtime->addTelemetry(JS_TELEMETRY_GC_RESET, slices_.back().wasReset()); + if (slices_.back().wasReset()) + runtime->addTelemetry(JS_TELEMETRY_GC_RESET_REASON, uint32_t(slices_.back().resetReason)); - if (slices.back().budget.isTimeBudget()) { - int64_t budget_ms = slices.back().budget.timeBudget.budget; + if (slices_.back().budget.isTimeBudget()) { + int64_t budget_ms = slices_.back().budget.timeBudget.budget; runtime->addTelemetry(JS_TELEMETRY_GC_BUDGET_MS, budget_ms); if (budget_ms == runtime->gc.defaultSliceBudget()) runtime->addTelemetry(JS_TELEMETRY_GC_ANIMATION_MS, t(sliceTime)); // Record any phase that goes more than 2x over its budget. if (sliceTime.ToMilliseconds() > 2 * budget_ms) { - Phase longest = LongestPhaseSelfTime(slices.back().phaseTimes); + Phase longest = LongestPhaseSelfTime(slices_.back().phaseTimes); runtime->addTelemetry(JS_TELEMETRY_GC_SLOW_PHASE, phases[longest].telemetryBucket); } } sliceCount_++; } bool last = !runtime->gc.isIncrementalGCInProgress(); if (last) endGC(); - if (enableProfiling_ && !aborted && slices.back().duration() >= profileThreshold_) + if (enableProfiling_ && !aborted && slices_.back().duration() >= profileThreshold_) printSliceProfile(); // Slice callbacks should only fire for the outermost level. if (!aborted) { bool wasFullGC = zoneStats.isCollectingAllZones(); if (sliceCallback) (*sliceCallback)(TlsContext.get(), last ? JS::GC_CYCLE_END : JS::GC_SLICE_END, - JS::GCDescription(!wasFullGC, gckind, slices.back().reason)); + JS::GCDescription(!wasFullGC, gckind, slices_.back().reason)); } // Do this after the slice callback since it uses these values. if (last) { for (auto& count : counts) count = 0; // Clear the timers at the end of a GC because we accumulate time in @@ -1308,18 +1330,18 @@ Statistics::recordPhaseEnd(Phase phase) TimeStamp now = TimeStamp::Now(); if (phase == PHASE_MUTATOR) timedGCStart = now; phaseNestingDepth--; TimeDuration t = now - phaseStartTimes[phase]; - if (!slices.empty()) - slices.back().phaseTimes[activeDagSlot][phase] += t; + if (!slices_.empty()) + slices_.back().phaseTimes[activeDagSlot][phase] += t; phaseTimes[activeDagSlot][phase] += t; phaseStartTimes[phase] = TimeStamp(); } void Statistics::endPhase(Phase phase) { recordPhaseEnd(phase); @@ -1333,18 +1355,18 @@ Statistics::endPhase(Phase phase) resumePhases(); } void Statistics::endParallelPhase(Phase phase, const GCParallelTask* task) { phaseNestingDepth--; - if (!slices.empty()) - slices.back().phaseTimes[PHASE_DAG_NONE][phase] += task->duration(); + if (!slices_.empty()) + slices_.back().phaseTimes[PHASE_DAG_NONE][phase] += task->duration(); phaseTimes[PHASE_DAG_NONE][phase] += task->duration(); phaseStartTimes[phase] = TimeStamp(); } TimeStamp Statistics::beginSCC() { return TimeStamp::Now(); @@ -1366,36 +1388,38 @@ Statistics::endSCC(unsigned scc, TimeSta * 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(TimeDuration window) const { - MOZ_ASSERT(!slices.empty()); + MOZ_ASSERT(!slices_.empty()); - TimeDuration gc = slices[0].end - slices[0].start; + TimeDuration gc = slices_[0].end - slices_[0].start; TimeDuration gcMax = gc; if (gc >= window) return 0.0; int startIndex = 0; - for (size_t endIndex = 1; endIndex < slices.length(); endIndex++) { - gc += slices[endIndex].end - slices[endIndex].start; + for (size_t endIndex = 1; endIndex < slices_.length(); endIndex++) { + auto& startSlice = slices_[startIndex]; + auto& endSlice = slices_[endIndex]; + gc += endSlice.end - endSlice.start; - while (slices[endIndex].end - slices[startIndex].end >= window) { - gc -= slices[startIndex].end - slices[startIndex].start; + while (endSlice.end - startSlice.end >= window) { + gc -= startSlice.end - startSlice.start; startIndex++; } TimeDuration cur = gc; - if (slices[endIndex].end - slices[startIndex].start > window) - cur -= (slices[endIndex].end - slices[startIndex].start - window); + if (endSlice.end - startSlice.start > window) + cur -= (endSlice.end - startSlice.start - window); if (cur > gcMax) gcMax = cur; } return double((window - gcMax) / window); } void @@ -1434,17 +1458,17 @@ Statistics::printProfileTimes(const Prof for (auto time : times) fprintf(stderr, " %6" PRIi64, static_cast<int64_t>(time.ToMilliseconds())); fprintf(stderr, "\n"); } void Statistics::printSliceProfile() { - const SliceData& slice = slices.back(); + const SliceData& slice = slices_.back(); maybePrintProfileHeaders(); fprintf(stderr, "MajorGC: %20s %1d -> %1d ", ExplainReason(slice.reason), int(slice.initialState), int(slice.finalState)); ProfileDurations times; times[ProfileKey::Total] = slice.duration();
--- a/js/src/gc/Statistics.h +++ b/js/src/gc/Statistics.h @@ -240,17 +240,17 @@ struct Statistics // Note when we sweep a zone or compartment. void sweptZone() { ++zoneStats.sweptZoneCount; } void sweptCompartment() { ++zoneStats.sweptCompartmentCount; } void reset(gc::AbortReason reason) { MOZ_ASSERT(reason != gc::AbortReason::None); if (!aborted) - slices.back().resetReason = reason; + slices_.back().resetReason = reason; } void nonincremental(gc::AbortReason reason) { MOZ_ASSERT(reason != gc::AbortReason::None); nonincrementalReason_ = reason; } bool nonincremental() const { @@ -269,17 +269,17 @@ struct Statistics void beginNurseryCollection(JS::gcreason::Reason reason); void endNurseryCollection(JS::gcreason::Reason reason); TimeStamp beginSCC(); void endSCC(unsigned scc, TimeStamp start); UniqueChars formatCompactSliceMessage() const; UniqueChars formatCompactSummaryMessage() const; - UniqueChars formatJsonMessage(uint64_t timestamp) const; + UniqueChars formatJsonMessage(uint64_t timestamp, bool includeSlices = true) const; UniqueChars formatDetailedMessage() const; JS::GCSliceCallback setSliceCallback(JS::GCSliceCallback callback); JS::GCNurseryCollectionCallback setNurseryCollectionCallback( JS::GCNurseryCollectionCallback callback); TimeDuration clearMaxGCPauseAccumulator(); TimeDuration getMaxGCPauseSinceClear(); @@ -314,43 +314,54 @@ struct Statistics size_t startFaults, endFaults; PhaseTimeTable phaseTimes; TimeDuration duration() const { return end - start; } bool wasReset() const { return resetReason != gc::AbortReason::None; } }; typedef Vector<SliceData, 8, SystemAllocPolicy> SliceDataVector; - typedef SliceDataVector::ConstRange SliceRange; + + const SliceDataVector& slices() const { return slices_; } - SliceRange sliceRange() const { return slices.all(); } - size_t slicesLength() const { return slices.length(); } + TimeStamp start() const { + MOZ_ASSERT(phaseStartTimes[PHASE_GC_BEGIN]); + return slices_[0].start; + } + + TimeStamp end() const { + MOZ_ASSERT(phaseStartTimes[PHASE_GC_BEGIN]); + return slices_.back().end; + } // Occasionally print header lines for profiling information. void maybePrintProfileHeaders(); // Print header line for profile times. void printProfileHeader(); // Print total profile times on shutdown. void printTotalProfileTimes(); + // Return JSON for the timings of just the given slice. + UniqueChars formatJsonSlice(size_t sliceNum) const; + private: JSRuntime* runtime; /* File pointer used for MOZ_GCTIMER output. */ FILE* fp; ZoneGCStats zoneStats; JSGCInvocationKind gckind; gc::AbortReason nonincrementalReason_; - SliceDataVector slices; + SliceDataVector slices_; /* Most recent time when the given phase started. */ EnumeratedArray<Phase, PHASE_LIMIT, TimeStamp> phaseStartTimes; /* Bookkeeping for GC timings when timingMutator is true */ TimeStamp timedGCStart; TimeDuration timedGCTime;
--- a/js/src/jsgc.cpp +++ b/js/src/jsgc.cpp @@ -252,16 +252,17 @@ using namespace js; using namespace js::gc; using mozilla::ArrayLength; using mozilla::Get; using mozilla::HashCodeScrambler; using mozilla::Maybe; using mozilla::Swap; +using mozilla::TimeStamp; using JS::AutoGCRooter; /* Increase the IGC marking slice time if we are in highFrequencyGC mode. */ static const int IGC_MARK_SLICE_MULTIPLIER = 2; const AllocKind gc::slotsToThingKind[] = { /* 0 */ AllocKind::OBJECT0, AllocKind::OBJECT2, AllocKind::OBJECT2, AllocKind::OBJECT4, @@ -7621,16 +7622,54 @@ JS::GCDescription::formatJSON(JSContext* if (!out) return nullptr; out.get()[nchars] = 0; CopyAndInflateChars(out.get(), cstr.get(), nchars); return out.release(); } +TimeStamp +JS::GCDescription::startTime(JSContext* cx) const +{ + return cx->runtime()->gc.stats().start(); +} + +TimeStamp +JS::GCDescription::endTime(JSContext* cx) const +{ + return cx->runtime()->gc.stats().end(); +} + +TimeStamp +JS::GCDescription::lastSliceStart(JSContext* cx) const +{ + return cx->runtime()->gc.stats().slices().back().start; +} + +TimeStamp +JS::GCDescription::lastSliceEnd(JSContext* cx) const +{ + return cx->runtime()->gc.stats().slices().back().end; +} + +JS::UniqueChars +JS::GCDescription::sliceToJSON(JSContext* cx) const +{ + size_t slices = cx->runtime()->gc.stats().slices().length(); + MOZ_ASSERT(slices > 0); + return cx->runtime()->gc.stats().formatJsonSlice(slices - 1); +} + +JS::UniqueChars +JS::GCDescription::summaryToJSON(JSContext* cx) const +{ + return cx->runtime()->gc.stats().formatJsonMessage(0, false); +} + JS_PUBLIC_API(JS::GCSliceCallback) JS::SetGCSliceCallback(JSContext* cx, GCSliceCallback callback) { return cx->runtime()->gc.setSliceCallback(callback); } JS_PUBLIC_API(JS::DoCycleCollectionCallback) JS::SetDoCycleCollectionCallback(JSContext* cx, JS::DoCycleCollectionCallback callback)
--- a/js/src/vm/Debugger.cpp +++ b/js/src/vm/Debugger.cpp @@ -11734,31 +11734,31 @@ namespace dbg { GarbageCollectionEvent::Create(JSRuntime* rt, ::js::gcstats::Statistics& stats, uint64_t gcNumber) { auto data = rt->make_unique<GarbageCollectionEvent>(gcNumber); if (!data) return nullptr; data->nonincrementalReason = stats.nonincrementalReason(); - for (auto range = stats.sliceRange(); !range.empty(); range.popFront()) { + for (auto& slice : stats.slices()) { if (!data->reason) { // There is only one GC reason for the whole cycle, but for legacy // reasons this data is stored and replicated on each slice. Each // slice used to have its own GCReason, but now they are all the // same. - data->reason = gcreason::ExplainReason(range.front().reason); + data->reason = gcreason::ExplainReason(slice.reason); MOZ_ASSERT(data->reason); } if (!data->collections.growBy(1)) return nullptr; - data->collections.back().startTimestamp = range.front().start; - data->collections.back().endTimestamp = range.front().end; + data->collections.back().startTimestamp = slice.start; + data->collections.back().endTimestamp = slice.end; } return data; } static bool DefineStringProperty(JSContext* cx, HandleObject obj, PropertyName* propName, const char* strVal) {