Bug 850523 (part 1) - Add a "js-main-runtime-temporary-peak" memory reporter. r=wmccloskey,jlebar.

--HG--
extra : rebase_source : d183213c48b9d07fb62fce830998bcb8bb044f47
This commit is contained in:
Nicholas Nethercote 2013-03-06 20:40:36 -08:00
parent f42ce30aaa
commit b381a3dc72
7 changed files with 119 additions and 31 deletions

View File

@ -429,6 +429,9 @@ SystemCompartmentCount(JSRuntime *rt);
extern JS_PUBLIC_API(size_t)
UserCompartmentCount(JSRuntime *rt);
extern JS_PUBLIC_API(size_t)
PeakSizeOfTemporary(const JSRuntime *rt);
} // namespace JS
#endif // js_MemoryMetrics_h

View File

@ -60,9 +60,16 @@ LifoAlloc::freeAll()
while (first) {
BumpChunk *victim = first;
first = first->next();
decrementCurSize(victim->computedSizeOfIncludingThis());
BumpChunk::delete_(victim);
}
first = latest = last = NULL;
/*
* Nb: maintaining curSize_ correctly isn't easy. Fortunately, this is an
* excellent sanity check.
*/
JS_ASSERT(curSize_ == 0);
}
LifoAlloc::BumpChunk *
@ -105,6 +112,11 @@ LifoAlloc::getOrCreateChunk(size_t n)
latest->setNext(newChunk);
latest = last = newChunk;
}
size_t computedChunkSize = newChunk->computedSizeOfIncludingThis();
JS_ASSERT(computedChunkSize == chunkSize);
incrementCurSize(computedChunkSize);
return newChunk;
}
@ -118,8 +130,10 @@ LifoAlloc::transferFrom(LifoAlloc *other)
if (!other->first)
return;
incrementCurSize(other->curSize_);
append(other->first, other->last);
other->first = other->last = other->latest = NULL;
other->curSize_ = 0;
}
void
@ -131,14 +145,22 @@ LifoAlloc::transferUnusedFrom(LifoAlloc *other)
if (other->markCount || !other->first)
return;
/*
* Because of how getOrCreateChunk works, there may be unused chunks before
* |last|. We do not transfer those chunks. In most cases it is expected
* that last == first, so this should be a rare situation that, when it
* happens, should not recur.
*/
// Transfer all chunks *after* |latest|.
if (other->latest->next()) {
if (other->latest == other->first) {
// We're transferring everything except the first chunk.
size_t delta = other->curSize_ - other->first->computedSizeOfIncludingThis();
other->decrementCurSize(delta);
incrementCurSize(delta);
} else {
for (BumpChunk *chunk = other->latest->next(); chunk; chunk = chunk->next()) {
size_t size = chunk->computedSizeOfIncludingThis();
incrementCurSize(size);
other->decrementCurSize(size);
}
}
append(other->latest->next(), other->last);
other->latest->setNext(NULL);
other->last = other->latest;

View File

@ -94,10 +94,15 @@ class BumpChunk
void setNext(BumpChunk *succ) { next_ = succ; }
size_t used() const { return bump - bumpBase(); }
size_t sizeOfIncludingThis(JSMallocSizeOfFun mallocSizeOf) {
return mallocSizeOf(this);
}
size_t computedSizeOfIncludingThis() {
return limit - headerBase();
}
void resetBump() {
setBump(headerBase() + sizeof(BumpChunk));
}
@ -165,11 +170,13 @@ class LifoAlloc
BumpChunk *last;
size_t markCount;
size_t defaultChunkSize_;
size_t curSize_;
size_t peakSize_;
void operator=(const LifoAlloc &) MOZ_DELETE;
LifoAlloc(const LifoAlloc &) MOZ_DELETE;
/*
/*
* Return a BumpChunk that can perform an allocation of at least size |n|
* and add it to the chain appropriately.
*
@ -183,6 +190,7 @@ class LifoAlloc
first = latest = last = NULL;
defaultChunkSize_ = defaultChunkSize;
markCount = 0;
curSize_ = 0;
}
void append(BumpChunk *start, BumpChunk *end) {
@ -194,17 +202,39 @@ class LifoAlloc
last = end;
}
void incrementCurSize(size_t size) {
curSize_ += size;
if (curSize_ > peakSize_)
peakSize_ = curSize_;
}
void decrementCurSize(size_t size) {
MOZ_ASSERT(curSize_ >= size);
curSize_ -= size;
}
public:
explicit LifoAlloc(size_t defaultChunkSize) { reset(defaultChunkSize); }
explicit LifoAlloc(size_t defaultChunkSize)
: peakSize_(0)
{
reset(defaultChunkSize);
}
/* Steal allocated chunks from |other|. */
void steal(LifoAlloc *other) {
JS_ASSERT(!other->markCount);
/*
* Copy everything from |other| to |this| except for |peakSize_|, which
* requires some care.
*/
size_t oldPeakSize = peakSize_;
PodCopy((char *) this, (char *) other, sizeof(*this));
peakSize_ = Max(oldPeakSize, curSize_);
other->reset(defaultChunkSize_);
}
/* Append allocated chunks from |other|. They are removed from |other|. */
/* Append all chunks from |other|. They are removed from |other|. */
void transferFrom(LifoAlloc *other);
/* Append unused chunks from |other|. They are removed from |other|. */
@ -249,12 +279,10 @@ class LifoAlloc
JS_ALWAYS_INLINE
bool ensureUnusedApproximate(size_t n) {
size_t total = 0;
BumpChunk *chunk = latest;
while (chunk) {
for (BumpChunk *chunk = latest; chunk; chunk = chunk->next()) {
total += chunk->unused();
if (total >= n)
return true;
chunk = chunk->next();
}
BumpChunk *latestBefore = latest;
if (!getOrCreateChunk(n))
@ -298,18 +326,15 @@ class LifoAlloc
return;
}
/*
/*
* Find the chunk that contains |mark|, and make sure we don't pass
* |latest| along the way -- we should be making the chain of active
* chunks shorter, not longer!
*/
BumpChunk *container = first;
while (true) {
if (container->contains(mark))
break;
BumpChunk *container;
for (container = first; !container->contains(mark); container = container->next())
JS_ASSERT(container != latest);
container = container->next();
}
latest = container;
latest->release(mark);
}
@ -324,25 +349,23 @@ class LifoAlloc
/* Get the total "used" (occupied bytes) count for the arena chunks. */
size_t used() const {
size_t accum = 0;
BumpChunk *it = first;
while (it) {
accum += it->used();
if (it == latest)
for (BumpChunk *chunk = first; chunk; chunk = chunk->next()) {
accum += chunk->used();
if (chunk == latest)
break;
it = it->next();
}
return accum;
}
/* Get the total size of the arena chunks (including unused space). */
size_t sizeOfExcludingThis(JSMallocSizeOfFun mallocSizeOf) const {
size_t accum = 0;
BumpChunk *it = first;
while (it) {
accum += it->sizeOfIncludingThis(mallocSizeOf);
it = it->next();
}
return accum;
size_t n = 0;
for (BumpChunk *chunk = first; chunk; chunk = chunk->next())
n += chunk->sizeOfIncludingThis(mallocSizeOf);
/* While we're here, let's sanity check curSize_. */
MOZ_ASSERT(curSize_ == n);
return n;
}
/* Like sizeOfExcludingThis(), but includes the size of the LifoAlloc itself. */
@ -350,6 +373,12 @@ class LifoAlloc
return mallocSizeOf(this) + sizeOfExcludingThis(mallocSizeOf);
}
/*
* Get the peak size of the arena chunks (including unused space and
* bookkeeping space).
*/
size_t peakSizeOfExcludingThis() const { return peakSize_; }
/* Doesn't perform construction; useful for lazily-initialized POD types. */
template <typename T>
JS_ALWAYS_INLINE

View File

@ -392,3 +392,10 @@ JS::UserCompartmentCount(JSRuntime *rt)
}
return n;
}
JS_PUBLIC_API(size_t)
JS::PeakSizeOfTemporary(const JSRuntime *rt)
{
return rt->tempLifoAlloc.peakSizeOfExcludingThis();
}

