From 6dfb423c22f3212c0b67157f44b75001dd3187d4 Mon Sep 17 00:00:00 2001 From: Bill McCloskey Date: Mon, 9 Apr 2012 12:30:33 -0700 Subject: [PATCH] Bug 745034 - Add page fault counts to GC statistics (r=terrence,dmandelin) --- js/src/configure.in | 4 +- js/src/gc/Memory.cpp | 34 +++++++++++++ js/src/gc/Memory.h | 3 ++ js/src/gc/Statistics.cpp | 100 ++++++++++++++++++++++++++------------- js/src/gc/Statistics.h | 6 ++- 5 files changed, 110 insertions(+), 37 deletions(-) diff --git a/js/src/configure.in b/js/src/configure.in index df0af872d36..500b94a0d79 100644 --- a/js/src/configure.in +++ b/js/src/configure.in @@ -2165,7 +2165,7 @@ ia64*-hpux*) # Use temp file for windres (bug 213281) RCFLAGS='-O coff --use-temp-file' # mingw doesn't require kernel32, user32, and advapi32 explicitly - LIBS="$LIBS -lgdi32 -lwinmm -lwsock32" + LIBS="$LIBS -lgdi32 -lwinmm -lwsock32 -lpsapi" MOZ_JS_LIBS='-L$(libdir) -lmozjs' MOZ_FIX_LINK_PATHS= DYNAMIC_XPCOM_LIBS='-L$(LIBXUL_DIST)/lib -lxpcom -lxpcom_core -lmozalloc' @@ -2210,7 +2210,7 @@ ia64*-hpux*) # make 'foo == bar;' error out CFLAGS="$CFLAGS -we4553" CXXFLAGS="$CXXFLAGS -we4553" - LIBS="$LIBS kernel32.lib user32.lib gdi32.lib winmm.lib wsock32.lib advapi32.lib" + LIBS="$LIBS kernel32.lib user32.lib gdi32.lib winmm.lib wsock32.lib advapi32.lib psapi.lib" MOZ_DEBUG_FLAGS='-Zi' MOZ_DEBUG_LDFLAGS='-DEBUG -DEBUGTYPE:CV' WARNINGS_AS_ERRORS='-WX' diff --git a/js/src/gc/Memory.cpp b/js/src/gc/Memory.cpp index 835320037ce..815e90b781e 100644 --- a/js/src/gc/Memory.cpp +++ b/js/src/gc/Memory.cpp @@ -15,6 +15,7 @@ namespace gc { #if defined(XP_WIN) #include "jswin.h" +#include static size_t AllocationGranularity = 0; @@ -91,6 +92,15 @@ MarkPagesInUse(void *p, size_t size) return true; } +size_t +GetPageFaultCount() +{ + PROCESS_MEMORY_COUNTERS pmc; + if (!GetProcessMemoryInfo(GetCurrentProcess(), &pmc, sizeof(pmc))) + return 0; + return pmc.PageFaultCount; +} + #elif defined(XP_OS2) #define INCL_DOSMEMMGR @@ -216,6 +226,12 @@ MarkPagesInUse(void *p, size_t size) return true; } +size_t +GetPageFaultCount() +{ + return 0; +} + #elif defined(SOLARIS) #include @@ -267,10 +283,18 @@ MarkPagesInUse(void *p, size_t size) return true; } +size_t +GetPageFaultCount() +{ + return 0; +} + #elif defined(XP_UNIX) || defined(XP_MACOSX) || defined(DARWIN) #include #include +#include +#include void InitMemorySubsystem() @@ -336,6 +360,16 @@ MarkPagesInUse(void *p, size_t size) return true; } +size_t +GetPageFaultCount() +{ + struct rusage usage; + int err = getrusage(RUSAGE_SELF, &usage); + if (err) + return 0; + return usage.ru_minflt + usage.ru_majflt; +} + #else #error "Memory mapping functions are not defined for your OS." #endif diff --git a/js/src/gc/Memory.h b/js/src/gc/Memory.h index 3d24b2ac507..a096dca0420 100644 --- a/js/src/gc/Memory.h +++ b/js/src/gc/Memory.h @@ -31,6 +31,9 @@ bool MarkPagesUnused(void *p, size_t size); // platforms. bool MarkPagesInUse(void *p, size_t size); +// Returns #(hard faults) + #(soft faults) +size_t GetPageFaultCount(); + } /* namespace gc */ } /* namespace js */ diff --git a/js/src/gc/Statistics.cpp b/js/src/gc/Statistics.cpp index 7d72ad625b3..ec71f6452d4 100644 --- a/js/src/gc/Statistics.cpp +++ b/js/src/gc/Statistics.cpp @@ -49,6 +49,7 @@ #include "jsutil.h" #include "prmjtime.h" +#include "gc/Memory.h" #include "gc/Statistics.h" #include "gc/Barrier-inl.h" @@ -124,7 +125,7 @@ class StatisticsSerializer if (needComma_) pJSON(", "); if (asJSON_ && name) { - putQuoted(name); + putKey(name); pJSON(": "); } pJSON("{"); @@ -141,7 +142,7 @@ class StatisticsSerializer if (needComma_) pJSON(", "); if (asJSON_) - putQuoted(name); + putKey(name); pJSON(": ["); needComma_ = false; } @@ -289,32 +290,53 @@ t(int64_t t) return double(t) / PRMJ_USEC_PER_MSEC; } +struct PhaseInfo +{ + unsigned index; + const char *name; +}; + +static PhaseInfo phases[] = { + { PHASE_GC_BEGIN, "Begin Callback" }, + { PHASE_WAIT_BACKGROUND_THREAD, "Wait Background Thread" }, + { PHASE_PURGE, "Purge" }, + { PHASE_MARK, "Mark" }, + { PHASE_MARK_ROOTS, "Mark Roots" }, + { PHASE_MARK_DELAYED, "Mark Delayed" }, + { PHASE_MARK_OTHER, "Mark Other" }, + { PHASE_FINALIZE_START, "Finalize Start Callback" }, + { PHASE_SWEEP, "Sweep" }, + { PHASE_SWEEP_COMPARTMENTS, "Sweep Compartments" }, + { 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_DISCARD_ANALYSIS, "Discard Analysis" }, + { PHASE_DISCARD_TI, "Discard TI" }, + { PHASE_SWEEP_TYPES, "Sweep Types" }, + { PHASE_CLEAR_SCRIPT_ANALYSIS, "Clear Script Analysis" }, + { PHASE_FINALIZE_END, "Finalize End Callback" }, + { PHASE_DESTROY, "Deallocate" }, + { PHASE_GC_END, "End Callback" }, + { 0, NULL } +}; + static void -formatPhases(StatisticsSerializer &ss, const char *name, int64_t *times) +FormatPhaseTimes(StatisticsSerializer &ss, const char *name, int64_t *times) { ss.beginObject(name); - ss.appendIfNonzeroMS("Begin Callback", t(times[PHASE_GC_BEGIN])); - ss.appendIfNonzeroMS("Wait Background Thread", t(times[PHASE_WAIT_BACKGROUND_THREAD])); - ss.appendIfNonzeroMS("Purge", t(times[PHASE_PURGE])); - ss.appendIfNonzeroMS("Mark", t(times[PHASE_MARK])); - ss.appendIfNonzeroMS("Mark Roots", t(times[PHASE_MARK_ROOTS])); - ss.appendIfNonzeroMS("Mark Delayed", t(times[PHASE_MARK_DELAYED])); - ss.appendIfNonzeroMS("Mark Other", t(times[PHASE_MARK_OTHER])); - ss.appendIfNonzeroMS("Finalize Start Callback", t(times[PHASE_FINALIZE_START])); - ss.appendIfNonzeroMS("Sweep", t(times[PHASE_SWEEP])); - ss.appendIfNonzeroMS("Sweep Compartments", t(times[PHASE_SWEEP_COMPARTMENTS])); - ss.appendIfNonzeroMS("Sweep Object", t(times[PHASE_SWEEP_OBJECT])); - ss.appendIfNonzeroMS("Sweep String", t(times[PHASE_SWEEP_STRING])); - ss.appendIfNonzeroMS("Sweep Script", t(times[PHASE_SWEEP_SCRIPT])); - ss.appendIfNonzeroMS("Sweep Shape", t(times[PHASE_SWEEP_SHAPE])); - ss.appendIfNonzeroMS("Discard Code", t(times[PHASE_DISCARD_CODE])); - ss.appendIfNonzeroMS("Discard Analysis", t(times[PHASE_DISCARD_ANALYSIS])); - ss.appendIfNonzeroMS("Discard TI", t(times[PHASE_DISCARD_TI])); - ss.appendIfNonzeroMS("Sweep Types", t(times[PHASE_SWEEP_TYPES])); - ss.appendIfNonzeroMS("Clear Script Analysis", t(times[PHASE_CLEAR_SCRIPT_ANALYSIS])); - ss.appendIfNonzeroMS("Finalize End Callback", t(times[PHASE_FINALIZE_END])); - ss.appendIfNonzeroMS("Deallocate", t(times[PHASE_DESTROY])); - ss.appendIfNonzeroMS("End Callback", t(times[PHASE_GC_END])); + for (unsigned i = 0; phases[i].name; i++) + ss.appendIfNonzeroMS(phases[i].name, t(times[phases[i].index])); + 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(); } @@ -365,21 +387,25 @@ Statistics::formatData(StatisticsSerializer &ss, uint64_t timestamp) ss.beginObject(NULL); ss.extra(" "); ss.appendNumber("Slice", "%d", "", i); - ss.appendDecimal("Time", "ms", t(slices[i].end - slices[0].start)); - ss.extra(" ("); ss.appendDecimal("Pause", "", t(width)); + ss.extra(" ("); + ss.appendDecimal("When", "ms", t(slices[i].end - slices[0].start)); ss.appendString("Reason", ExplainReason(slices[i].reason)); if (slices[i].resetReason) ss.appendString("Reset", slices[i].resetReason); ss.extra("): "); - formatPhases(ss, "times", slices[i].phaseTimes); + FormatPhaseTimes(ss, "Times", slices[i].phaseTimes); + if (ss.isJSON()) + FormatPhaseFaults(ss, "Page Faults", slices[i].phaseFaults); ss.endLine(); ss.endObject(); } ss.endArray(); } ss.extra(" Totals: "); - formatPhases(ss, "totals", phaseTimes); + FormatPhaseTimes(ss, "Totals", phaseTimes); + if (ss.isJSON()) + FormatPhaseFaults(ss, "Total Page Faults", phaseFaults); ss.endObject(); return !ss.isOOM(); @@ -439,7 +465,7 @@ Statistics::~Statistics() if (fp) { if (fullFormat) { StatisticsSerializer ss(StatisticsSerializer::AsText); - formatPhases(ss, "", phaseTotals); + FormatPhaseTimes(ss, "", phaseTotals); char *msg = ss.finishCString(); if (msg) { fprintf(fp, "TOTALS\n%s\n\n-------\n", msg); @@ -481,8 +507,10 @@ Statistics::printStats() void Statistics::beginGC() { - PodArrayZero(phaseStarts); + PodArrayZero(phaseStartTimes); + PodArrayZero(phaseStartFaults); PodArrayZero(phaseTimes); + PodArrayZero(phaseFaults); slices.clearAndFree(); nonincrementalReason = NULL; @@ -570,7 +598,8 @@ Statistics::endSlice() void Statistics::beginPhase(Phase phase) { - phaseStarts[phase] = PRMJ_Now(); + phaseStartTimes[phase] = PRMJ_Now(); + phaseStartFaults[phase] = gc::GetPageFaultCount(); if (phase == gcstats::PHASE_MARK) Probes::GCStartMarkPhase(); @@ -581,11 +610,14 @@ Statistics::beginPhase(Phase phase) void Statistics::endPhase(Phase phase) { - int64_t now = PRMJ_Now(); - int64_t t = now - phaseStarts[phase]; + int64_t t = PRMJ_Now() - phaseStartTimes[phase]; slices.back().phaseTimes[phase] += t; phaseTimes[phase] += t; + 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 b738238442d..d617211433d 100644 --- a/js/src/gc/Statistics.h +++ b/js/src/gc/Statistics.h @@ -131,12 +131,14 @@ struct Statistics { : reason(reason), resetReason(NULL), start(start) { PodArrayZero(phaseTimes); + PodArrayZero(phaseFaults); } gcreason::Reason reason; const char *resetReason; int64_t start, end; int64_t phaseTimes[PHASE_LIMIT]; + size_t phaseFaults[PHASE_LIMIT]; int64_t duration() const { return end - start; } }; @@ -144,10 +146,12 @@ struct Statistics { Vector slices; /* Most recent time when the given phase started. */ - int64_t phaseStarts[PHASE_LIMIT]; + 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];