michael@0: /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- michael@0: * vim: set ts=8 sts=4 et sw=4 tw=99: michael@0: * This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #ifndef gc_Statistics_h michael@0: #define gc_Statistics_h michael@0: michael@0: #include "mozilla/DebugOnly.h" michael@0: #include "mozilla/PodOperations.h" michael@0: michael@0: #include "jsalloc.h" michael@0: #include "jspubtd.h" michael@0: michael@0: #include "js/GCAPI.h" michael@0: #include "js/Vector.h" michael@0: michael@0: struct JSCompartment; michael@0: michael@0: namespace js { michael@0: namespace gcstats { michael@0: michael@0: enum Phase { michael@0: PHASE_GC_BEGIN, michael@0: PHASE_WAIT_BACKGROUND_THREAD, michael@0: PHASE_MARK_DISCARD_CODE, michael@0: PHASE_PURGE, michael@0: PHASE_MARK, michael@0: PHASE_MARK_ROOTS, michael@0: PHASE_MARK_DELAYED, michael@0: PHASE_SWEEP, michael@0: PHASE_SWEEP_MARK, michael@0: PHASE_SWEEP_MARK_TYPES, michael@0: PHASE_SWEEP_MARK_INCOMING_BLACK, michael@0: PHASE_SWEEP_MARK_WEAK, michael@0: PHASE_SWEEP_MARK_INCOMING_GRAY, michael@0: PHASE_SWEEP_MARK_GRAY, michael@0: PHASE_SWEEP_MARK_GRAY_WEAK, michael@0: PHASE_FINALIZE_START, michael@0: PHASE_SWEEP_ATOMS, michael@0: PHASE_SWEEP_COMPARTMENTS, michael@0: PHASE_SWEEP_DISCARD_CODE, michael@0: PHASE_SWEEP_TABLES, michael@0: PHASE_SWEEP_TABLES_WRAPPER, michael@0: PHASE_SWEEP_TABLES_BASE_SHAPE, michael@0: PHASE_SWEEP_TABLES_INITIAL_SHAPE, michael@0: PHASE_SWEEP_TABLES_TYPE_OBJECT, michael@0: PHASE_SWEEP_TABLES_BREAKPOINT, michael@0: PHASE_SWEEP_TABLES_REGEXP, michael@0: PHASE_DISCARD_ANALYSIS, michael@0: PHASE_DISCARD_TI, michael@0: PHASE_FREE_TI_ARENA, michael@0: PHASE_SWEEP_TYPES, michael@0: PHASE_SWEEP_OBJECT, michael@0: PHASE_SWEEP_STRING, michael@0: PHASE_SWEEP_SCRIPT, michael@0: PHASE_SWEEP_SHAPE, michael@0: PHASE_SWEEP_JITCODE, michael@0: PHASE_FINALIZE_END, michael@0: PHASE_DESTROY, michael@0: PHASE_GC_END, michael@0: michael@0: PHASE_LIMIT michael@0: }; michael@0: michael@0: enum Stat { michael@0: STAT_NEW_CHUNK, michael@0: STAT_DESTROY_CHUNK, michael@0: STAT_MINOR_GC, michael@0: michael@0: STAT_LIMIT michael@0: }; michael@0: michael@0: class StatisticsSerializer; michael@0: michael@0: struct Statistics { michael@0: Statistics(JSRuntime *rt); michael@0: ~Statistics(); michael@0: michael@0: void beginPhase(Phase phase); michael@0: void endPhase(Phase phase); michael@0: michael@0: void beginSlice(int collectedCount, int zoneCount, int compartmentCount, JS::gcreason::Reason reason); michael@0: void endSlice(); michael@0: michael@0: void reset(const char *reason) { slices.back().resetReason = reason; } michael@0: void nonincremental(const char *reason) { nonincrementalReason = reason; } michael@0: michael@0: void count(Stat s) { michael@0: JS_ASSERT(s < STAT_LIMIT); michael@0: counts[s]++; michael@0: } michael@0: michael@0: int64_t beginSCC(); michael@0: void endSCC(unsigned scc, int64_t start); michael@0: michael@0: jschar *formatMessage(); michael@0: jschar *formatJSON(uint64_t timestamp); michael@0: michael@0: private: michael@0: JSRuntime *runtime; michael@0: michael@0: int64_t startupTime; michael@0: michael@0: FILE *fp; michael@0: bool fullFormat; michael@0: michael@0: /* michael@0: * GCs can't really nest, but a second GC can be triggered from within the michael@0: * JSGC_END callback. michael@0: */ michael@0: int gcDepth; michael@0: michael@0: int collectedCount; michael@0: int zoneCount; michael@0: int compartmentCount; michael@0: const char *nonincrementalReason; michael@0: michael@0: struct SliceData { michael@0: SliceData(JS::gcreason::Reason reason, int64_t start, size_t startFaults) michael@0: : reason(reason), resetReason(nullptr), start(start), startFaults(startFaults) michael@0: { michael@0: mozilla::PodArrayZero(phaseTimes); michael@0: } michael@0: michael@0: JS::gcreason::Reason reason; michael@0: const char *resetReason; michael@0: int64_t start, end; michael@0: size_t startFaults, endFaults; michael@0: int64_t phaseTimes[PHASE_LIMIT]; michael@0: michael@0: int64_t duration() const { return end - start; } michael@0: }; michael@0: michael@0: Vector slices; michael@0: michael@0: /* Most recent time when the given phase started. */ michael@0: int64_t phaseStartTimes[PHASE_LIMIT]; michael@0: michael@0: /* Total time in a given phase for this GC. */ michael@0: int64_t phaseTimes[PHASE_LIMIT]; michael@0: michael@0: /* Total time in a given phase over all GCs. */ michael@0: int64_t phaseTotals[PHASE_LIMIT]; michael@0: michael@0: /* Number of events of this type for this GC. */ michael@0: unsigned int counts[STAT_LIMIT]; michael@0: michael@0: /* Allocated space before the GC started. */ michael@0: size_t preBytes; michael@0: michael@0: #ifdef DEBUG michael@0: /* Phases that are currently on stack. */ michael@0: static const size_t MAX_NESTING = 8; michael@0: Phase phaseNesting[MAX_NESTING]; michael@0: #endif michael@0: mozilla::DebugOnly phaseNestingDepth; michael@0: michael@0: /* Sweep times for SCCs of compartments. */ michael@0: Vector sccTimes; michael@0: michael@0: void beginGC(); michael@0: void endGC(); michael@0: michael@0: void gcDuration(int64_t *total, int64_t *maxPause); michael@0: void sccDurations(int64_t *total, int64_t *maxPause); michael@0: void printStats(); michael@0: bool formatData(StatisticsSerializer &ss, uint64_t timestamp); michael@0: michael@0: double computeMMU(int64_t resolution); michael@0: }; michael@0: michael@0: struct AutoGCSlice michael@0: { michael@0: AutoGCSlice(Statistics &stats, int collectedCount, int zoneCount, int compartmentCount, michael@0: JS::gcreason::Reason reason michael@0: MOZ_GUARD_OBJECT_NOTIFIER_PARAM) michael@0: : stats(stats) michael@0: { michael@0: MOZ_GUARD_OBJECT_NOTIFIER_INIT; michael@0: stats.beginSlice(collectedCount, zoneCount, compartmentCount, reason); michael@0: } michael@0: ~AutoGCSlice() { stats.endSlice(); } michael@0: michael@0: Statistics &stats; michael@0: MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER michael@0: }; michael@0: michael@0: struct AutoPhase michael@0: { michael@0: AutoPhase(Statistics &stats, Phase phase michael@0: MOZ_GUARD_OBJECT_NOTIFIER_PARAM) michael@0: : stats(stats), phase(phase) michael@0: { michael@0: MOZ_GUARD_OBJECT_NOTIFIER_INIT; michael@0: stats.beginPhase(phase); michael@0: } michael@0: ~AutoPhase() { michael@0: stats.endPhase(phase); michael@0: } michael@0: michael@0: Statistics &stats; michael@0: Phase phase; michael@0: MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER michael@0: }; michael@0: michael@0: struct MaybeAutoPhase michael@0: { michael@0: MaybeAutoPhase(MOZ_GUARD_OBJECT_NOTIFIER_ONLY_PARAM) michael@0: : stats(nullptr) michael@0: { michael@0: MOZ_GUARD_OBJECT_NOTIFIER_INIT; michael@0: } michael@0: void construct(Statistics &statsArg, Phase phaseArg) michael@0: { michael@0: JS_ASSERT(!stats); michael@0: stats = &statsArg; michael@0: phase = phaseArg; michael@0: stats->beginPhase(phase); michael@0: } michael@0: ~MaybeAutoPhase() { michael@0: if (stats) michael@0: stats->endPhase(phase); michael@0: } michael@0: michael@0: Statistics *stats; michael@0: Phase phase; michael@0: MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER michael@0: }; michael@0: michael@0: struct AutoSCC michael@0: { michael@0: AutoSCC(Statistics &stats, unsigned scc michael@0: MOZ_GUARD_OBJECT_NOTIFIER_PARAM) michael@0: : stats(stats), scc(scc) michael@0: { michael@0: MOZ_GUARD_OBJECT_NOTIFIER_INIT; michael@0: start = stats.beginSCC(); michael@0: } michael@0: ~AutoSCC() { michael@0: stats.endSCC(scc, start); michael@0: } michael@0: michael@0: Statistics &stats; michael@0: unsigned scc; michael@0: int64_t start; michael@0: MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER michael@0: }; michael@0: michael@0: const char *ExplainReason(JS::gcreason::Reason reason); michael@0: michael@0: } /* namespace gcstats */ michael@0: } /* namespace js */ michael@0: michael@0: #endif /* gc_Statistics_h */