View File

@ -1412,6 +1412,7 @@ NS_MEMORY_REPORTER_IMPLEMENT(XPConnectJSGCHeap,
nsIMemoryReporter::UNITS_BYTES,
GetGCChunkTotalBytes,
"Memory used by the garbage-collected JavaScript heap.")
static int64_t
GetJSSystemCompartmentCount()
{
@ -1451,6 +1452,22 @@ NS_MEMORY_REPORTER_IMPLEMENT(XPConnectJSUserCompartmentCount,
"listed under 'js' if a garbage collection occurs at an inopportune time, "
"but such cases should be rare.")
static int64_t
GetJSMainRuntimeTemporaryPeakSize()
{
return JS::PeakSizeOfTemporary(nsXPConnect::GetRuntimeInstance()->GetJSRuntime());
}
// This is also a single reporter so it can be used by telemetry.
NS_MEMORY_REPORTER_IMPLEMENT(JSMainRuntimeTemporaryPeak,
"js-main-runtime-temporary-peak",
KIND_OTHER,
nsIMemoryReporter::UNITS_BYTES,
GetJSMainRuntimeTemporaryPeakSize,
"The peak size of the transient storage in the main JSRuntime (the "
"current size of which is reported as "
"'explicit/js-non-window/runtime/temporary').");
// The REPORT* macros do an unconditional report. The ZCREPORT* macros are for
// compartments and zones; they aggregate any entries smaller than
// SUNDRIES_THRESHOLD into "gc-heap/sundries" and "other-sundries" entries for
@ -2666,6 +2683,7 @@ XPCJSRuntime::XPCJSRuntime(nsXPConnect* aXPConnect)
NS_RegisterMemoryReporter(new NS_MEMORY_REPORTER_NAME(XPConnectJSGCHeap));
NS_RegisterMemoryReporter(new NS_MEMORY_REPORTER_NAME(XPConnectJSSystemCompartmentCount));
NS_RegisterMemoryReporter(new NS_MEMORY_REPORTER_NAME(XPConnectJSUserCompartmentCount));
NS_RegisterMemoryReporter(new NS_MEMORY_REPORTER_NAME(JSMainRuntimeTemporaryPeak));
NS_RegisterMemoryMultiReporter(new JSCompartmentsMultiReporter);
mJSHolders.Init(512);

View File

@ -210,6 +210,14 @@
"extended_statistics_ok": true,
"description": "Total JavaScript compartments used for web pages"
},
"MEMORY_JS_MAIN_RUNTIME_TEMPORARY_PEAK": {
"kind": "exponential",
"low": 1024,
"high": "16 * 1024 * 1024",
"n_buckets": 200,
"extended_statistics_ok": true,
"description": "Peak memory used by the main JSRuntime to store transient data (KB)"
},
"MEMORY_JS_GC_HEAP": {
"kind": "exponential",
"low": 1024,

View File

@ -59,6 +59,7 @@ const MEM_HISTOGRAMS = {
"js-gc-heap": "MEMORY_JS_GC_HEAP",
"js-compartments/system": "MEMORY_JS_COMPARTMENTS_SYSTEM",
"js-compartments/user": "MEMORY_JS_COMPARTMENTS_USER",
"js-main-runtime-temporary-peak": "MEMORY_JS_MAIN_RUNTIME_TEMPORARY_PEAK",
"explicit": "MEMORY_EXPLICIT",
"resident-fast": "MEMORY_RESIDENT",
"vsize": "MEMORY_VSIZE",