Bug 714264 - Part c: Move IterateData / CollectCompartmentStatsForRuntime / GetExplicitNonHeapForRuntime to js/MemoryMetrics.h; r=njn

This patch also removes those APIs exposed in js/MemoryMetrics.h that aren't
used anymore.
This commit is contained in:
Ms2ger 2012-01-11 09:23:08 +01:00
Родитель 4dabf76c50
Коммит 555fdc26d7
12 изменённых файлов: 500 добавлений и 498 удалений

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

@ -59,6 +59,8 @@
#include "jscntxt.h"
#include "jsdbgapi.h"
#include "jsprf.h"
#include "js/MemoryMetrics.h"
#include "nsAlgorithm.h"
#include "nsContentUtils.h"
#include "nsDOMClassInfo.h"
@ -93,7 +95,7 @@ using mozilla::MutexAutoLock;
using mozilla::TimeDuration;
using mozilla::TimeStamp;
using mozilla::dom::workers::exceptions::ThrowDOMExceptionForCode;
using mozilla::xpconnect::memory::IterateData;
using mozilla::xpconnect::memory::ReportJSRuntimeStats;
USING_WORKERS_NAMESPACE
@ -223,7 +225,8 @@ public:
{
AssertIsOnMainThread();
IterateData data;
JS::IterateData data(xpc::JsMallocSizeOf, xpc::GetCompartmentName,
xpc::DestroyCompartmentName);
nsresult rv = CollectForRuntime(/* isQuick = */false, &data);
if (NS_FAILED(rv)) {
return rv;
@ -1475,9 +1478,9 @@ public:
{
JSAutoSuspendRequest asr(aCx);
*mSucceeded = mIsQuick ?
mozilla::xpconnect::memory::GetExplicitNonHeapForRuntime(JS_GetRuntime(aCx), mData) :
mozilla::xpconnect::memory::CollectCompartmentStatsForRuntime(JS_GetRuntime(aCx), mData);
*mSucceeded = mIsQuick
? JS::GetExplicitNonHeapForRuntime(JS_GetRuntime(aCx), static_cast<int64_t*>(mData), xpc::JsMallocSizeOf)
: JS::CollectCompartmentStatsForRuntime(JS_GetRuntime(aCx), static_cast<JS::IterateData*>(mData));
{
MutexAutoLock lock(mMutex);

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

@ -69,16 +69,6 @@ class nsIURI;
class nsPIDOMWindow;
class nsITimer;
namespace mozilla {
namespace xpconnect {
namespace memory {
struct IterateData;
} // namespace memory
} // namespace xpconnect
} // namespace mozilla
BEGIN_WORKERS_NAMESPACE
class WorkerPrivate;

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

@ -44,9 +44,11 @@
#include <string.h>
#include "jsalloc.h"
#include "jspubtd.h"
#include "js/Utility.h"
#include "js/Vector.h"
namespace JS {
@ -59,6 +61,7 @@ struct TypeInferenceMemoryStats
int64_t temporary;
};
typedef void* (* GetNameCallback)(JSContext *cx, JSCompartment *c);
typedef void (* DestroyNameCallback)(void *string);
struct CompartmentStats
@ -112,6 +115,81 @@ struct CompartmentStats
TypeInferenceMemoryStats typeInferenceMemory;
};
struct IterateData
{
IterateData(JSMallocSizeOfFun mallocSizeOf, GetNameCallback getNameCb,
DestroyNameCallback destroyNameCb)
: runtimeObject(0)
, runtimeAtomsTable(0)
, runtimeContexts(0)
, runtimeThreadsNormal(0)
, runtimeThreadsTemporary(0)
, runtimeThreadsRegexpCode(0)
, runtimeThreadsStackCommitted(0)
, gcHeapChunkTotal(0)
, gcHeapChunkCleanUnused(0)
, gcHeapChunkDirtyUnused(0)
, gcHeapChunkCleanDecommitted(0)
, gcHeapChunkDirtyDecommitted(0)
, gcHeapArenaUnused(0)
, gcHeapChunkAdmin(0)
, gcHeapUnusedPercentage(0)
, totalObjects(0)
, totalShapes(0)
, totalScripts(0)
, totalStrings(0)
#ifdef JS_METHODJIT
, totalMjit(0)
#endif
, totalTypeInference(0)
, totalAnalysisTemp(0)
, compartmentStatsVector()
, currCompartmentStats(NULL)
, mallocSizeOf(mallocSizeOf)
, getNameCb(getNameCb)
, destroyNameCb(destroyNameCb)
{}
int64_t runtimeObject;
int64_t runtimeAtomsTable;
int64_t runtimeContexts;
int64_t runtimeThreadsNormal;
int64_t runtimeThreadsTemporary;
int64_t runtimeThreadsRegexpCode;
int64_t runtimeThreadsStackCommitted;
int64_t gcHeapChunkTotal;
int64_t gcHeapChunkCleanUnused;
int64_t gcHeapChunkDirtyUnused;
int64_t gcHeapChunkCleanDecommitted;
int64_t gcHeapChunkDirtyDecommitted;
int64_t gcHeapArenaUnused;
int64_t gcHeapChunkAdmin;
int64_t gcHeapUnusedPercentage;
int64_t totalObjects;
int64_t totalShapes;
int64_t totalScripts;
int64_t totalStrings;
#ifdef JS_METHODJIT
int64_t totalMjit;
#endif
int64_t totalTypeInference;
int64_t totalAnalysisTemp;
js::Vector<CompartmentStats, 0, js::SystemAllocPolicy> compartmentStatsVector;
CompartmentStats *currCompartmentStats;
JSMallocSizeOfFun mallocSizeOf;
GetNameCallback getNameCb;
DestroyNameCallback destroyNameCb;
};
extern JS_PUBLIC_API(bool)
CollectCompartmentStatsForRuntime(JSRuntime *rt, IterateData *data);
extern JS_PUBLIC_API(bool)
GetExplicitNonHeapForRuntime(JSRuntime *rt, int64_t *amount,
JSMallocSizeOfFun mallocSizeOf);
extern JS_PUBLIC_API(void)
SizeOfCompartmentTypeInferenceData(JSContext *cx, JSCompartment *compartment,
TypeInferenceMemoryStats *stats,
@ -122,32 +200,9 @@ SizeOfObjectTypeInferenceData(/*TypeObject*/ void *object,
TypeInferenceMemoryStats *stats,
JSMallocSizeOfFun mallocSizeOf);
extern JS_PUBLIC_API(size_t)
SizeOfObjectDynamicSlots(JSObject *obj, JSMallocSizeOfFun mallocSizeOf);
extern JS_PUBLIC_API(size_t)
SizeOfCompartmentShapeTable(JSCompartment *c, JSMallocSizeOfFun mallocSizeOf);
extern JS_PUBLIC_API(size_t)
SizeOfCompartmentMjitCode(const JSCompartment *c);
extern JS_PUBLIC_API(bool)
IsShapeInDictionary(const void *shape);
extern JS_PUBLIC_API(size_t)
SizeOfShapePropertyTable(const void *shape, JSMallocSizeOfFun mallocSizeOf);
extern JS_PUBLIC_API(size_t)
SizeOfShapeKids(const void *shape, JSMallocSizeOfFun mallocSizeOf);
extern JS_PUBLIC_API(size_t)
SizeOfScriptData(JSScript *script, JSMallocSizeOfFun mallocSizeOf);
#ifdef JS_METHODJIT
extern JS_PUBLIC_API(size_t)
SizeOfScriptJitData(JSScript *script, JSMallocSizeOfFun mallocSizeOf);
#endif
extern JS_PUBLIC_API(size_t)
SystemCompartmentCount(const JSRuntime *rt);

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

@ -171,6 +171,7 @@ CPPSRCS = \
SemanticAnalysis.cpp \
TokenStream.cpp \
LifoAlloc.cpp \
MemoryMetrics.cpp \
RegExpObject.cpp \
RegExpStatics.cpp \
RegExp.cpp \

382
js/src/MemoryMetrics.cpp Normal file
Просмотреть файл

@ -0,0 +1,382 @@
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is about:memory glue.
*
* The Initial Developer of the Original Code is
* Ms2ger <ms2ger@gmail.com>.
* Portions created by the Initial Developer are Copyright (C) 2011
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#include "js/MemoryMetrics.h"
#include "mozilla/Assertions.h"
#include "jsapi.h"
#include "jscntxt.h"
#include "jscompartment.h"
#include "jsgc.h"
#include "jsobj.h"
#include "jsscope.h"
#include "jsscript.h"
#include "jsobjinlines.h"
namespace JS {
using namespace js;
static void
CompartmentMemoryCallback(JSContext *cx, void *vdata, JSCompartment *compartment)
{
// Append a new CompartmentStats to the vector.
IterateData *data = static_cast<IterateData *>(vdata);
// CollectCompartmentStatsForRuntime reserves enough space.
MOZ_ALWAYS_TRUE(data->compartmentStatsVector.growBy(1));
CompartmentStats &curr = data->compartmentStatsVector.back();
curr.init(data->getNameCb(cx, compartment), data->destroyNameCb);
data->currCompartmentStats = &curr;
// Get the compartment-level numbers.
#ifdef JS_METHODJIT
curr.mjitCode = compartment->sizeOfMjitCode();
#endif
SizeOfCompartmentTypeInferenceData(cx, compartment,
&curr.typeInferenceMemory,
data->mallocSizeOf);
curr.shapesCompartmentTables =
SizeOfCompartmentShapeTable(compartment, data->mallocSizeOf);
}
static void
ExplicitNonHeapCompartmentCallback(JSContext *cx, void *data, JSCompartment *compartment)
{
#ifdef JS_METHODJIT
size_t *n = static_cast<size_t *>(data);
*n += compartment->sizeOfMjitCode();
#endif
}
static void
ChunkCallback(JSContext *cx, void *vdata, gc::Chunk *chunk)
{
// Nb: This function is only called for dirty chunks, which is why we
// increment gcHeapChunkDirtyDecommitted.
IterateData *data = static_cast<IterateData *>(vdata);
for (size_t i = 0; i < gc::ArenasPerChunk; i++)
if (chunk->decommittedArenas.get(i))
data->gcHeapChunkDirtyDecommitted += gc::ArenaSize;
}
static void
ArenaCallback(JSContext *cx, void *vdata, gc::Arena *arena,
JSGCTraceKind traceKind, size_t thingSize)
{
IterateData *data = static_cast<IterateData *>(vdata);
data->currCompartmentStats->gcHeapArenaHeaders +=
sizeof(gc::ArenaHeader);
size_t allocationSpace = arena->thingsSpan(thingSize);
data->currCompartmentStats->gcHeapArenaPadding +=
gc::ArenaSize - allocationSpace - sizeof(gc::ArenaHeader);
// We don't call the callback on unused things. So we compute the
// unused space like this: arenaUnused = maxArenaUnused - arenaUsed.
// We do this by setting arenaUnused to maxArenaUnused here, and then
// subtracting thingSize for every used cell, in CellCallback().
data->currCompartmentStats->gcHeapArenaUnused += allocationSpace;
}
static void
CellCallback(JSContext *cx, void *vdata, void *thing, JSGCTraceKind traceKind,
size_t thingSize)
{
IterateData *data = static_cast<IterateData *>(vdata);
CompartmentStats *curr = data->currCompartmentStats;
switch (traceKind) {
case JSTRACE_OBJECT:
{
JSObject *obj = static_cast<JSObject *>(thing);
if (obj->isFunction()) {
curr->gcHeapObjectsFunction += thingSize;
} else {
curr->gcHeapObjectsNonFunction += thingSize;
}
curr->objectSlots += obj->dynamicSlotSize(data->mallocSizeOf);
break;
}
case JSTRACE_STRING:
{
JSString *str = static_cast<JSString *>(thing);
curr->gcHeapStrings += thingSize;
curr->stringChars += str->charsHeapSize(data->mallocSizeOf);
break;
}
case JSTRACE_SHAPE:
{
Shape *shape = static_cast<Shape*>(thing);
if (shape->inDictionary()) {
curr->gcHeapShapesDict += thingSize;
curr->shapesExtraDictTables +=
shape->sizeOfPropertyTable(data->mallocSizeOf);
} else {
curr->gcHeapShapesTree += thingSize;
curr->shapesExtraTreeTables +=
shape->sizeOfPropertyTable(data->mallocSizeOf);
curr->shapesExtraTreeShapeKids +=
shape->sizeOfKids(data->mallocSizeOf);
}
break;
}
case JSTRACE_BASE_SHAPE:
{
curr->gcHeapShapesBase += thingSize;
break;
}
case JSTRACE_SCRIPT:
{
JSScript *script = static_cast<JSScript *>(thing);
curr->gcHeapScripts += thingSize;
curr->scriptData += script->dataSize(data->mallocSizeOf);
#ifdef JS_METHODJIT
curr->mjitData += script->jitDataSize(data->mallocSizeOf);
#endif
break;
}
case JSTRACE_TYPE_OBJECT:
{
types::TypeObject *obj = static_cast<types::TypeObject *>(thing);
curr->gcHeapTypeObjects += thingSize;
SizeOfObjectTypeInferenceData(obj, &curr->typeInferenceMemory,
data->mallocSizeOf);
break;
}
case JSTRACE_XML:
{
curr->gcHeapXML += thingSize;
break;
}
}
// Yes, this is a subtraction: see ArenaCallback() for details.
curr->gcHeapArenaUnused -= thingSize;
}
JS_PUBLIC_API(bool)
CollectCompartmentStatsForRuntime(JSRuntime *rt, IterateData *data)
{
JSContext *cx = JS_NewContext(rt, 0);
if (!cx)
return false;
{
JSAutoRequest ar(cx);
if (!data->compartmentStatsVector.reserve(rt->compartments.length()))
return false;
data->gcHeapChunkCleanDecommitted =
rt->gcChunkPool.countCleanDecommittedArenas(rt) *
gc::ArenaSize;
data->gcHeapChunkCleanUnused =
int64_t(JS_GetGCParameter(rt, JSGC_UNUSED_CHUNKS)) *
gc::ChunkSize -
data->gcHeapChunkCleanDecommitted;
data->gcHeapChunkTotal =
int64_t(JS_GetGCParameter(rt, JSGC_TOTAL_CHUNKS)) *
gc::ChunkSize;
IterateCompartmentsArenasCells(cx, data, CompartmentMemoryCallback,
ArenaCallback, CellCallback);
IterateChunks(cx, data, ChunkCallback);
data->runtimeObject = data->mallocSizeOf(rt, sizeof(JSRuntime));
// Nb: we use sizeOfExcludingThis() because atomState.atoms is within
// JSRuntime, and so counted when JSRuntime is counted.
data->runtimeAtomsTable =
rt->atomState.atoms.sizeOfExcludingThis(data->mallocSizeOf);
{
#ifndef JS_THREADSAFE
#error "This code assumes JS_THREADSAFE is defined"
#endif
// Need the GC lock to call JS_ContextIteratorUnlocked() and to
// access rt->threads.
AutoLockGC lock(rt);
JSContext *acx, *iter = NULL;
while ((acx = JS_ContextIteratorUnlocked(rt, &iter)) != NULL) {
data->runtimeContexts +=
acx->sizeOfIncludingThis(data->mallocSizeOf);
}
for (JSThread::Map::Range r = rt->threads.all(); !r.empty(); r.popFront()) {
JSThread *thread = r.front().value;
size_t normal, temporary, regexpCode, stackCommitted;
thread->sizeOfIncludingThis(data->mallocSizeOf,
&normal,
&temporary,
&regexpCode,
&stackCommitted);
data->runtimeThreadsNormal += normal;
data->runtimeThreadsTemporary += temporary;
data->runtimeThreadsRegexpCode += regexpCode;
data->runtimeThreadsStackCommitted += stackCommitted;
}
}
}
JS_DestroyContextNoGC(cx);
// This is initialized to all bytes stored in used chunks, and then we
// subtract used space from it each time around the loop.
data->gcHeapChunkDirtyUnused = data->gcHeapChunkTotal -
data->gcHeapChunkCleanUnused -
data->gcHeapChunkCleanDecommitted -
data->gcHeapChunkDirtyDecommitted;
for (size_t index = 0;
index < data->compartmentStatsVector.length();
index++) {
CompartmentStats &stats = data->compartmentStatsVector[index];
int64_t used = stats.gcHeapArenaHeaders +
stats.gcHeapArenaPadding +
stats.gcHeapArenaUnused +
stats.gcHeapObjectsNonFunction +
stats.gcHeapObjectsFunction +
stats.gcHeapStrings +
stats.gcHeapShapesTree +
stats.gcHeapShapesDict +
stats.gcHeapShapesBase +
stats.gcHeapScripts +
stats.gcHeapTypeObjects +
stats.gcHeapXML;
data->gcHeapChunkDirtyUnused -= used;
data->gcHeapArenaUnused += stats.gcHeapArenaUnused;
data->totalObjects += stats.gcHeapObjectsNonFunction +
stats.gcHeapObjectsFunction +
stats.objectSlots;
data->totalShapes += stats.gcHeapShapesTree +
stats.gcHeapShapesDict +
stats.gcHeapShapesBase +
stats.shapesExtraTreeTables +
stats.shapesExtraDictTables +
stats.shapesCompartmentTables;
data->totalScripts += stats.gcHeapScripts +
stats.scriptData;
data->totalStrings += stats.gcHeapStrings +
stats.stringChars;
#ifdef JS_METHODJIT
data->totalMjit += stats.mjitCode +
stats.mjitData;
#endif
data->totalTypeInference += stats.gcHeapTypeObjects +
stats.typeInferenceMemory.objects +
stats.typeInferenceMemory.scripts +
stats.typeInferenceMemory.tables;
data->totalAnalysisTemp += stats.typeInferenceMemory.temporary;
}
size_t numDirtyChunks = (data->gcHeapChunkTotal -
data->gcHeapChunkCleanUnused) /
gc::ChunkSize;
int64_t perChunkAdmin =
sizeof(gc::Chunk) - (sizeof(gc::Arena) * gc::ArenasPerChunk);
data->gcHeapChunkAdmin = numDirtyChunks * perChunkAdmin;
data->gcHeapChunkDirtyUnused -= data->gcHeapChunkAdmin;
// Why 10000x? 100x because it's a percentage, and another 100x
// because nsIMemoryReporter requires that for percentage amounts so
// they can be fractional.
data->gcHeapUnusedPercentage = (data->gcHeapChunkCleanUnused +
data->gcHeapChunkDirtyUnused +
data->gcHeapChunkCleanDecommitted +
data->gcHeapChunkDirtyDecommitted +
data->gcHeapArenaUnused) * 10000 /
data->gcHeapChunkTotal;
return true;
}
JS_PUBLIC_API(bool)
GetExplicitNonHeapForRuntime(JSRuntime *rt, int64_t *amount,
JSMallocSizeOfFun mallocSizeOf)
{
JSContext *cx = JS_NewContext(rt, 0);
if (!cx)
return false;
// explicit/<compartment>/gc-heap/*
*amount = int64_t(JS_GetGCParameter(rt, JSGC_TOTAL_CHUNKS)) *
gc::ChunkSize;
{
JSAutoRequest ar(cx);
// explicit/<compartment>/mjit-code
size_t n = 0;
IterateCompartments(cx, &n, ExplicitNonHeapCompartmentCallback);
*amount += n;
{
#ifndef JS_THREADSAFE
#error "This code assumes JS_THREADSAFE is defined"
#endif
// Need the GC lock to call JS_ContextIteratorUnlocked() and to
// access rt->threads.
AutoLockGC lock(rt);
// explicit/runtime/threads/regexp-code
// explicit/runtime/threads/stack-committed
for (JSThread::Map::Range r = rt->threads.all(); !r.empty(); r.popFront()) {
JSThread *thread = r.front().value;
size_t regexpCode, stackCommitted;
thread->sizeOfIncludingThis(mallocSizeOf,
NULL,
NULL,
&regexpCode,
&stackCommitted);
*amount += regexpCode;
*amount += stackCommitted;
}
}
}
JS_DestroyContextNoGC(cx);
return true;
}
} // namespace JS

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

@ -156,12 +156,6 @@ JSCompartment::sizeOfMjitCode() const
return method + unused;
}
JS_PUBLIC_API(size_t)
JS::SizeOfCompartmentMjitCode(const JSCompartment *c)
{
return c->sizeOfMjitCode();
}
#endif
bool

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

@ -6599,12 +6599,6 @@ js::HandleNonGenericMethodClassMismatch(JSContext *cx, CallArgs args, Native nat
return false;
}
JS_PUBLIC_API(size_t)
JS::SizeOfObjectDynamicSlots(JSObject *obj, JSMallocSizeOfFun mallocSizeOf)
{
return obj->dynamicSlotSize(mallocSizeOf);
}
#ifdef DEBUG
/*

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

@ -1522,21 +1522,3 @@ JSCompartment::sweepInitialShapeTable(JSContext *cx)
}
}
}
JS_PUBLIC_API(bool)
JS::IsShapeInDictionary(const void *shape)
{
return static_cast<const Shape*>(shape)->inDictionary();
}
JS_PUBLIC_API(size_t)
JS::SizeOfShapePropertyTable(const void *shape, JSMallocSizeOfFun mallocSizeOf)
{
return static_cast<const Shape*>(shape)->sizeOfPropertyTable(mallocSizeOf);
}
JS_PUBLIC_API(size_t)
JS::SizeOfShapeKids(const void *shape, JSMallocSizeOfFun mallocSizeOf)
{
return static_cast<const Shape*>(shape)->sizeOfKids(mallocSizeOf);
}

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

@ -1314,12 +1314,6 @@ JSScript::dataSize(JSMallocSizeOfFun mallocSizeOf)
return mallocSizeOf(data, dataSize());
}
JS_PUBLIC_API(size_t)
JS::SizeOfScriptData(JSScript *script, JSMallocSizeOfFun mallocSizeOf)
{
return script->dataSize(mallocSizeOf);
}
/*
* Nb: srcnotes are variable-length. This function computes the number of
* srcnote *slots*, which may be greater than the number of srcnotes.

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

@ -1297,12 +1297,6 @@ JSScript::jitDataSize(JSMallocSizeOfFun mallocSizeOf)
return n;
}
JS_PUBLIC_API(size_t)
JS::SizeOfScriptJitData(JSScript *script, JSMallocSizeOfFun mallocSizeOf)
{
return script->jitDataSize(mallocSizeOf);
}
/* Please keep in sync with Compiler::finishThisUp! */
size_t
mjit::JITScript::scriptDataSize(JSMallocSizeOfFun mallocSizeOf)

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

@ -77,8 +77,6 @@
JS_LOCK_GC, JS_UNLOCK_GC
js_NextActiveContext, js::TriggerOperationCallback
JSString::charsHeapSize
CollectCompartmentStatsForRuntime
mWatchdogWakeup = JS_NEW_CONDVAR(mJSRuntime->gcLock);
mJSRuntime->setActivityCallback(ActivityCallback, this);
#endif
@ -1254,7 +1252,9 @@ XPCJSRuntime::~XPCJSRuntime()
XPCPerThreadData::ShutDown();
}
static void*
namespace xpc {
void*
GetCompartmentName(JSContext *cx, JSCompartment *c)
{
nsCString* name = new nsCString();
@ -1295,147 +1295,17 @@ GetCompartmentName(JSContext *cx, JSCompartment *c)
return name;
}
static void
void
DestroyCompartmentName(void *string)
{
delete static_cast<nsCString*>(string);
}
namespace {
NS_MEMORY_REPORTER_MALLOC_SIZEOF_FUN(JsMallocSizeOf, "js")
void
CompartmentMemoryCallback(JSContext *cx, void *vdata, JSCompartment *compartment)
{
// Append a new CompartmentStats to the vector.
IterateData *data = static_cast<IterateData *>(vdata);
JS::CompartmentStats *curr =
data->compartmentStatsVector.AppendElement();
curr->init(GetCompartmentName(cx, compartment), DestroyCompartmentName);
data->currCompartmentStats = curr;
} // namespace xpc
// Get the compartment-level numbers.
#ifdef JS_METHODJIT
curr->mjitCode = JS::SizeOfCompartmentMjitCode(compartment);
#endif
JS::SizeOfCompartmentTypeInferenceData(cx, compartment,
&curr->typeInferenceMemory,
JsMallocSizeOf);
curr->shapesCompartmentTables =
JS::SizeOfCompartmentShapeTable(compartment, JsMallocSizeOf);
}
void
ExplicitNonHeapCompartmentCallback(JSContext *cx, void *data, JSCompartment *compartment)
{
size_t *n = static_cast<size_t *>(data);
#ifdef JS_METHODJIT
*n += JS::SizeOfCompartmentMjitCode(compartment);
#endif
}
void
ChunkCallback(JSContext *cx, void *vdata, js::gc::Chunk *chunk)
{
// Nb: This function is only called for dirty chunks, which is why we
// increment gcHeapChunkDirtyDecommitted.
IterateData *data = static_cast<IterateData *>(vdata);
for (uint32_t i = 0; i < js::gc::ArenasPerChunk; i++)
if (chunk->decommittedArenas.get(i))
data->gcHeapChunkDirtyDecommitted += js::gc::ArenaSize;
}
void
ArenaCallback(JSContext *cx, void *vdata, js::gc::Arena *arena,
JSGCTraceKind traceKind, size_t thingSize)
{
IterateData *data = static_cast<IterateData *>(vdata);
data->currCompartmentStats->gcHeapArenaHeaders +=
sizeof(js::gc::ArenaHeader);
size_t allocationSpace = arena->thingsSpan(thingSize);
data->currCompartmentStats->gcHeapArenaPadding +=
js::gc::ArenaSize - allocationSpace - sizeof(js::gc::ArenaHeader);
// We don't call the callback on unused things. So we compute the
// unused space like this: arenaUnused = maxArenaUnused - arenaUsed.
// We do this by setting arenaUnused to maxArenaUnused here, and then
// subtracting thingSize for every used cell, in CellCallback().
data->currCompartmentStats->gcHeapArenaUnused += allocationSpace;
}
void
CellCallback(JSContext *cx, void *vdata, void *thing, JSGCTraceKind traceKind,
size_t thingSize)
{
IterateData *data = static_cast<IterateData *>(vdata);
JS::CompartmentStats *curr = data->currCompartmentStats;
switch (traceKind) {
case JSTRACE_OBJECT:
{
JSObject *obj = static_cast<JSObject *>(thing);
if (JS_ObjectIsFunction(cx, obj)) {
curr->gcHeapObjectsFunction += thingSize;
} else {
curr->gcHeapObjectsNonFunction += thingSize;
}
curr->objectSlots += JS::SizeOfObjectDynamicSlots(obj, JsMallocSizeOf);
break;
}
case JSTRACE_STRING:
{
JSString *str = static_cast<JSString *>(thing);
curr->gcHeapStrings += thingSize;
curr->stringChars += str->charsHeapSize(JsMallocSizeOf);
break;
}
case JSTRACE_SHAPE:
{
if (JS::IsShapeInDictionary(thing)) {
curr->gcHeapShapesDict += thingSize;
curr->shapesExtraDictTables +=
JS::SizeOfShapePropertyTable(thing, JsMallocSizeOf);
} else {
curr->gcHeapShapesTree += thingSize;
curr->shapesExtraTreeTables +=
JS::SizeOfShapePropertyTable(thing, JsMallocSizeOf);
curr->shapesExtraTreeShapeKids +=
JS::SizeOfShapeKids(thing, JsMallocSizeOf);
}
break;
}
case JSTRACE_BASE_SHAPE:
{
curr->gcHeapShapesBase += thingSize;
break;
}
case JSTRACE_SCRIPT:
{
JSScript *script = static_cast<JSScript *>(thing);
curr->gcHeapScripts += thingSize;
curr->scriptData += JS::SizeOfScriptData(script, JsMallocSizeOf);
#ifdef JS_METHODJIT
curr->mjitData += JS::SizeOfScriptJitData(script, JsMallocSizeOf);
#endif
break;
}
case JSTRACE_TYPE_OBJECT:
{
js::types::TypeObject *obj = static_cast<js::types::TypeObject *>(thing);
curr->gcHeapTypeObjects += thingSize;
JS::SizeOfObjectTypeInferenceData(obj, &curr->typeInferenceMemory,
JsMallocSizeOf);
break;
}
case JSTRACE_XML:
{
curr->gcHeapXML += thingSize;
break;
}
}
// Yes, this is a subtraction: see ArenaCallback() for details.
curr->gcHeapArenaUnused -= thingSize;
}
namespace {
template <int N>
inline void
@ -1577,209 +1447,6 @@ namespace mozilla {
namespace xpconnect {
namespace memory {
JSBool
CollectCompartmentStatsForRuntime(JSRuntime *rt, void *vdata)
{
IterateData *data = (IterateData *)vdata;
JSContext *cx = JS_NewContext(rt, 0);
if (!cx) {
NS_ERROR("couldn't create context for memory tracing");
return false;
}
{
JSAutoRequest ar(cx);
data->compartmentStatsVector.SetCapacity(rt->compartments.length());
data->gcHeapChunkCleanDecommitted =
rt->gcChunkPool.countCleanDecommittedArenas(rt) *
js::gc::ArenaSize;
data->gcHeapChunkCleanUnused =
PRInt64(JS_GetGCParameter(rt, JSGC_UNUSED_CHUNKS)) *
js::gc::ChunkSize -
data->gcHeapChunkCleanDecommitted;
data->gcHeapChunkTotal =
PRInt64(JS_GetGCParameter(rt, JSGC_TOTAL_CHUNKS)) *
js::gc::ChunkSize;
js::IterateCompartmentsArenasCells(cx, data, CompartmentMemoryCallback,
ArenaCallback, CellCallback);
js::IterateChunks(cx, data, ChunkCallback);
data->runtimeObject = JsMallocSizeOf(rt, sizeof(JSRuntime));
// Nb: we use sizeOfExcludingThis() because atomState.atoms is within
// JSRuntime, and so counted when JSRuntime is counted.
data->runtimeAtomsTable =
rt->atomState.atoms.sizeOfExcludingThis(JsMallocSizeOf);
{
#ifndef JS_THREADSAFE
#error "This code assumes JS_THREADSAFE is defined"
#endif
// Need the GC lock to call JS_ContextIteratorUnlocked() and to
// access rt->threads.
js::AutoLockGC lock(rt);
JSContext *acx, *iter = NULL;
while ((acx = JS_ContextIteratorUnlocked(rt, &iter)) != NULL) {
data->runtimeContexts +=
acx->sizeOfIncludingThis(JsMallocSizeOf);
}
for (JSThread::Map::Range r = rt->threads.all(); !r.empty(); r.popFront()) {
JSThread *thread = r.front().value;
size_t normal, temporary, regexpCode, stackCommitted;
thread->sizeOfIncludingThis(JsMallocSizeOf,
&normal,
&temporary,
&regexpCode,
&stackCommitted);
data->runtimeThreadsNormal += normal;
data->runtimeThreadsTemporary += temporary;
data->runtimeThreadsRegexpCode += regexpCode;
data->runtimeThreadsStackCommitted += stackCommitted;
}
}
XPCJSRuntime *xpcrt = nsXPConnect::GetRuntimeInstance();
data->xpconnect +=
xpcrt->SizeOfIncludingThis(JsMallocSizeOf);
data->xpconnect +=
XPCWrappedNativeScope::SizeOfAllScopesIncludingThis(JsMallocSizeOf);
}
JS_DestroyContextNoGC(cx);
// This is initialized to all bytes stored in used chunks, and then we
// subtract used space from it each time around the loop.
data->gcHeapChunkDirtyUnused = data->gcHeapChunkTotal -
data->gcHeapChunkCleanUnused -
data->gcHeapChunkCleanDecommitted -
data->gcHeapChunkDirtyDecommitted;
for (PRUint32 index = 0;
index < data->compartmentStatsVector.Length();
index++) {
JS::CompartmentStats &stats = data->compartmentStatsVector[index];
PRInt64 used = stats.gcHeapArenaHeaders +
stats.gcHeapArenaPadding +
stats.gcHeapArenaUnused +
stats.gcHeapObjectsNonFunction +
stats.gcHeapObjectsFunction +
stats.gcHeapStrings +
stats.gcHeapShapesTree +
stats.gcHeapShapesDict +
stats.gcHeapShapesBase +
stats.gcHeapScripts +
stats.gcHeapTypeObjects +
stats.gcHeapXML;
data->gcHeapChunkDirtyUnused -= used;
data->gcHeapArenaUnused += stats.gcHeapArenaUnused;
data->totalObjects += stats.gcHeapObjectsNonFunction +
stats.gcHeapObjectsFunction +
stats.objectSlots;
data->totalShapes += stats.gcHeapShapesTree +
stats.gcHeapShapesDict +
stats.gcHeapShapesBase +
stats.shapesExtraTreeTables +
stats.shapesExtraDictTables +
stats.shapesCompartmentTables;
data->totalScripts += stats.gcHeapScripts +
stats.scriptData;
data->totalStrings += stats.gcHeapStrings +
stats.stringChars;
#ifdef JS_METHODJIT
data->totalMjit += stats.mjitCode +
stats.mjitData;
#endif
data->totalTypeInference += stats.gcHeapTypeObjects +
stats.typeInferenceMemory.objects +
stats.typeInferenceMemory.scripts +
stats.typeInferenceMemory.tables;
data->totalAnalysisTemp += stats.typeInferenceMemory.temporary;
}
size_t numDirtyChunks = (data->gcHeapChunkTotal -
data->gcHeapChunkCleanUnused) /
js::gc::ChunkSize;
PRInt64 perChunkAdmin =
sizeof(js::gc::Chunk) - (sizeof(js::gc::Arena) * js::gc::ArenasPerChunk);
data->gcHeapChunkAdmin = numDirtyChunks * perChunkAdmin;
data->gcHeapChunkDirtyUnused -= data->gcHeapChunkAdmin;
// Why 10000x? 100x because it's a percentage, and another 100x
// because nsIMemoryReporter requires that for percentage amounts so
// they can be fractional.
data->gcHeapUnusedPercentage = (data->gcHeapChunkCleanUnused +
data->gcHeapChunkDirtyUnused +
data->gcHeapChunkCleanDecommitted +
data->gcHeapChunkDirtyDecommitted +
data->gcHeapArenaUnused) * 10000 /
data->gcHeapChunkTotal;
return true;
}
JSBool
GetExplicitNonHeapForRuntime(JSRuntime *rt, void *data)
{
PRInt64 *amount = (PRInt64 *)data;
JSContext *cx = JS_NewContext(rt, 0);
if (!cx) {
NS_ERROR("couldn't create context for memory tracing");
return NS_ERROR_ABORT;
}
// explicit/<compartment>/gc-heap/*
*amount = PRInt64(JS_GetGCParameter(rt, JSGC_TOTAL_CHUNKS)) *
js::gc::ChunkSize;
{
JSAutoRequest ar(cx);
// explicit/<compartment>/mjit-code
size_t n = 0;
js::IterateCompartments(cx, &n, ExplicitNonHeapCompartmentCallback);
*amount += n;
{
#ifndef JS_THREADSAFE
#error "This code assumes JS_THREADSAFE is defined"
#endif
// Need the GC lock to call JS_ContextIteratorUnlocked() and to
// access rt->threads.
js::AutoLockGC lock(rt);
// explicit/runtime/threads/regexp-code
// explicit/runtime/threads/stack-committed
for (JSThread::Map::Range r = rt->threads.all(); !r.empty(); r.popFront()) {
JSThread *thread = r.front().value;
size_t regexpCode, stackCommitted;
thread->sizeOfIncludingThis(JsMallocSizeOf,
NULL,
NULL,
&regexpCode,
&stackCommitted);
*amount += regexpCode;
*amount += stackCommitted;
}
}
}
JS_DestroyContextNoGC(cx);
return true;
}
#define SLOP_BYTES_STRING \
" The measurement includes slop bytes caused by the heap allocator rounding up request sizes."
@ -1985,13 +1652,13 @@ ReportCompartmentStats(const JS::CompartmentStats &stats,
}
void
ReportJSRuntimeStats(const IterateData &data, const nsACString &pathPrefix,
ReportJSRuntimeStats(const JS::IterateData &data, const nsACString &pathPrefix,
nsIMemoryMultiReporterCallback *callback,
nsISupports *closure)
{
PRInt64 gcTotal = 0;
for (PRUint32 index = 0;
index < data.compartmentStatsVector.Length();
for (size_t index = 0;
index < data.compartmentStatsVector.length();
index++) {
gcTotal += ReportCompartmentStats(data.compartmentStatsVector[index], pathPrefix,
callback, closure);
@ -2039,11 +1706,6 @@ ReportJSRuntimeStats(const IterateData &data, const nsACString &pathPrefix,
"hardly cost anything.",
callback, closure);
ReportMemoryBytes(pathPrefix + NS_LITERAL_CSTRING("xpconnect"),
nsIMemoryReporter::KIND_HEAP, data.xpconnect,
"Memory used by XPConnect." SLOP_BYTES_STRING,
callback, closure);
ReportGCHeapBytes(pathPrefix +
NS_LITERAL_CSTRING("gc-heap-chunk-dirty-unused"),
&gcTotal, data.gcHeapChunkDirtyUnused,
@ -2094,22 +1756,35 @@ public:
NS_IMETHOD CollectReports(nsIMemoryMultiReporterCallback *callback,
nsISupports *closure)
{
JSRuntime *rt = nsXPConnect::GetRuntimeInstance()->GetJSRuntime();
XPCJSRuntime *xpcrt = nsXPConnect::GetRuntimeInstance();
// In the first step we get all the stats and stash them in a local
// data structure. In the second step we pass all the stashed stats to
// the callback. Separating these steps is important because the
// callback may be a JS function, and executing JS while getting these
// stats seems like a bad idea.
IterateData data;
if (!CollectCompartmentStatsForRuntime(rt, &data))
JS::IterateData data(xpc::JsMallocSizeOf, xpc::GetCompartmentName,
xpc::DestroyCompartmentName);
if (!JS::CollectCompartmentStatsForRuntime(xpcrt->GetJSRuntime(), &data))
return NS_ERROR_FAILURE;
uint64_t xpconnect;
{
xpconnect =
xpcrt->SizeOfIncludingThis(xpc::JsMallocSizeOf) +
XPCWrappedNativeScope::SizeOfAllScopesIncludingThis(xpc::JsMallocSizeOf);
}
NS_NAMED_LITERAL_CSTRING(pathPrefix, "explicit/js/");
// This is the second step (see above).
ReportJSRuntimeStats(data, pathPrefix, callback, closure);
ReportMemoryBytes(pathPrefix + NS_LITERAL_CSTRING("xpconnect"),
nsIMemoryReporter::KIND_HEAP, xpconnect,
"Memory used by XPConnect." SLOP_BYTES_STRING,
callback, closure);
ReportMemoryBytes(NS_LITERAL_CSTRING("js-gc-heap-chunk-dirty-unused"),
nsIMemoryReporter::KIND_OTHER,
data.gcHeapChunkDirtyUnused,
@ -2203,7 +1878,7 @@ public:
{
JSRuntime *rt = nsXPConnect::GetRuntimeInstance()->GetJSRuntime();
if (!GetExplicitNonHeapForRuntime(rt, n))
if (!JS::GetExplicitNonHeapForRuntime(rt, n, xpc::JsMallocSizeOf))
return NS_ERROR_FAILURE;
return NS_OK;

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

@ -207,6 +207,10 @@ bool Base64Decode(JSContext *cx, JS::Value val, JS::Value *out);
*/
bool StringToJsval(JSContext *cx, nsString &str, JS::Value *rval);
void *GetCompartmentName(JSContext *cx, JSCompartment *c);
void DestroyCompartmentName(void *string);
size_t JsMallocSizeOf(const void *ptr, size_t computedSize);
} // namespace xpc
class nsIMemoryMultiReporterCallback;
@ -215,74 +219,8 @@ namespace mozilla {
namespace xpconnect {
namespace memory {
struct IterateData
{
IterateData()
: runtimeObject(0),
runtimeAtomsTable(0),
runtimeContexts(0),
runtimeThreadsNormal(0),
runtimeThreadsTemporary(0),
runtimeThreadsRegexpCode(0),
runtimeThreadsStackCommitted(0),
xpconnect(0),
gcHeapChunkTotal(0),
gcHeapChunkCleanUnused(0),
gcHeapChunkDirtyUnused(0),
gcHeapChunkCleanDecommitted(0),
gcHeapChunkDirtyDecommitted(0),
gcHeapArenaUnused(0),
gcHeapChunkAdmin(0),
gcHeapUnusedPercentage(0),
totalObjects(0),
totalShapes(0),
totalScripts(0),
totalStrings(0),
#ifdef JS_METHODJIT
totalMjit(0),
#endif
totalTypeInference(0),
totalAnalysisTemp(0),
compartmentStatsVector(),
currCompartmentStats(NULL) { }
PRInt64 runtimeObject;
PRInt64 runtimeAtomsTable;
PRInt64 runtimeContexts;
PRInt64 runtimeThreadsNormal;
PRInt64 runtimeThreadsTemporary;
PRInt64 runtimeThreadsRegexpCode;
PRInt64 runtimeThreadsStackCommitted;
PRInt64 xpconnect;
PRInt64 gcHeapChunkTotal;
PRInt64 gcHeapChunkCleanUnused;
PRInt64 gcHeapChunkDirtyUnused;
PRInt64 gcHeapChunkCleanDecommitted;
PRInt64 gcHeapChunkDirtyDecommitted;
PRInt64 gcHeapArenaUnused;
PRInt64 gcHeapChunkAdmin;
PRInt64 gcHeapUnusedPercentage;
PRInt64 totalObjects;
PRInt64 totalShapes;
PRInt64 totalScripts;
PRInt64 totalStrings;
#ifdef JS_METHODJIT
PRInt64 totalMjit;
#endif
PRInt64 totalTypeInference;
PRInt64 totalAnalysisTemp;
nsTArray<JS::CompartmentStats> compartmentStatsVector;
JS::CompartmentStats *currCompartmentStats;
};
JSBool
CollectCompartmentStatsForRuntime(JSRuntime *rt, void *data);
JSBool
GetExplicitNonHeapForRuntime(JSRuntime *rt, void *data);
void
ReportJSRuntimeStats(const IterateData &data, const nsACString &pathPrefix,
ReportJSRuntimeStats(const JS::IterateData &data, const nsACString &pathPrefix,
nsIMemoryMultiReporterCallback *callback,
nsISupports *closure);