Bug 1322560 - Convert GC statistics over to JSONPrinter, r=jonco

--HG--
extra : rebase_source : 3e38274af5dadbed6fa6ddf884607a6de0d3f058
This commit is contained in:
Steve Fink 2017-05-03 13:57:10 -07:00
Родитель 728e71fe62
Коммит 7b71d877e9
9 изменённых файлов: 201 добавлений и 189 удалений

Просмотреть файл

@ -642,174 +642,123 @@ Statistics::formatDetailedTotals() const
return DuplicateString(buffer);
}
UniqueChars
Statistics::formatJsonSlice(size_t sliceNum) const
void
Statistics::formatJsonSlice(size_t sliceNum, JSONPrinter& json) const
{
FragmentVector fragments;
json.beginObject();
formatJsonSliceDescription(sliceNum, slices_[sliceNum], json);
if (!fragments.append(DuplicateString("{")) ||
!fragments.append(formatJsonSliceDescription(sliceNum, slices_[sliceNum])) ||
!fragments.append(DuplicateString("\"times\":{")) ||
!fragments.append(formatJsonPhaseTimes(slices_[sliceNum].phaseTimes)) ||
!fragments.append(DuplicateString("}}")))
{
return UniqueChars(nullptr);
}
json.beginObjectProperty("times");
formatJsonPhaseTimes(slices_[sliceNum].phaseTimes, json);
json.endObject();
return Join(fragments);
json.endObject();
}
UniqueChars
Statistics::formatJsonMessage(uint64_t timestamp, bool includeSlices) const
Statistics::renderJsonSlice(size_t sliceNum) const
{
Sprinter printer(nullptr, false);
if (!printer.init())
return UniqueChars(nullptr);
JSONPrinter json(printer);
formatJsonSlice(sliceNum, json);
return UniqueChars(printer.release());
}
UniqueChars
Statistics::renderJsonMessage(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)))
{
Sprinter printer(nullptr, false);
if (!printer.init())
return UniqueChars(nullptr);
}
JSONPrinter json(printer);
json.beginObject();
formatJsonDescription(timestamp, json);
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);
json.beginListProperty("slices");
for (unsigned i = 0; i < slices_.length(); i++)
formatJsonSlice(i, json);
json.endList();
}
if (!fragments.append(DuplicateString("\"totals\":{")) ||
!fragments.append(formatJsonPhaseTimes(phaseTimes)) ||
!fragments.append(DuplicateString("}}")))
{
return UniqueChars(nullptr);
}
json.beginObjectProperty("totals");
formatJsonPhaseTimes(phaseTimes, json);
json.endObject();
return Join(fragments);
json.endObject();
return UniqueChars(printer.release());
}
// JSON requires decimals to be separated by periods, but the LC_NUMERIC
// setting may cause printf to use commas in some locales. Split a duration
// into whole and fractional parts of milliseconds, for use in passing to
// %llu.%03llu.
static lldiv_t
SplitDurationMS(TimeDuration d)
void
Statistics::formatJsonDescription(uint64_t timestamp, JSONPrinter& json) const
{
return lldiv(static_cast<int64_t>(d.ToMicroseconds()), 1000);
}
json.property("timestamp", timestamp);
UniqueChars
Statistics::formatJsonDescription(uint64_t timestamp) const
{
TimeDuration total, longest;
gcDuration(&total, &longest);
lldiv_t totalParts = SplitDurationMS(total);
lldiv_t longestParts = SplitDurationMS(longest);
json.property("max_pause", longest, JSONPrinter::MILLISECONDS);
json.property("total_time", total, JSONPrinter::MILLISECONDS);
TimeDuration sccTotal, sccLongest;
sccDurations(&sccTotal, &sccLongest);
lldiv_t sccTotalParts = SplitDurationMS(sccTotal);
lldiv_t sccLongestParts = SplitDurationMS(sccLongest);
json.property("reason", ExplainReason(slices_[0].reason));
json.property("zones_collected", zoneStats.collectedZoneCount);
json.property("total_zones", zoneStats.zoneCount);
json.property("total_compartments", zoneStats.compartmentCount);
json.property("minor_gcs", counts[STAT_MINOR_GC]);
json.property("store_buffer_overflows", counts[STAT_STOREBUFFER_OVERFLOW]);
const double mmu20 = computeMMU(TimeDuration::FromMilliseconds(20));
const double mmu50 = computeMMU(TimeDuration::FromMilliseconds(50));
json.property("mmu_20ms", int(mmu20 * 100));
json.property("mmu_50ms", int(mmu50 * 100));
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,"
"\"scc_sweep_max_pause\":%llu.%03llu,"
"\"nonincremental_reason\":\"%s\","
"\"allocated\":%u,"
"\"added_chunks\":%d,"
"\"removed_chunks\":%d,"
"\"major_gc_number\":%llu,"
"\"minor_gc_number\":%llu,";
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,
getCount(STAT_MINOR_GC),
getCount(STAT_STOREBUFFER_OVERFLOW),
int(mmu20 * 100),
int(mmu50 * 100),
sccTotalParts.quot, sccTotalParts.rem,
sccLongestParts.quot, sccLongestParts.rem,
ExplainAbortReason(nonincrementalReason_),
unsigned(preBytes / 1024 / 1024),
getCount(STAT_NEW_CHUNK),
getCount(STAT_DESTROY_CHUNK),
startingMajorGCNumber,
startingMinorGCNumber);
return DuplicateString(buffer);
TimeDuration sccTotal, sccLongest;
sccDurations(&sccTotal, &sccLongest);
json.property("scc_sweep_total", sccTotal, JSONPrinter::MILLISECONDS);
json.property("scc_sweep_max_pause", sccLongest, JSONPrinter::MILLISECONDS);
json.property("nonincremental_reason", ExplainAbortReason(nonincrementalReason_));
json.property("allocated", uint64_t(preBytes) / 1024 / 1024);
json.property("added_chunks", getCount(STAT_NEW_CHUNK));
json.property("removed_chunks", getCount(STAT_DESTROY_CHUNK));
json.property("major_gc_number", startingMajorGCNumber);
json.property("minor_gc_number", startingMinorGCNumber);
}
UniqueChars
Statistics::formatJsonSliceDescription(unsigned i, const SliceData& slice) const
void
Statistics::formatJsonSliceDescription(unsigned i, const SliceData& slice, JSONPrinter& json) const
{
TimeDuration duration = slice.duration();
lldiv_t durationParts = SplitDurationMS(duration);
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,"
"\"pause\":%llu.%03llu,"
"\"when\":%llu.%03llu,"
"\"reason\":\"%s\","
"\"initial_state\":\"%s\","
"\"final_state\":\"%s\","
"\"budget\":\"%s\","
"\"page_faults\":%llu,"
"\"start_timestamp\":%llu,"
"\"end_timestamp\":%llu,";
char buffer[1024];
SprintfLiteral(buffer, format,
i,
durationParts.quot, durationParts.rem,
whenParts.quot, whenParts.rem,
ExplainReason(slice.reason),
gc::StateName(slice.initialState),
gc::StateName(slice.finalState),
budgetDescription,
pageFaults,
(slice.start - originTime).ToSeconds(),
(slice.end - originTime).ToSeconds());
return DuplicateString(buffer);
json.property("slice", i);
json.property("pause", slice.duration(), JSONPrinter::MILLISECONDS);
json.property("when", when, JSONPrinter::MILLISECONDS);
json.property("reason", ExplainReason(slice.reason));
json.property("initial_state", gc::StateName(slice.initialState));
json.property("final_state", gc::StateName(slice.finalState));
json.property("budget", budgetDescription);
json.property("page_faults", int64_t(slice.endFaults - slice.startFaults));
json.property("start_timestamp", slice.start - originTime, JSONPrinter::SECONDS);
json.property("end_timestamp", slice.end - originTime, JSONPrinter::SECONDS);
}
UniqueChars
FilterJsonKey(const char*const buffer)
static UniqueChars
SanitizeJsonKey(const char* const buffer)
{
char* mut = strdup(buffer);
if (!mut)
return UniqueChars(nullptr);
char* c = mut;
while (*c) {
if (!isalpha(*c))
@ -818,31 +767,25 @@ FilterJsonKey(const char*const buffer)
*c = tolower(*c);
++c;
}
return UniqueChars(mut);
}
UniqueChars
Statistics::formatJsonPhaseTimes(const PhaseTimeTable& phaseTimes) const
void
Statistics::formatJsonPhaseTimes(const PhaseTimeTable& phaseTimes, JSONPrinter& json) const
{
FragmentVector fragments;
char buffer[128];
for (AllPhaseIterator iter; !iter.done(); iter.advance()) {
Phase phase;
size_t dagSlot;
iter.get(&phase, &dagSlot);
UniqueChars name = FilterJsonKey(phases[phase].name);
UniqueChars name = SanitizeJsonKey(phases[phase].name);
if (!name)
json.outOfMemory();
TimeDuration ownTime = phaseTimes[dagSlot][phase];
if (!ownTime.IsZero()) {
lldiv_t ownParts = SplitDurationMS(ownTime);
SprintfLiteral(buffer, "\"%s\":%llu.%03llu",
name.get(), ownParts.quot, ownParts.rem);
if (!fragments.append(DuplicateString(buffer)))
return UniqueChars(nullptr);
}
if (!ownTime.IsZero())
json.property(name.get(), ownTime, JSONPrinter::MILLISECONDS);
}
return Join(fragments, ",");
}
Statistics::Statistics(JSRuntime* rt)
@ -1414,18 +1357,18 @@ Statistics::computeMMU(TimeDuration window) const
int startIndex = 0;
for (size_t endIndex = 1; endIndex < slices_.length(); endIndex++) {
auto& startSlice = slices_[startIndex];
auto* startSlice = &slices_[startIndex];
auto& endSlice = slices_[endIndex];
gc += endSlice.end - endSlice.start;
while (endSlice.end - startSlice.end >= window) {
gc -= startSlice.end - startSlice.start;
startIndex++;
while (endSlice.end - startSlice->end >= window) {
gc -= startSlice->end - startSlice->start;
startSlice = &slices_[++startIndex];
}
TimeDuration cur = gc;
if (endSlice.end - startSlice.start > window)
cur -= (endSlice.end - startSlice.start - window);
if (endSlice.end - startSlice->start > window)
cur -= (endSlice.end - startSlice->start - window);
if (cur > gcMax)
gcMax = cur;
}

Просмотреть файл

@ -20,6 +20,7 @@
#include "js/GCAPI.h"
#include "js/Vector.h"
#include "vm/JSONPrinter.h"
using mozilla::Maybe;
@ -283,7 +284,6 @@ struct Statistics
UniqueChars formatCompactSliceMessage() const;
UniqueChars formatCompactSummaryMessage() const;
UniqueChars formatJsonMessage(uint64_t timestamp, bool includeSlices = true) const;
UniqueChars formatDetailedMessage() const;
JS::GCSliceCallback setSliceCallback(JS::GCSliceCallback callback);
@ -348,8 +348,12 @@ struct Statistics
// Print total profile times on shutdown.
void printTotalProfileTimes();
// Return JSON for a whole major GC, optionally including detailed
// per-slice data.
UniqueChars renderJsonMessage(uint64_t timestamp, bool includeSlices = true) const;
// Return JSON for the timings of just the given slice.
UniqueChars formatJsonSlice(size_t sliceNum) const;
UniqueChars renderJsonSlice(size_t sliceNum) const;
private:
JSRuntime* runtime;
@ -455,9 +459,10 @@ FOR_EACH_GC_PROFILE_TIME(DEFINE_TIME_KEY)
UniqueChars formatDetailedPhaseTimes(const PhaseTimeTable& phaseTimes) const;
UniqueChars formatDetailedTotals() const;
UniqueChars formatJsonDescription(uint64_t timestamp) const;
UniqueChars formatJsonSliceDescription(unsigned i, const SliceData& slice) const;
UniqueChars formatJsonPhaseTimes(const PhaseTimeTable& phaseTimes) const;
void formatJsonDescription(uint64_t timestamp, JSONPrinter&) const;
void formatJsonSliceDescription(unsigned i, const SliceData& slice, JSONPrinter&) const;
void formatJsonPhaseTimes(const PhaseTimeTable& phaseTimes, JSONPrinter&) const;
void formatJsonSlice(size_t sliceNum, JSONPrinter&) const;
double computeMMU(TimeDuration resolution) const;

Просмотреть файл

@ -81,14 +81,14 @@ CacheIRSpewer::beginCache(LockGuard<Mutex>&, const IRGenerator& gen)
JSONPrinter& j = json.ref();
j.beginObject();
j.property("name", "%s", CacheKindNames[uint8_t(gen.cacheKind_)]);
j.property("file", "%s", gen.script_->filename());
j.property("name", CacheKindNames[uint8_t(gen.cacheKind_)]);
j.property("file", gen.script_->filename());
j.property("mode", int(gen.mode_));
if (jsbytecode* pc = gen.pc_) {
unsigned column;
j.property("line", PCToLineNumber(gen.script_, pc, &column));
j.property("column", column);
j.property("pc", "%p", pc);
j.formatProperty("pc", "%p", pc);
}
}
@ -133,7 +133,7 @@ CacheIRSpewer::valueProperty(LockGuard<Mutex>&, const char* name, const Value& v
const char* type = InformalValueTypeName(v);
if (v.isInt32())
type = "int32";
j.property("type", "%s", type);
j.property("type", type);
if (v.isInt32()) {
j.property("value", v.toInt32());
@ -147,7 +147,7 @@ CacheIRSpewer::valueProperty(LockGuard<Mutex>&, const char* name, const Value& v
j.endStringProperty();
}
} else if (v.isObject()) {
j.property("value", "%p (shape: %p)", &v.toObject(),
j.formatProperty("value", "%p (shape: %p)", &v.toObject(),
v.toObject().maybeShape());
}
@ -158,7 +158,7 @@ void
CacheIRSpewer::attached(LockGuard<Mutex>&, const char* name)
{
MOZ_ASSERT(enabled());
json.ref().property("attached", "%s", name);
json.ref().property("attached", name);
}
void

Просмотреть файл

@ -26,7 +26,7 @@ JSONSpewer::beginFunction(JSScript* script)
{
beginObject();
if (script)
property("name", "%s:%" PRIuSIZE, script->filename(), script->lineno());
formatProperty("name", "%s:%" PRIuSIZE, script->filename(), script->lineno());
else
property("name", "wasm compilation");
beginListProperty("passes");
@ -36,7 +36,7 @@ void
JSONSpewer::beginPass(const char* pass)
{
beginObject();
property("name", "%s", pass);
property("name", pass);
}
void
@ -52,13 +52,13 @@ JSONSpewer::spewMResumePoint(MResumePoint* rp)
switch (rp->mode()) {
case MResumePoint::ResumeAt:
property("mode", "%s", "At");
property("mode", "At");
break;
case MResumePoint::ResumeAfter:
property("mode", "%s", "After");
property("mode", "After");
break;
case MResumePoint::Outer:
property("mode", "%s", "Outer");
property("mode", "Outer");
break;
}
@ -119,7 +119,7 @@ JSONSpewer::spewMDef(MDefinition* def)
out_.printf(" : %s%s", StringFromMIRType(def->type()), (isTruncated ? " (t)" : ""));
endStringProperty();
} else {
property("type", "%s%s", StringFromMIRType(def->type()), (isTruncated ? " (t)" : ""));
formatProperty("type", "%s%s", StringFromMIRType(def->type()), (isTruncated ? " (t)" : ""));
}
if (def->isInstruction()) {
@ -255,7 +255,7 @@ JSONSpewer::spewRanges(BacktrackingAllocator* regalloc)
LiveRange* range = LiveRange::get(*iter);
beginObject();
property("allocation", "%s", range->bundle()->allocation().toString().get());
property("allocation", range->bundle()->allocation().toString().get());
property("start", range->from().bits());
property("end", range->to().bits());
endObject();

Просмотреть файл

@ -7634,7 +7634,7 @@ JS::GCDescription::toGCEvent(JSContext* cx) const
char16_t*
JS::GCDescription::formatJSON(JSContext* cx, uint64_t timestamp) const
{
UniqueChars cstr = cx->runtime()->gc.stats().formatJsonMessage(timestamp);
UniqueChars cstr = cx->runtime()->gc.stats().renderJsonMessage(timestamp);
size_t nchars = strlen(cstr.get());
UniqueTwoByteChars out(js_pod_malloc<char16_t>(nchars + 1));
@ -7675,13 +7675,13 @@ 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);
return cx->runtime()->gc.stats().renderJsonSlice(slices - 1);
}
JS::UniqueChars
JS::GCDescription::summaryToJSON(JSContext* cx) const
{
return cx->runtime()->gc.stats().formatJsonMessage(0, false);
return cx->runtime()->gc.stats().renderJsonMessage(0, false);
}
JS_PUBLIC_API(JS::GCSliceCallback)

Просмотреть файл

@ -8,12 +8,21 @@
#include "mozilla/Assertions.h"
#include "mozilla/FloatingPoint.h"
#include "mozilla/IntegerPrintfMacros.h"
#include "mozilla/SizePrintfMacros.h"
#include <stdarg.h>
#include "jsdtoa.h"
using namespace js;
JSONPrinter::~JSONPrinter()
{
if (dtoaState_)
DestroyDtoaState(dtoaState_);
}
void
JSONPrinter::indent()
{
@ -76,7 +85,15 @@ JSONPrinter::endStringProperty()
}
void
JSONPrinter::property(const char* name, const char* format, ...)
JSONPrinter::property(const char* name, const char* value)
{
beginStringProperty(name);
out_.put(value);
endStringProperty();
}
void
JSONPrinter::formatProperty(const char* name, const char* format, ...)
{
va_list ap;
va_start(ap, format);
@ -124,7 +141,7 @@ void
JSONPrinter::property(const char* name, uint32_t value)
{
propertyName(name);
out_.printf("%u", value);
out_.printf("%" PRIu32, value);
}
void
@ -160,6 +177,33 @@ JSONPrinter::property(const char* name, double value)
out_.printf("null");
}
void
JSONPrinter::floatProperty(const char* name, double value, size_t precision)
{
if (!mozilla::IsFinite(value)) {
propertyName(name);
out_.printf("null");
return;
}
if (!dtoaState_) {
dtoaState_ = NewDtoaState();
if (!dtoaState_) {
out_.reportOutOfMemory();
return;
}
}
char buffer[DTOSTR_STANDARD_BUFFER_SIZE];
char* str = js_dtostr(dtoaState_, buffer, sizeof(buffer), DTOSTR_STANDARD, precision, value);
if (!str) {
out_.reportOutOfMemory();
return;
}
property(name, str);
}
void
JSONPrinter::property(const char* name, const mozilla::TimeDuration& dur, TimePrecision precision)
{

Просмотреть файл

@ -7,13 +7,15 @@
#ifndef vm_JSONPrinter_h
#define vm_JSONPrinter_h
#include <stdio.h>
#include "mozilla/TimeStamp.h"
#include <stdio.h>
#include "js/TypeDecls.h"
#include "vm/Printer.h"
struct DtoaState;
namespace js {
class JSONPrinter
@ -22,6 +24,7 @@ class JSONPrinter
int indentLevel_;
bool first_;
GenericPrinter& out_;
DtoaState* dtoaState_;
void indent();
@ -29,8 +32,12 @@ class JSONPrinter
explicit JSONPrinter(GenericPrinter& out)
: indentLevel_(0),
first_(true),
out_(out)
{ }
out_(out),
dtoaState_(nullptr)
{
}
~JSONPrinter();
void beginObject();
void beginObjectProperty(const char* name);
@ -39,7 +46,7 @@ class JSONPrinter
void value(const char* format, ...) MOZ_FORMAT_PRINTF(2, 3);
void value(int value);
void property(const char* name, const char* format, ...) MOZ_FORMAT_PRINTF(3, 4);
void property(const char* name, const char* value);
void property(const char* name, int32_t value);
void property(const char* name, uint32_t value);
void property(const char* name, int64_t value);
@ -52,18 +59,25 @@ class JSONPrinter
#endif
void property(const char* name, double value);
void formatProperty(const char* name, const char* format, ...) MOZ_FORMAT_PRINTF(3, 4);
// JSON requires decimals to be separated by periods, but the LC_NUMERIC
// setting may cause printf to use commas in some locales. Enforce using a
// period.
// setting may cause printf to use commas in some locales.
enum TimePrecision { SECONDS, MILLISECONDS };
void property(const char* name, const mozilla::TimeDuration& dur, TimePrecision precision);
void floatProperty(const char* name, double value, size_t precision);
void beginStringProperty(const char* name);
void endStringProperty();
void endObject();
void endList();
// Notify the output that the caller has detected OOM and should transition
// to its saw-OOM state.
void outOfMemory() { out_.reportOutOfMemory(); }
protected:
void propertyName(const char* name);
};

Просмотреть файл

@ -150,16 +150,20 @@ Sprinter::checkInvariants() const
MOZ_ASSERT(base[size - 1] == 0);
}
const char*
Sprinter::string() const
char*
Sprinter::release()
{
return base;
}
checkInvariants();
if (hadOOM_)
return nullptr;
const char*
Sprinter::stringEnd() const
{
return base + offset;
char* str = base;
base = nullptr;
offset = size = 0;
#ifdef DEBUG
initialized = false;
#endif
return str;
}
char*

Просмотреть файл

@ -94,8 +94,10 @@ class Sprinter final : public GenericPrinter
void checkInvariants() const;
const char* string() const;
const char* stringEnd() const;
const char* string() const { return base; }
const char* stringEnd() const { return base + offset; }
char* release();
// Returns the string at offset |off|.
char* stringAt(ptrdiff_t off) const;
// Returns the char at offset |off|.