From 76ac85af0541450615eacd6df3d17740c4c0ea8d Mon Sep 17 00:00:00 2001 From: Bill McCloskey Date: Sun, 8 Jul 2012 10:24:32 -0700 Subject: [PATCH] Bug 771720 - Improve GC data collection (r=terrence) --- js/src/gc/Statistics.cpp | 37 +++++++++++------------------ js/src/gc/Statistics.h | 19 ++++++++------- js/src/jscompartment.cpp | 50 +++++++++++++++++++++++++--------------- js/src/jsgc.cpp | 44 +++++++++++++++++++++++------------ 4 files changed, 85 insertions(+), 65 deletions(-) diff --git a/js/src/gc/Statistics.cpp b/js/src/gc/Statistics.cpp index 277a2744522..ab3cf05d88b 100644 --- a/js/src/gc/Statistics.cpp +++ b/js/src/gc/Statistics.cpp @@ -276,20 +276,26 @@ static PhaseInfo phases[] = { { PHASE_WAIT_BACKGROUND_THREAD, "Wait Background Thread" }, { PHASE_PURGE, "Purge" }, { PHASE_MARK, "Mark" }, + { PHASE_MARK_DISCARD_CODE, "Mark Discard Code" }, { PHASE_MARK_ROOTS, "Mark Roots" }, { PHASE_MARK_TYPES, "Mark Types" }, { PHASE_MARK_DELAYED, "Mark Delayed" }, - { PHASE_MARK_OTHER, "Mark Other" }, + { PHASE_MARK_WEAK, "Mark Weak" }, + { PHASE_MARK_GRAY, "Mark Gray" }, + { PHASE_MARK_GRAY_WEAK, "Mark Gray and Weak" }, { PHASE_FINALIZE_START, "Finalize Start Callback" }, { PHASE_SWEEP, "Sweep" }, + { PHASE_SWEEP_ATOMS, "Sweep Atoms" }, { PHASE_SWEEP_COMPARTMENTS, "Sweep Compartments" }, + { PHASE_SWEEP_TABLES, "Sweep Tables" }, { PHASE_SWEEP_OBJECT, "Sweep Object" }, { PHASE_SWEEP_STRING, "Sweep String" }, { PHASE_SWEEP_SCRIPT, "Sweep Script" }, { PHASE_SWEEP_SHAPE, "Sweep Shape" }, - { PHASE_DISCARD_CODE, "Discard Code" }, + { PHASE_SWEEP_DISCARD_CODE, "Sweep Discard Code" }, { PHASE_DISCARD_ANALYSIS, "Discard Analysis" }, { PHASE_DISCARD_TI, "Discard TI" }, + { PHASE_FREE_TI_ARENA, "Free TI Arena" }, { PHASE_SWEEP_TYPES, "Sweep Types" }, { PHASE_CLEAR_SCRIPT_ANALYSIS, "Clear Script Analysis" }, { PHASE_FINALIZE_END, "Finalize End Callback" }, @@ -307,15 +313,6 @@ FormatPhaseTimes(StatisticsSerializer &ss, const char *name, int64_t *times) ss.endObject(); } -static void -FormatPhaseFaults(StatisticsSerializer &ss, const char *name, size_t *faults) -{ - ss.beginObject(name); - for (unsigned i = 0; phases[i].name; i++) - ss.appendNumber(phases[i].name, "%u", "", unsigned(faults[phases[i].index])); - ss.endObject(); -} - bool Statistics::formatData(StatisticsSerializer &ss, uint64_t timestamp) { @@ -367,12 +364,14 @@ Statistics::formatData(StatisticsSerializer &ss, uint64_t timestamp) ss.extra(" ("); ss.appendDecimal("When", "ms", t(slices[i].start - slices[0].start)); ss.appendString("Reason", ExplainReason(slices[i].reason)); + if (ss.isJSON()) { + ss.appendDecimal("Page Faults", "", + double(slices[i].endFaults - slices[i].startFaults)); + } if (slices[i].resetReason) ss.appendString("Reset", slices[i].resetReason); ss.extra("): "); FormatPhaseTimes(ss, "Times", slices[i].phaseTimes); - if (ss.isJSON()) - FormatPhaseFaults(ss, "Page Faults", slices[i].phaseFaults); ss.endLine(); ss.endObject(); } @@ -380,8 +379,6 @@ Statistics::formatData(StatisticsSerializer &ss, uint64_t timestamp) } ss.extra(" Totals: "); FormatPhaseTimes(ss, "Totals", phaseTimes); - if (ss.isJSON()) - FormatPhaseFaults(ss, "Total Page Faults", phaseFaults); ss.endObject(); return !ss.isOOM(); @@ -484,9 +481,7 @@ void Statistics::beginGC() { PodArrayZero(phaseStartTimes); - PodArrayZero(phaseStartFaults); PodArrayZero(phaseTimes); - PodArrayZero(phaseFaults); slices.clearAndFree(); nonincrementalReason = NULL; @@ -531,7 +526,7 @@ Statistics::beginSlice(int collectedCount, int compartmentCount, gcreason::Reaso if (first) beginGC(); - SliceData data(reason, PRMJ_Now()); + SliceData data(reason, PRMJ_Now(), gc::GetPageFaultCount()); (void) slices.append(data); /* Ignore any OOMs here. */ if (JSAccumulateTelemetryDataCallback cb = runtime->telemetryCallback) @@ -549,6 +544,7 @@ void Statistics::endSlice() { slices.back().end = PRMJ_Now(); + slices.back().endFaults = gc::GetPageFaultCount(); if (JSAccumulateTelemetryDataCallback cb = runtime->telemetryCallback) { (*cb)(JS_TELEMETRY_GC_SLICE_MS, t(slices.back().end - slices.back().start)); @@ -578,7 +574,6 @@ Statistics::beginPhase(Phase phase) JS_ASSERT(!phaseStartTimes[phase]); phaseStartTimes[phase] = PRMJ_Now(); - phaseStartFaults[phase] = gc::GetPageFaultCount(); if (phase == gcstats::PHASE_MARK) Probes::GCStartMarkPhase(); @@ -594,10 +589,6 @@ Statistics::endPhase(Phase phase) phaseTimes[phase] += t; phaseStartTimes[phase] = 0; - size_t faults = gc::GetPageFaultCount() - phaseStartFaults[phase]; - slices.back().phaseFaults[phase] += faults; - phaseFaults[phase] += faults; - if (phase == gcstats::PHASE_MARK) Probes::GCEndMarkPhase(); else if (phase == gcstats::PHASE_SWEEP) diff --git a/js/src/gc/Statistics.h b/js/src/gc/Statistics.h index 03d70a97a30..5b4e355f0bb 100644 --- a/js/src/gc/Statistics.h +++ b/js/src/gc/Statistics.h @@ -24,20 +24,26 @@ enum Phase { PHASE_WAIT_BACKGROUND_THREAD, PHASE_PURGE, PHASE_MARK, + PHASE_MARK_DISCARD_CODE, PHASE_MARK_ROOTS, PHASE_MARK_TYPES, PHASE_MARK_DELAYED, - PHASE_MARK_OTHER, + PHASE_MARK_WEAK, + PHASE_MARK_GRAY, + PHASE_MARK_GRAY_WEAK, PHASE_FINALIZE_START, PHASE_SWEEP, + PHASE_SWEEP_ATOMS, PHASE_SWEEP_COMPARTMENTS, + PHASE_SWEEP_TABLES, PHASE_SWEEP_OBJECT, PHASE_SWEEP_STRING, PHASE_SWEEP_SCRIPT, PHASE_SWEEP_SHAPE, - PHASE_DISCARD_CODE, + PHASE_SWEEP_DISCARD_CODE, PHASE_DISCARD_ANALYSIS, PHASE_DISCARD_TI, + PHASE_FREE_TI_ARENA, PHASE_SWEEP_TYPES, PHASE_CLEAR_SCRIPT_ANALYSIS, PHASE_FINALIZE_END, @@ -96,18 +102,17 @@ struct Statistics { const char *nonincrementalReason; struct SliceData { - SliceData(gcreason::Reason reason, int64_t start) - : reason(reason), resetReason(NULL), start(start) + SliceData(gcreason::Reason reason, int64_t start, size_t startFaults) + : reason(reason), resetReason(NULL), start(start), startFaults(startFaults) { PodArrayZero(phaseTimes); - PodArrayZero(phaseFaults); } gcreason::Reason reason; const char *resetReason; int64_t start, end; + size_t startFaults, endFaults; int64_t phaseTimes[PHASE_LIMIT]; - size_t phaseFaults[PHASE_LIMIT]; int64_t duration() const { return end - start; } }; @@ -116,11 +121,9 @@ struct Statistics { /* Most recent time when the given phase started. */ int64_t phaseStartTimes[PHASE_LIMIT]; - size_t phaseStartFaults[PHASE_LIMIT]; /* Total time in a given phase for this GC. */ int64_t phaseTimes[PHASE_LIMIT]; - size_t phaseFaults[PHASE_LIMIT]; /* Total time in a given phase over all GCs. */ int64_t phaseTotals[PHASE_LIMIT]; diff --git a/js/src/jscompartment.cpp b/js/src/jscompartment.cpp index f6cb31cff5b..6f8752f5905 100644 --- a/js/src/jscompartment.cpp +++ b/js/src/jscompartment.cpp @@ -472,30 +472,35 @@ JSCompartment::discardJitCode(FreeOp *fop) void JSCompartment::sweep(FreeOp *fop, bool releaseTypes) { - sweepCrossCompartmentWrappers(); - - /* Remove dead references held weakly by the compartment. */ - - sweepBaseShapeTable(); - sweepInitialShapeTable(); - sweepNewTypeObjectTable(newTypeObjects); - sweepNewTypeObjectTable(lazyTypeObjects); - - if (emptyTypeObject && !IsTypeObjectMarked(emptyTypeObject.unsafeGet())) - emptyTypeObject = NULL; - - sweepBreakpoints(fop); - { - gcstats::AutoPhase ap(rt->gcStats, gcstats::PHASE_DISCARD_CODE); + gcstats::AutoPhase ap(rt->gcStats, gcstats::PHASE_SWEEP_DISCARD_CODE); discardJitCode(fop); } - if (global_ && !IsObjectMarked(&global_)) - global_ = NULL; + /* This function includes itself in PHASE_SWEEP_TABLES. */ + sweepCrossCompartmentWrappers(); - /* JIT code can hold references on RegExpShared, so sweep regexps after clearing code. */ - regExps.sweep(rt); + { + gcstats::AutoPhase ap(rt->gcStats, gcstats::PHASE_SWEEP_TABLES); + + /* Remove dead references held weakly by the compartment. */ + + sweepBaseShapeTable(); + sweepInitialShapeTable(); + sweepNewTypeObjectTable(newTypeObjects); + sweepNewTypeObjectTable(lazyTypeObjects); + + if (emptyTypeObject && !IsTypeObjectMarked(emptyTypeObject.unsafeGet())) + emptyTypeObject = NULL; + + sweepBreakpoints(fop); + + if (global_ && !IsObjectMarked(&global_)) + global_ = NULL; + + /* JIT code can hold references on RegExpShared, so sweep regexps after clearing code. */ + regExps.sweep(rt); + } if (!activeAnalysis && !gcPreserveCode) { gcstats::AutoPhase ap(rt->gcStats, gcstats::PHASE_DISCARD_ANALYSIS); @@ -548,6 +553,11 @@ JSCompartment::sweep(FreeOp *fop, bool releaseTypes) script->clearAnalysis(); } } + + { + gcstats::AutoPhase ap2(rt->gcStats, gcstats::PHASE_FREE_TI_ARENA); + oldAlloc.freeAll(); + } } active = false; @@ -561,6 +571,8 @@ JSCompartment::sweep(FreeOp *fop, bool releaseTypes) void JSCompartment::sweepCrossCompartmentWrappers() { + gcstats::AutoPhase ap(rt->gcStats, gcstats::PHASE_SWEEP_TABLES); + /* Remove dead wrappers from the table. */ for (WrapperMap::Enum e(crossCompartmentWrappers); !e.empty(); e.popFront()) { CrossCompartmentKey key = e.front().key; diff --git a/js/src/jsgc.cpp b/js/src/jsgc.cpp index 59a4ccf0147..030d5b59c64 100644 --- a/js/src/jsgc.cpp +++ b/js/src/jsgc.cpp @@ -3052,7 +3052,7 @@ BeginMarkPhase(JSRuntime *rt, bool isIncremental) /* For non-incremental GC the following sweep discards the jit code. */ if (isIncremental) { for (GCCompartmentsIter c(rt); !c.done(); c.next()) { - gcstats::AutoPhase ap(rt->gcStats, gcstats::PHASE_DISCARD_CODE); + gcstats::AutoPhase ap(rt->gcStats, gcstats::PHASE_MARK_DISCARD_CODE); c->discardJitCode(rt->defaultFreeOp()); } } @@ -3110,19 +3110,30 @@ MarkGrayAndWeak(JSRuntime *rt) { GCMarker *gcmarker = &rt->gcMarker; - JS_ASSERT(gcmarker->isDrained()); - MarkWeakReferences(gcmarker); - - gcmarker->setMarkColorGray(); - if (gcmarker->hasBufferedGrayRoots()) { - gcmarker->markBufferedGrayRoots(); - } else { - if (JSTraceDataOp op = rt->gcGrayRootsTraceOp) - (*op)(gcmarker, rt->gcGrayRootsData); + { + gcstats::AutoPhase ap(rt->gcStats, gcstats::PHASE_MARK_WEAK); + JS_ASSERT(gcmarker->isDrained()); + MarkWeakReferences(gcmarker); } - SliceBudget budget; - gcmarker->drainMarkStack(budget); - MarkWeakReferences(gcmarker); + + { + gcstats::AutoPhase ap(rt->gcStats, gcstats::PHASE_MARK_GRAY); + gcmarker->setMarkColorGray(); + if (gcmarker->hasBufferedGrayRoots()) { + gcmarker->markBufferedGrayRoots(); + } else { + if (JSTraceDataOp op = rt->gcGrayRootsTraceOp) + (*op)(gcmarker, rt->gcGrayRootsData); + } + SliceBudget budget; + gcmarker->drainMarkStack(budget); + } + + { + gcstats::AutoPhase ap(rt->gcStats, gcstats::PHASE_MARK_GRAY_WEAK); + MarkWeakReferences(gcmarker); + } + JS_ASSERT(gcmarker->isDrained()); } @@ -3136,7 +3147,6 @@ EndMarkPhase(JSRuntime *rt, bool isIncremental) { { gcstats::AutoPhase ap1(rt->gcStats, gcstats::PHASE_MARK); - gcstats::AutoPhase ap2(rt->gcStats, gcstats::PHASE_MARK_OTHER); MarkGrayAndWeak(rt); } @@ -3315,6 +3325,7 @@ SweepPhase(JSRuntime *rt, JSGCInvocationKind gckind, bool *startBackgroundSweep) c->arenas.purge(); FreeOp fop(rt, *startBackgroundSweep, false); + { gcstats::AutoPhase ap(rt->gcStats, gcstats::PHASE_FINALIZE_START); if (rt->gcFinalizeCallback) @@ -3325,7 +3336,10 @@ SweepPhase(JSRuntime *rt, JSGCInvocationKind gckind, bool *startBackgroundSweep) WeakMapBase::sweepAll(&rt->gcMarker); rt->debugScopes->sweep(); - SweepAtomState(rt); + { + gcstats::AutoPhase ap2(rt->gcStats, gcstats::PHASE_SWEEP_ATOMS); + SweepAtomState(rt); + } /* Collect watch points associated with unreachable objects. */ WatchpointMap::sweepAll(rt);