зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1322560 - Convert GC statistics over to JSONPrinter, r=jonco
--HG-- extra : rebase_source : 3e38274af5dadbed6fa6ddf884607a6de0d3f058
This commit is contained in:
Родитель
728e71fe62
Коммит
7b71d877e9
|
@ -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|.
|
||||
|
|
Загрузка…
Ссылка в новой задаче