зеркало из https://github.com/mozilla/pjs.git
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:
Родитель
4dabf76c50
Коммит
555fdc26d7
|
@ -59,6 +59,8 @@
|
||||||
#include "jscntxt.h"
|
#include "jscntxt.h"
|
||||||
#include "jsdbgapi.h"
|
#include "jsdbgapi.h"
|
||||||
#include "jsprf.h"
|
#include "jsprf.h"
|
||||||
|
#include "js/MemoryMetrics.h"
|
||||||
|
|
||||||
#include "nsAlgorithm.h"
|
#include "nsAlgorithm.h"
|
||||||
#include "nsContentUtils.h"
|
#include "nsContentUtils.h"
|
||||||
#include "nsDOMClassInfo.h"
|
#include "nsDOMClassInfo.h"
|
||||||
|
@ -93,7 +95,7 @@ using mozilla::MutexAutoLock;
|
||||||
using mozilla::TimeDuration;
|
using mozilla::TimeDuration;
|
||||||
using mozilla::TimeStamp;
|
using mozilla::TimeStamp;
|
||||||
using mozilla::dom::workers::exceptions::ThrowDOMExceptionForCode;
|
using mozilla::dom::workers::exceptions::ThrowDOMExceptionForCode;
|
||||||
using mozilla::xpconnect::memory::IterateData;
|
using mozilla::xpconnect::memory::ReportJSRuntimeStats;
|
||||||
|
|
||||||
USING_WORKERS_NAMESPACE
|
USING_WORKERS_NAMESPACE
|
||||||
|
|
||||||
|
@ -223,7 +225,8 @@ public:
|
||||||
{
|
{
|
||||||
AssertIsOnMainThread();
|
AssertIsOnMainThread();
|
||||||
|
|
||||||
IterateData data;
|
JS::IterateData data(xpc::JsMallocSizeOf, xpc::GetCompartmentName,
|
||||||
|
xpc::DestroyCompartmentName);
|
||||||
nsresult rv = CollectForRuntime(/* isQuick = */false, &data);
|
nsresult rv = CollectForRuntime(/* isQuick = */false, &data);
|
||||||
if (NS_FAILED(rv)) {
|
if (NS_FAILED(rv)) {
|
||||||
return rv;
|
return rv;
|
||||||
|
@ -1475,9 +1478,9 @@ public:
|
||||||
{
|
{
|
||||||
JSAutoSuspendRequest asr(aCx);
|
JSAutoSuspendRequest asr(aCx);
|
||||||
|
|
||||||
*mSucceeded = mIsQuick ?
|
*mSucceeded = mIsQuick
|
||||||
mozilla::xpconnect::memory::GetExplicitNonHeapForRuntime(JS_GetRuntime(aCx), mData) :
|
? JS::GetExplicitNonHeapForRuntime(JS_GetRuntime(aCx), static_cast<int64_t*>(mData), xpc::JsMallocSizeOf)
|
||||||
mozilla::xpconnect::memory::CollectCompartmentStatsForRuntime(JS_GetRuntime(aCx), mData);
|
: JS::CollectCompartmentStatsForRuntime(JS_GetRuntime(aCx), static_cast<JS::IterateData*>(mData));
|
||||||
|
|
||||||
{
|
{
|
||||||
MutexAutoLock lock(mMutex);
|
MutexAutoLock lock(mMutex);
|
||||||
|
|
|
@ -69,16 +69,6 @@ class nsIURI;
|
||||||
class nsPIDOMWindow;
|
class nsPIDOMWindow;
|
||||||
class nsITimer;
|
class nsITimer;
|
||||||
|
|
||||||
namespace mozilla {
|
|
||||||
namespace xpconnect {
|
|
||||||
namespace memory {
|
|
||||||
|
|
||||||
struct IterateData;
|
|
||||||
|
|
||||||
} // namespace memory
|
|
||||||
} // namespace xpconnect
|
|
||||||
} // namespace mozilla
|
|
||||||
|
|
||||||
BEGIN_WORKERS_NAMESPACE
|
BEGIN_WORKERS_NAMESPACE
|
||||||
|
|
||||||
class WorkerPrivate;
|
class WorkerPrivate;
|
||||||
|
|
|
@ -44,9 +44,11 @@
|
||||||
|
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "jsalloc.h"
|
||||||
#include "jspubtd.h"
|
#include "jspubtd.h"
|
||||||
|
|
||||||
#include "js/Utility.h"
|
#include "js/Utility.h"
|
||||||
|
#include "js/Vector.h"
|
||||||
|
|
||||||
namespace JS {
|
namespace JS {
|
||||||
|
|
||||||
|
@ -59,6 +61,7 @@ struct TypeInferenceMemoryStats
|
||||||
int64_t temporary;
|
int64_t temporary;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
typedef void* (* GetNameCallback)(JSContext *cx, JSCompartment *c);
|
||||||
typedef void (* DestroyNameCallback)(void *string);
|
typedef void (* DestroyNameCallback)(void *string);
|
||||||
|
|
||||||
struct CompartmentStats
|
struct CompartmentStats
|
||||||
|
@ -112,6 +115,81 @@ struct CompartmentStats
|
||||||
TypeInferenceMemoryStats typeInferenceMemory;
|
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)
|
extern JS_PUBLIC_API(void)
|
||||||
SizeOfCompartmentTypeInferenceData(JSContext *cx, JSCompartment *compartment,
|
SizeOfCompartmentTypeInferenceData(JSContext *cx, JSCompartment *compartment,
|
||||||
TypeInferenceMemoryStats *stats,
|
TypeInferenceMemoryStats *stats,
|
||||||
|
@ -122,32 +200,9 @@ SizeOfObjectTypeInferenceData(/*TypeObject*/ void *object,
|
||||||
TypeInferenceMemoryStats *stats,
|
TypeInferenceMemoryStats *stats,
|
||||||
JSMallocSizeOfFun mallocSizeOf);
|
JSMallocSizeOfFun mallocSizeOf);
|
||||||
|
|
||||||
extern JS_PUBLIC_API(size_t)
|
|
||||||
SizeOfObjectDynamicSlots(JSObject *obj, JSMallocSizeOfFun mallocSizeOf);
|
|
||||||
|
|
||||||
extern JS_PUBLIC_API(size_t)
|
extern JS_PUBLIC_API(size_t)
|
||||||
SizeOfCompartmentShapeTable(JSCompartment *c, JSMallocSizeOfFun mallocSizeOf);
|
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)
|
extern JS_PUBLIC_API(size_t)
|
||||||
SystemCompartmentCount(const JSRuntime *rt);
|
SystemCompartmentCount(const JSRuntime *rt);
|
||||||
|
|
||||||
|
|
|
@ -171,6 +171,7 @@ CPPSRCS = \
|
||||||
SemanticAnalysis.cpp \
|
SemanticAnalysis.cpp \
|
||||||
TokenStream.cpp \
|
TokenStream.cpp \
|
||||||
LifoAlloc.cpp \
|
LifoAlloc.cpp \
|
||||||
|
MemoryMetrics.cpp \
|
||||||
RegExpObject.cpp \
|
RegExpObject.cpp \
|
||||||
RegExpStatics.cpp \
|
RegExpStatics.cpp \
|
||||||
RegExp.cpp \
|
RegExp.cpp \
|
||||||
|
|
|
@ -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,
|
||||||
|
®expCode,
|
||||||
|
&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,
|
||||||
|
®expCode,
|
||||||
|
&stackCommitted);
|
||||||
|
|
||||||
|
*amount += regexpCode;
|
||||||
|
*amount += stackCommitted;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
JS_DestroyContextNoGC(cx);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace JS
|
|
@ -156,12 +156,6 @@ JSCompartment::sizeOfMjitCode() const
|
||||||
return method + unused;
|
return method + unused;
|
||||||
}
|
}
|
||||||
|
|
||||||
JS_PUBLIC_API(size_t)
|
|
||||||
JS::SizeOfCompartmentMjitCode(const JSCompartment *c)
|
|
||||||
{
|
|
||||||
return c->sizeOfMjitCode();
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
bool
|
bool
|
||||||
|
|
|
@ -6599,12 +6599,6 @@ js::HandleNonGenericMethodClassMismatch(JSContext *cx, CallArgs args, Native nat
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
JS_PUBLIC_API(size_t)
|
|
||||||
JS::SizeOfObjectDynamicSlots(JSObject *obj, JSMallocSizeOfFun mallocSizeOf)
|
|
||||||
{
|
|
||||||
return obj->dynamicSlotSize(mallocSizeOf);
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef DEBUG
|
#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());
|
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
|
* Nb: srcnotes are variable-length. This function computes the number of
|
||||||
* srcnote *slots*, which may be greater than the number of srcnotes.
|
* srcnote *slots*, which may be greater than the number of srcnotes.
|
||||||
|
|
|
@ -1297,12 +1297,6 @@ JSScript::jitDataSize(JSMallocSizeOfFun mallocSizeOf)
|
||||||
return n;
|
return n;
|
||||||
}
|
}
|
||||||
|
|
||||||
JS_PUBLIC_API(size_t)
|
|
||||||
JS::SizeOfScriptJitData(JSScript *script, JSMallocSizeOfFun mallocSizeOf)
|
|
||||||
{
|
|
||||||
return script->jitDataSize(mallocSizeOf);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Please keep in sync with Compiler::finishThisUp! */
|
/* Please keep in sync with Compiler::finishThisUp! */
|
||||||
size_t
|
size_t
|
||||||
mjit::JITScript::scriptDataSize(JSMallocSizeOfFun mallocSizeOf)
|
mjit::JITScript::scriptDataSize(JSMallocSizeOfFun mallocSizeOf)
|
||||||
|
|
|
@ -77,8 +77,6 @@
|
||||||
JS_LOCK_GC, JS_UNLOCK_GC
|
JS_LOCK_GC, JS_UNLOCK_GC
|
||||||
|
|
||||||
js_NextActiveContext, js::TriggerOperationCallback
|
js_NextActiveContext, js::TriggerOperationCallback
|
||||||
JSString::charsHeapSize
|
|
||||||
CollectCompartmentStatsForRuntime
|
|
||||||
mWatchdogWakeup = JS_NEW_CONDVAR(mJSRuntime->gcLock);
|
mWatchdogWakeup = JS_NEW_CONDVAR(mJSRuntime->gcLock);
|
||||||
mJSRuntime->setActivityCallback(ActivityCallback, this);
|
mJSRuntime->setActivityCallback(ActivityCallback, this);
|
||||||
#endif
|
#endif
|
||||||
|
@ -1254,7 +1252,9 @@ XPCJSRuntime::~XPCJSRuntime()
|
||||||
XPCPerThreadData::ShutDown();
|
XPCPerThreadData::ShutDown();
|
||||||
}
|
}
|
||||||
|
|
||||||
static void*
|
namespace xpc {
|
||||||
|
|
||||||
|
void*
|
||||||
GetCompartmentName(JSContext *cx, JSCompartment *c)
|
GetCompartmentName(JSContext *cx, JSCompartment *c)
|
||||||
{
|
{
|
||||||
nsCString* name = new nsCString();
|
nsCString* name = new nsCString();
|
||||||
|
@ -1295,147 +1295,17 @@ GetCompartmentName(JSContext *cx, JSCompartment *c)
|
||||||
return name;
|
return name;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
void
|
||||||
DestroyCompartmentName(void *string)
|
DestroyCompartmentName(void *string)
|
||||||
{
|
{
|
||||||
delete static_cast<nsCString*>(string);
|
delete static_cast<nsCString*>(string);
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace {
|
|
||||||
|
|
||||||
NS_MEMORY_REPORTER_MALLOC_SIZEOF_FUN(JsMallocSizeOf, "js")
|
NS_MEMORY_REPORTER_MALLOC_SIZEOF_FUN(JsMallocSizeOf, "js")
|
||||||
|
|
||||||
void
|
} // namespace xpc
|
||||||
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;
|
|
||||||
|
|
||||||
// Get the compartment-level numbers.
|
namespace {
|
||||||
#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;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <int N>
|
template <int N>
|
||||||
inline void
|
inline void
|
||||||
|
@ -1577,209 +1447,6 @@ namespace mozilla {
|
||||||
namespace xpconnect {
|
namespace xpconnect {
|
||||||
namespace memory {
|
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,
|
|
||||||
®expCode,
|
|
||||||
&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,
|
|
||||||
®expCode,
|
|
||||||
&stackCommitted);
|
|
||||||
|
|
||||||
*amount += regexpCode;
|
|
||||||
*amount += stackCommitted;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
JS_DestroyContextNoGC(cx);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
#define SLOP_BYTES_STRING \
|
#define SLOP_BYTES_STRING \
|
||||||
" The measurement includes slop bytes caused by the heap allocator rounding up request sizes."
|
" The measurement includes slop bytes caused by the heap allocator rounding up request sizes."
|
||||||
|
|
||||||
|
@ -1985,13 +1652,13 @@ ReportCompartmentStats(const JS::CompartmentStats &stats,
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
ReportJSRuntimeStats(const IterateData &data, const nsACString &pathPrefix,
|
ReportJSRuntimeStats(const JS::IterateData &data, const nsACString &pathPrefix,
|
||||||
nsIMemoryMultiReporterCallback *callback,
|
nsIMemoryMultiReporterCallback *callback,
|
||||||
nsISupports *closure)
|
nsISupports *closure)
|
||||||
{
|
{
|
||||||
PRInt64 gcTotal = 0;
|
PRInt64 gcTotal = 0;
|
||||||
for (PRUint32 index = 0;
|
for (size_t index = 0;
|
||||||
index < data.compartmentStatsVector.Length();
|
index < data.compartmentStatsVector.length();
|
||||||
index++) {
|
index++) {
|
||||||
gcTotal += ReportCompartmentStats(data.compartmentStatsVector[index], pathPrefix,
|
gcTotal += ReportCompartmentStats(data.compartmentStatsVector[index], pathPrefix,
|
||||||
callback, closure);
|
callback, closure);
|
||||||
|
@ -2039,11 +1706,6 @@ ReportJSRuntimeStats(const IterateData &data, const nsACString &pathPrefix,
|
||||||
"hardly cost anything.",
|
"hardly cost anything.",
|
||||||
callback, closure);
|
callback, closure);
|
||||||
|
|
||||||
ReportMemoryBytes(pathPrefix + NS_LITERAL_CSTRING("xpconnect"),
|
|
||||||
nsIMemoryReporter::KIND_HEAP, data.xpconnect,
|
|
||||||
"Memory used by XPConnect." SLOP_BYTES_STRING,
|
|
||||||
callback, closure);
|
|
||||||
|
|
||||||
ReportGCHeapBytes(pathPrefix +
|
ReportGCHeapBytes(pathPrefix +
|
||||||
NS_LITERAL_CSTRING("gc-heap-chunk-dirty-unused"),
|
NS_LITERAL_CSTRING("gc-heap-chunk-dirty-unused"),
|
||||||
&gcTotal, data.gcHeapChunkDirtyUnused,
|
&gcTotal, data.gcHeapChunkDirtyUnused,
|
||||||
|
@ -2094,22 +1756,35 @@ public:
|
||||||
NS_IMETHOD CollectReports(nsIMemoryMultiReporterCallback *callback,
|
NS_IMETHOD CollectReports(nsIMemoryMultiReporterCallback *callback,
|
||||||
nsISupports *closure)
|
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
|
// 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
|
// data structure. In the second step we pass all the stashed stats to
|
||||||
// the callback. Separating these steps is important because the
|
// the callback. Separating these steps is important because the
|
||||||
// callback may be a JS function, and executing JS while getting these
|
// callback may be a JS function, and executing JS while getting these
|
||||||
// stats seems like a bad idea.
|
// stats seems like a bad idea.
|
||||||
IterateData data;
|
JS::IterateData data(xpc::JsMallocSizeOf, xpc::GetCompartmentName,
|
||||||
if (!CollectCompartmentStatsForRuntime(rt, &data))
|
xpc::DestroyCompartmentName);
|
||||||
|
if (!JS::CollectCompartmentStatsForRuntime(xpcrt->GetJSRuntime(), &data))
|
||||||
return NS_ERROR_FAILURE;
|
return NS_ERROR_FAILURE;
|
||||||
|
|
||||||
|
uint64_t xpconnect;
|
||||||
|
{
|
||||||
|
xpconnect =
|
||||||
|
xpcrt->SizeOfIncludingThis(xpc::JsMallocSizeOf) +
|
||||||
|
XPCWrappedNativeScope::SizeOfAllScopesIncludingThis(xpc::JsMallocSizeOf);
|
||||||
|
}
|
||||||
|
|
||||||
NS_NAMED_LITERAL_CSTRING(pathPrefix, "explicit/js/");
|
NS_NAMED_LITERAL_CSTRING(pathPrefix, "explicit/js/");
|
||||||
|
|
||||||
// This is the second step (see above).
|
// This is the second step (see above).
|
||||||
ReportJSRuntimeStats(data, pathPrefix, callback, closure);
|
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"),
|
ReportMemoryBytes(NS_LITERAL_CSTRING("js-gc-heap-chunk-dirty-unused"),
|
||||||
nsIMemoryReporter::KIND_OTHER,
|
nsIMemoryReporter::KIND_OTHER,
|
||||||
data.gcHeapChunkDirtyUnused,
|
data.gcHeapChunkDirtyUnused,
|
||||||
|
@ -2203,7 +1878,7 @@ public:
|
||||||
{
|
{
|
||||||
JSRuntime *rt = nsXPConnect::GetRuntimeInstance()->GetJSRuntime();
|
JSRuntime *rt = nsXPConnect::GetRuntimeInstance()->GetJSRuntime();
|
||||||
|
|
||||||
if (!GetExplicitNonHeapForRuntime(rt, n))
|
if (!JS::GetExplicitNonHeapForRuntime(rt, n, xpc::JsMallocSizeOf))
|
||||||
return NS_ERROR_FAILURE;
|
return NS_ERROR_FAILURE;
|
||||||
|
|
||||||
return NS_OK;
|
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);
|
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
|
} // namespace xpc
|
||||||
|
|
||||||
class nsIMemoryMultiReporterCallback;
|
class nsIMemoryMultiReporterCallback;
|
||||||
|
@ -215,74 +219,8 @@ namespace mozilla {
|
||||||
namespace xpconnect {
|
namespace xpconnect {
|
||||||
namespace memory {
|
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
|
void
|
||||||
ReportJSRuntimeStats(const IterateData &data, const nsACString &pathPrefix,
|
ReportJSRuntimeStats(const JS::IterateData &data, const nsACString &pathPrefix,
|
||||||
nsIMemoryMultiReporterCallback *callback,
|
nsIMemoryMultiReporterCallback *callback,
|
||||||
nsISupports *closure);
|
nsISupports *closure);
|
||||||
|
|
||||||
|
|
Загрузка…
Ссылка в новой задаче