Bug 745034 - Add page fault counts to GC statistics (r=terrence,dmandelin)

This commit is contained in:
Bill McCloskey 2012-04-09 12:30:33 -07:00
parent c4f8c3922b
commit 6dfb423c22
5 changed files with 110 additions and 37 deletions

View File

@ -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'

View File

@ -15,6 +15,7 @@ namespace gc {
#if defined(XP_WIN)
#include "jswin.h"
#include <psapi.h>
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 <sys/mman.h>
@ -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 <sys/mman.h>
#include <unistd.h>
#include <sys/time.h>
#include <sys/resource.h>
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

View File

@ -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 */

View File

@ -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)

View File

@ -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<SliceData, 8, SystemAllocPolicy> 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];