зеркало из https://github.com/mozilla/gecko-dev.git
Bug 633653 - revamp about:memory. r=vlad,sdwilsh,dvander,gavin,ehsan,edwsmith; sr=benjamin.
This commit is contained in:
Родитель
2a37b38b71
Коммит
020b76968f
|
@ -173,8 +173,9 @@ static PRInt64 GetCanvasMemoryUsed(void *) {
|
|||
}
|
||||
|
||||
NS_MEMORY_REPORTER_IMPLEMENT(CanvasMemory,
|
||||
"content/canvas/2d_pixel_bytes",
|
||||
"Total memory used by 2D canvas (width * height * 4)",
|
||||
"heap-used/content/canvas/2d-pixel-bytes",
|
||||
"Memory used by 2D canvases. Each canvas "
|
||||
"requires (width * height * 4) bytes.",
|
||||
GetCanvasMemoryUsed,
|
||||
NULL)
|
||||
|
||||
|
|
|
@ -319,7 +319,9 @@ ContentChild::RecvPMemoryReportRequestConstructor(PMemoryReportRequestChild* chi
|
|||
report->GetDescription(getter_Copies(desc));
|
||||
report->GetMemoryUsed(&memoryUsed);
|
||||
|
||||
MemoryReport memreport(nsPrintfCString("Content Process - %d - ", getpid()),
|
||||
static const int maxLength = 31; // big enough; pid is only a few chars
|
||||
MemoryReport memreport(nsPrintfCString(maxLength, "Content (%d)",
|
||||
getpid()),
|
||||
path,
|
||||
desc,
|
||||
memoryUsed);
|
||||
|
|
|
@ -521,30 +521,30 @@ gfxASurface::MovePixels(const nsIntRect& aSourceRect,
|
|||
/** Memory reporting **/
|
||||
|
||||
static const char *sSurfaceNamesForSurfaceType[] = {
|
||||
"gfx/surface/image",
|
||||
"gfx/surface/pdf",
|
||||
"gfx/surface/ps",
|
||||
"gfx/surface/xlib",
|
||||
"gfx/surface/xcb",
|
||||
"gfx/surface/glitz",
|
||||
"gfx/surface/quartz",
|
||||
"gfx/surface/win32",
|
||||
"gfx/surface/beos",
|
||||
"gfx/surface/directfb",
|
||||
"gfx/surface/svg",
|
||||
"gfx/surface/os2",
|
||||
"gfx/surface/win32printing",
|
||||
"gfx/surface/quartzimage",
|
||||
"gfx/surface/script",
|
||||
"gfx/surface/qpainter",
|
||||
"gfx/surface/recording",
|
||||
"gfx/surface/vg",
|
||||
"gfx/surface/gl",
|
||||
"gfx/surface/drm",
|
||||
"gfx/surface/tee",
|
||||
"gfx/surface/xml",
|
||||
"gfx/surface/skia",
|
||||
"gfx/surface/d2d"
|
||||
"heap-used/gfx/surface/image",
|
||||
"heap-used/gfx/surface/pdf",
|
||||
"heap-used/gfx/surface/ps",
|
||||
"heap-used/gfx/surface/xlib",
|
||||
"heap-used/gfx/surface/xcb",
|
||||
"heap-used/gfx/surface/glitz",
|
||||
"heap-used/gfx/surface/quartz",
|
||||
"heap-used/gfx/surface/win32",
|
||||
"heap-used/gfx/surface/beos",
|
||||
"heap-used/gfx/surface/directfb",
|
||||
"heap-used/gfx/surface/svg",
|
||||
"heap-used/gfx/surface/os2",
|
||||
"heap-used/gfx/surface/win32printing",
|
||||
"heap-used/gfx/surface/quartzimage",
|
||||
"heap-used/gfx/surface/script",
|
||||
"heap-used/gfx/surface/qpainter",
|
||||
"heap-used/gfx/surface/recording",
|
||||
"heap-used/gfx/surface/vg",
|
||||
"heap-used/gfx/surface/gl",
|
||||
"heap-used/gfx/surface/drm",
|
||||
"heap-used/gfx/surface/tee",
|
||||
"heap-used/gfx/surface/xml",
|
||||
"heap-used/gfx/surface/skia",
|
||||
"heap-used/gfx/surface/d2d"
|
||||
};
|
||||
|
||||
PR_STATIC_ASSERT(NS_ARRAY_LENGTH(sSurfaceNamesForSurfaceType) == gfxASurface::SurfaceTypeMax);
|
||||
|
@ -558,7 +558,7 @@ SurfaceMemoryReporterPathForType(gfxASurface::gfxSurfaceType aType)
|
|||
{
|
||||
if (aType < 0 ||
|
||||
aType >= gfxASurface::SurfaceTypeMax)
|
||||
return "gfx/surface/unknown";
|
||||
return "heap-used/gfx/surface/unknown";
|
||||
|
||||
return sSurfaceNamesForSurfaceType[aType];
|
||||
}
|
||||
|
|
|
@ -96,12 +96,12 @@ public:
|
|||
NS_DECL_ISUPPORTS
|
||||
|
||||
NS_IMETHOD GetPath(char **memoryPath) {
|
||||
*memoryPath = strdup("gfx/d2d/surfacecache");
|
||||
*memoryPath = strdup("gfx-d2d-surfacecache");
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHOD GetDescription(char **desc) {
|
||||
*desc = strdup("Memory used by Direct2D internal surface cache.");
|
||||
*desc = strdup("Memory used by the Direct2D internal surface cache.");
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
@ -123,7 +123,7 @@ public:
|
|||
NS_DECL_ISUPPORTS
|
||||
|
||||
NS_IMETHOD GetPath(char **memoryPath) {
|
||||
*memoryPath = strdup("gfx/d2d/surfacevram");
|
||||
*memoryPath = strdup("gfx-d2d-surfacevram");
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
|
|
@ -52,13 +52,15 @@ static PRInt64 GetShmemAllocated(void*) { return gShmemAllocated; }
|
|||
static PRInt64 GetShmemMapped(void*) { return gShmemMapped; }
|
||||
|
||||
NS_MEMORY_REPORTER_IMPLEMENT(ShmemAllocated,
|
||||
"shmem/allocated",
|
||||
"Shmem bytes accessible (not necessarily mapped)",
|
||||
"shmem-allocated",
|
||||
"Memory shared with other processes that is "
|
||||
"accessible (but not necessarily mapped).",
|
||||
GetShmemAllocated,
|
||||
nsnull)
|
||||
NS_MEMORY_REPORTER_IMPLEMENT(ShmemMapped,
|
||||
"shmem/mapped",
|
||||
"Shmem bytes mapped into address space",
|
||||
"shmem-mapped",
|
||||
"Memory shared with other processes that is "
|
||||
"mapped into the address space.",
|
||||
GetShmemMapped,
|
||||
nsnull)
|
||||
|
||||
|
|
|
@ -32,6 +32,22 @@ namespace JSC {
|
|||
size_t ExecutableAllocator::pageSize = 0;
|
||||
size_t ExecutableAllocator::largeAllocSize = 0;
|
||||
|
||||
ExecutablePool::~ExecutablePool()
|
||||
{
|
||||
m_allocator->releasePoolPages(this);
|
||||
}
|
||||
|
||||
size_t
|
||||
ExecutableAllocator::getCodeSize() const
|
||||
{
|
||||
size_t n = 0;
|
||||
for (ExecPoolHashSet::Range r = m_pools.all(); !r.empty(); r.popFront()) {
|
||||
ExecutablePool* pool = r.front();
|
||||
n += pool->m_allocation.size;
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif // HAVE(ASSEMBLER)
|
||||
|
|
|
@ -31,6 +31,7 @@
|
|||
#include "assembler/wtf/Assertions.h"
|
||||
|
||||
#include "jsapi.h"
|
||||
#include "jshashtable.h"
|
||||
#include "jsprvtd.h"
|
||||
#include "jsvector.h"
|
||||
#include "jslock.h"
|
||||
|
@ -78,6 +79,8 @@ extern "C" void sync_instruction_memory(caddr_t v, u_int len);
|
|||
|
||||
namespace JSC {
|
||||
|
||||
class ExecutableAllocator;
|
||||
|
||||
// These are reference-counted. A new one starts with a count of 1.
|
||||
class ExecutablePool {
|
||||
|
||||
|
@ -92,6 +95,7 @@ private:
|
|||
#endif
|
||||
};
|
||||
|
||||
ExecutableAllocator* m_allocator;
|
||||
char* m_freePtr;
|
||||
char* m_end;
|
||||
Allocation m_allocation;
|
||||
|
@ -126,17 +130,12 @@ private:
|
|||
++m_refCount;
|
||||
}
|
||||
|
||||
private:
|
||||
ExecutablePool(Allocation a)
|
||||
: m_freePtr(a.pages), m_end(m_freePtr + a.size), m_allocation(a), m_refCount(1),
|
||||
m_destroy(false), m_gcNumber(0)
|
||||
ExecutablePool(ExecutableAllocator* allocator, Allocation a)
|
||||
: m_allocator(allocator), m_freePtr(a.pages), m_end(m_freePtr + a.size), m_allocation(a),
|
||||
m_refCount(1), m_destroy(false), m_gcNumber(0)
|
||||
{ }
|
||||
|
||||
~ExecutablePool()
|
||||
{
|
||||
if (m_allocation.pages)
|
||||
ExecutablePool::systemRelease(m_allocation);
|
||||
}
|
||||
~ExecutablePool();
|
||||
|
||||
void* alloc(size_t n)
|
||||
{
|
||||
|
@ -150,14 +149,10 @@ private:
|
|||
JS_ASSERT(m_end >= m_freePtr);
|
||||
return m_end - m_freePtr;
|
||||
}
|
||||
|
||||
// On OOM, this will return an Allocation where pages is NULL.
|
||||
static Allocation systemAlloc(size_t n);
|
||||
static void systemRelease(const Allocation& alloc);
|
||||
};
|
||||
|
||||
class ExecutableAllocator {
|
||||
enum ProtectionSeting { Writable, Executable };
|
||||
enum ProtectionSetting { Writable, Executable };
|
||||
|
||||
public:
|
||||
ExecutableAllocator()
|
||||
|
@ -175,13 +170,14 @@ public:
|
|||
largeAllocSize = pageSize * 16;
|
||||
}
|
||||
|
||||
JS_ASSERT(m_smallAllocationPools.empty());
|
||||
JS_ASSERT(m_smallPools.empty());
|
||||
}
|
||||
|
||||
~ExecutableAllocator()
|
||||
{
|
||||
for (size_t i = 0; i < m_smallAllocationPools.length(); i++)
|
||||
m_smallAllocationPools[i]->release(/* willDestroy = */true);
|
||||
for (size_t i = 0; i < m_smallPools.length(); i++)
|
||||
m_smallPools[i]->release(/* willDestroy = */true);
|
||||
JS_ASSERT(m_pools.empty()); // if this asserts we have a pool leak
|
||||
}
|
||||
|
||||
// alloc() returns a pointer to some memory, and also (by reference) a
|
||||
|
@ -209,6 +205,14 @@ public:
|
|||
return result;
|
||||
}
|
||||
|
||||
void releasePoolPages(ExecutablePool *pool) {
|
||||
JS_ASSERT(pool->m_allocation.pages);
|
||||
systemRelease(pool->m_allocation);
|
||||
m_pools.remove(m_pools.lookup(pool)); // this asserts if |pool| is not in m_pools
|
||||
}
|
||||
|
||||
size_t getCodeSize() const;
|
||||
|
||||
private:
|
||||
static size_t pageSize;
|
||||
static size_t largeAllocSize;
|
||||
|
@ -233,20 +237,34 @@ private:
|
|||
return size;
|
||||
}
|
||||
|
||||
// On OOM, this will return an Allocation where pages is NULL.
|
||||
static ExecutablePool::Allocation systemAlloc(size_t n);
|
||||
static void systemRelease(const ExecutablePool::Allocation& alloc);
|
||||
|
||||
ExecutablePool* createPool(size_t n)
|
||||
{
|
||||
size_t allocSize = roundUpAllocationSize(n, pageSize);
|
||||
if (allocSize == OVERSIZE_ALLOCATION)
|
||||
return NULL;
|
||||
|
||||
if (!m_pools.initialized() && !m_pools.init())
|
||||
return NULL;
|
||||
|
||||
#ifdef DEBUG_STRESS_JSC_ALLOCATOR
|
||||
ExecutablePool::Allocation a = ExecutablePool::systemAlloc(size_t(4294967291));
|
||||
ExecutablePool::Allocation a = systemAlloc(size_t(4294967291));
|
||||
#else
|
||||
ExecutablePool::Allocation a = ExecutablePool::systemAlloc(allocSize);
|
||||
ExecutablePool::Allocation a = systemAlloc(allocSize);
|
||||
#endif
|
||||
if (!a.pages)
|
||||
return NULL;
|
||||
|
||||
return js::OffTheBooks::new_<ExecutablePool>(a);
|
||||
ExecutablePool *pool = js::OffTheBooks::new_<ExecutablePool>(this, a);
|
||||
if (!pool) {
|
||||
systemRelease(a);
|
||||
return NULL;
|
||||
}
|
||||
m_pools.put(pool);
|
||||
return pool;
|
||||
}
|
||||
|
||||
ExecutablePool* poolForSize(size_t n)
|
||||
|
@ -258,8 +276,8 @@ private:
|
|||
// allocation fitting in a small pool, and (b) it minimizes the
|
||||
// potential waste when a small pool is next abandoned.
|
||||
ExecutablePool *minPool = NULL;
|
||||
for (size_t i = 0; i < m_smallAllocationPools.length(); i++) {
|
||||
ExecutablePool *pool = m_smallAllocationPools[i];
|
||||
for (size_t i = 0; i < m_smallPools.length(); i++) {
|
||||
ExecutablePool *pool = m_smallPools[i];
|
||||
if (n <= pool->available() && (!minPool || pool->available() < minPool->available()))
|
||||
minPool = pool;
|
||||
}
|
||||
|
@ -279,26 +297,26 @@ private:
|
|||
return NULL;
|
||||
// At this point, local |pool| is the owner.
|
||||
|
||||
if (m_smallAllocationPools.length() < maxSmallPools) {
|
||||
if (m_smallPools.length() < maxSmallPools) {
|
||||
// We haven't hit the maximum number of live pools; add the new pool.
|
||||
m_smallAllocationPools.append(pool);
|
||||
m_smallPools.append(pool);
|
||||
pool->addRef();
|
||||
} else {
|
||||
// Find the pool with the least space.
|
||||
int iMin = 0;
|
||||
for (size_t i = 1; i < m_smallAllocationPools.length(); i++)
|
||||
if (m_smallAllocationPools[i]->available() <
|
||||
m_smallAllocationPools[iMin]->available())
|
||||
for (size_t i = 1; i < m_smallPools.length(); i++)
|
||||
if (m_smallPools[i]->available() <
|
||||
m_smallPools[iMin]->available())
|
||||
{
|
||||
iMin = i;
|
||||
}
|
||||
|
||||
// If the new allocator will result in more free space than the small
|
||||
// pool with the least space, then we will use it instead
|
||||
ExecutablePool *minPool = m_smallAllocationPools[iMin];
|
||||
ExecutablePool *minPool = m_smallPools[iMin];
|
||||
if ((pool->available() - n) > minPool->available()) {
|
||||
minPool->release();
|
||||
m_smallAllocationPools[iMin] = pool;
|
||||
m_smallPools[iMin] = pool;
|
||||
pool->addRef();
|
||||
}
|
||||
}
|
||||
|
@ -411,12 +429,21 @@ public:
|
|||
private:
|
||||
|
||||
#if ENABLE_ASSEMBLER_WX_EXCLUSIVE
|
||||
static void reprotectRegion(void*, size_t, ProtectionSeting);
|
||||
static void reprotectRegion(void*, size_t, ProtectionSetting);
|
||||
#endif
|
||||
|
||||
// These are strong references; they keep pools alive.
|
||||
static const size_t maxSmallPools = 4;
|
||||
typedef js::Vector<ExecutablePool *, maxSmallPools, js::SystemAllocPolicy > SmallExecPoolVector;
|
||||
SmallExecPoolVector m_smallAllocationPools;
|
||||
typedef js::Vector<ExecutablePool *, maxSmallPools, js::SystemAllocPolicy> SmallExecPoolVector;
|
||||
SmallExecPoolVector m_smallPools;
|
||||
|
||||
// All live pools are recorded here, just for stats purposes. These are
|
||||
// weak references; they don't keep pools alive. When a pool is destroyed
|
||||
// its reference is removed from m_pools.
|
||||
typedef js::HashSet<ExecutablePool *, js::DefaultHasher<ExecutablePool *>, js::SystemAllocPolicy>
|
||||
ExecPoolHashSet;
|
||||
ExecPoolHashSet m_pools; // All pools, just for stats purposes.
|
||||
|
||||
static size_t determinePageSize();
|
||||
};
|
||||
|
||||
|
|
|
@ -38,7 +38,7 @@ size_t ExecutableAllocator::determinePageSize()
|
|||
return 4096u;
|
||||
}
|
||||
|
||||
ExecutablePool::Allocation ExecutablePool::systemAlloc(size_t n)
|
||||
ExecutablePool::Allocation ExecutableAllocator::systemAlloc(size_t n)
|
||||
{
|
||||
void* allocation = NULL;
|
||||
if (DosAllocMem(&allocation, n, OBJ_ANY|PAG_COMMIT|PAG_READ|PAG_WRITE) &&
|
||||
|
@ -48,7 +48,7 @@ ExecutablePool::Allocation ExecutablePool::systemAlloc(size_t n)
|
|||
return alloc;
|
||||
}
|
||||
|
||||
void ExecutablePool::systemRelease(const ExecutablePool::Allocation& alloc)
|
||||
void ExecutableAllocator::systemRelease(const ExecutablePool::Allocation& alloc)
|
||||
{
|
||||
DosFreeMem(alloc.pages);
|
||||
}
|
||||
|
|
|
@ -38,7 +38,7 @@ size_t ExecutableAllocator::determinePageSize()
|
|||
return getpagesize();
|
||||
}
|
||||
|
||||
ExecutablePool::Allocation ExecutablePool::systemAlloc(size_t n)
|
||||
ExecutablePool::Allocation ExecutableAllocator::systemAlloc(size_t n)
|
||||
{
|
||||
void* allocation = mmap(NULL, n, INITIAL_PROTECTION_FLAGS, MAP_PRIVATE | MAP_ANON, VM_TAG_FOR_EXECUTABLEALLOCATOR_MEMORY, 0);
|
||||
if (allocation == MAP_FAILED)
|
||||
|
@ -47,14 +47,14 @@ ExecutablePool::Allocation ExecutablePool::systemAlloc(size_t n)
|
|||
return alloc;
|
||||
}
|
||||
|
||||
void ExecutablePool::systemRelease(const ExecutablePool::Allocation& alloc)
|
||||
void ExecutableAllocator::systemRelease(const ExecutablePool::Allocation& alloc)
|
||||
{
|
||||
int result = munmap(alloc.pages, alloc.size);
|
||||
ASSERT_UNUSED(result, !result);
|
||||
}
|
||||
|
||||
#if WTF_ENABLE_ASSEMBLER_WX_EXCLUSIVE
|
||||
void ExecutableAllocator::reprotectRegion(void* start, size_t size, ProtectionSeting setting)
|
||||
void ExecutableAllocator::reprotectRegion(void* start, size_t size, ProtectionSetting setting)
|
||||
{
|
||||
if (!pageSize)
|
||||
intializePageSize();
|
||||
|
|
|
@ -47,7 +47,7 @@ size_t ExecutableAllocator::determinePageSize()
|
|||
#endif
|
||||
}
|
||||
|
||||
ExecutablePool::Allocation ExecutablePool::systemAlloc(size_t n)
|
||||
ExecutablePool::Allocation ExecutableAllocator::systemAlloc(size_t n)
|
||||
{
|
||||
RChunk* codeChunk = new RChunk();
|
||||
|
||||
|
@ -58,7 +58,7 @@ ExecutablePool::Allocation ExecutablePool::systemAlloc(size_t n)
|
|||
return alloc;
|
||||
}
|
||||
|
||||
void ExecutablePool::systemRelease(const ExecutablePool::Allocation& alloc)
|
||||
void ExecutableAllocator::systemRelease(const ExecutablePool::Allocation& alloc)
|
||||
{
|
||||
alloc.chunk->Close();
|
||||
delete alloc.chunk;
|
||||
|
|
|
@ -39,14 +39,14 @@ size_t ExecutableAllocator::determinePageSize()
|
|||
return system_info.dwPageSize;
|
||||
}
|
||||
|
||||
ExecutablePool::Allocation ExecutablePool::systemAlloc(size_t n)
|
||||
ExecutablePool::Allocation ExecutableAllocator::systemAlloc(size_t n)
|
||||
{
|
||||
void *allocation = VirtualAlloc(0, n, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
|
||||
ExecutablePool::Allocation alloc = {reinterpret_cast<char*>(allocation), n};
|
||||
return alloc;
|
||||
}
|
||||
|
||||
void ExecutablePool::systemRelease(const ExecutablePool::Allocation& alloc)
|
||||
void ExecutableAllocator::systemRelease(const ExecutablePool::Allocation& alloc)
|
||||
{
|
||||
VirtualFree(alloc.pages, 0, MEM_RELEASE);
|
||||
}
|
||||
|
|
|
@ -702,9 +702,10 @@ struct JSRuntime {
|
|||
JSPreWrapCallback preWrapObjectCallback;
|
||||
|
||||
#ifdef JS_METHODJIT
|
||||
uint32 mjitMemoryUsed;
|
||||
/* This measures the size of JITScripts, native maps and IC structs. */
|
||||
size_t mjitDataSize;
|
||||
#endif
|
||||
uint32 stringMemoryUsed;
|
||||
size_t stringMemoryUsed;
|
||||
|
||||
JSRuntime();
|
||||
~JSRuntime();
|
||||
|
|
|
@ -165,6 +165,14 @@ JSCompartment::init()
|
|||
#endif
|
||||
}
|
||||
|
||||
#ifdef JS_METHODJIT
|
||||
size_t
|
||||
JSCompartment::getMjitCodeSize() const
|
||||
{
|
||||
return jaegerCompartment->execAlloc()->getCodeSize();
|
||||
}
|
||||
#endif
|
||||
|
||||
bool
|
||||
JSCompartment::arenaListsAreEmpty()
|
||||
{
|
||||
|
|
|
@ -285,6 +285,10 @@ struct TraceMonitor {
|
|||
void mark(JSTracer *trc);
|
||||
|
||||
bool outOfMemory() const;
|
||||
|
||||
JS_FRIEND_API(void) getCodeAllocStats(size_t &total, size_t &frag_size, size_t &free_size) const;
|
||||
JS_FRIEND_API(size_t) getVMAllocatorsMainSize() const;
|
||||
JS_FRIEND_API(size_t) getVMAllocatorsReserveSize() const;
|
||||
};
|
||||
|
||||
namespace mjit {
|
||||
|
@ -409,6 +413,13 @@ struct JS_FRIEND_API(JSCompartment) {
|
|||
|
||||
#ifdef JS_METHODJIT
|
||||
js::mjit::JaegerCompartment *jaegerCompartment;
|
||||
/*
|
||||
* This function is here so that xpconnect/src/xpcjsruntime.cpp doesn't
|
||||
* need to see the declaration of JaegerCompartment, which would require
|
||||
* #including MethodJIT.h into xpconnect/src/xpcjsruntime.cpp, which is
|
||||
* difficult due to reasons explained in bug 483677.
|
||||
*/
|
||||
size_t getMjitCodeSize() const;
|
||||
#endif
|
||||
|
||||
/*
|
||||
|
|
|
@ -2461,6 +2461,34 @@ TraceMonitor::outOfMemory() const
|
|||
traceAlloc->outOfMemory();
|
||||
}
|
||||
|
||||
void
|
||||
TraceMonitor::getCodeAllocStats(size_t &total, size_t &frag_size, size_t &free_size) const
|
||||
{
|
||||
if (codeAlloc) {
|
||||
codeAlloc->getStats(total, frag_size, free_size);
|
||||
} else {
|
||||
total = 0;
|
||||
frag_size = 0;
|
||||
free_size = 0;
|
||||
}
|
||||
}
|
||||
|
||||
size_t
|
||||
TraceMonitor::getVMAllocatorsMainSize() const
|
||||
{
|
||||
return dataAlloc->getBytesAllocated() +
|
||||
traceAlloc->getBytesAllocated() +
|
||||
tempAlloc->getBytesAllocated();
|
||||
}
|
||||
|
||||
size_t
|
||||
TraceMonitor::getVMAllocatorsReserveSize() const
|
||||
{
|
||||
return dataAlloc->mReserveSize +
|
||||
traceAlloc->mReserveSize +
|
||||
tempAlloc->mReserveSize;
|
||||
}
|
||||
|
||||
/*
|
||||
* This function destroys the recorder after a successful recording, possibly
|
||||
* starting a suspended outer recorder.
|
||||
|
|
|
@ -409,6 +409,7 @@ public:
|
|||
: mOutOfMemory(false)
|
||||
, mSize(0)
|
||||
, mReserve(reserve)
|
||||
, mReserveSize(reserveSize)
|
||||
, mReserveCurr(uintptr_t(reserve))
|
||||
, mReserveLimit(uintptr_t(reserve + reserveSize))
|
||||
, mRt(rt)
|
||||
|
@ -470,6 +471,7 @@ public:
|
|||
|
||||
/* See nanojit::Allocator::allocChunk() for details on these. */
|
||||
char* mReserve;
|
||||
size_t mReserveSize;
|
||||
uintptr_t mReserveCurr;
|
||||
uintptr_t mReserveLimit;
|
||||
|
||||
|
|
|
@ -404,24 +404,24 @@ mjit::Compiler::finishThisUp(JITScript **jitp)
|
|||
#endif
|
||||
JaegerSpew(JSpew_Insns, "## Fast code (masm) size = %u, Slow code (stubcc) size = %u.\n", masm.size(), stubcc.size());
|
||||
|
||||
size_t totalSize = masm.size() +
|
||||
stubcc.size() +
|
||||
doubleList.length() * sizeof(double) +
|
||||
jumpTableOffsets.length() * sizeof(void *);
|
||||
size_t codeSize = masm.size() +
|
||||
stubcc.size() +
|
||||
doubleList.length() * sizeof(double) +
|
||||
jumpTableOffsets.length() * sizeof(void *);
|
||||
|
||||
JSC::ExecutablePool *execPool;
|
||||
uint8 *result =
|
||||
(uint8 *)script->compartment->jaegerCompartment->execAlloc()->alloc(totalSize, &execPool);
|
||||
(uint8 *)script->compartment->jaegerCompartment->execAlloc()->alloc(codeSize, &execPool);
|
||||
if (!result) {
|
||||
js_ReportOutOfMemory(cx);
|
||||
return Compile_Error;
|
||||
}
|
||||
JS_ASSERT(execPool);
|
||||
JSC::ExecutableAllocator::makeWritable(result, totalSize);
|
||||
JSC::ExecutableAllocator::makeWritable(result, codeSize);
|
||||
masm.executableCopy(result);
|
||||
stubcc.masm.executableCopy(result + masm.size());
|
||||
|
||||
JSC::LinkBuffer fullCode(result, totalSize);
|
||||
JSC::LinkBuffer fullCode(result, codeSize);
|
||||
JSC::LinkBuffer stubCode(result + masm.size(), stubcc.size());
|
||||
|
||||
size_t nNmapLive = 0;
|
||||
|
@ -432,23 +432,23 @@ mjit::Compiler::finishThisUp(JITScript **jitp)
|
|||
}
|
||||
|
||||
/* Please keep in sync with JITScript::scriptDataSize! */
|
||||
size_t totalBytes = sizeof(JITScript) +
|
||||
sizeof(NativeMapEntry) * nNmapLive +
|
||||
size_t dataSize = sizeof(JITScript) +
|
||||
sizeof(NativeMapEntry) * nNmapLive +
|
||||
#if defined JS_MONOIC
|
||||
sizeof(ic::GetGlobalNameIC) * getGlobalNames.length() +
|
||||
sizeof(ic::SetGlobalNameIC) * setGlobalNames.length() +
|
||||
sizeof(ic::CallICInfo) * callICs.length() +
|
||||
sizeof(ic::EqualityICInfo) * equalityICs.length() +
|
||||
sizeof(ic::TraceICInfo) * traceICs.length() +
|
||||
sizeof(ic::GetGlobalNameIC) * getGlobalNames.length() +
|
||||
sizeof(ic::SetGlobalNameIC) * setGlobalNames.length() +
|
||||
sizeof(ic::CallICInfo) * callICs.length() +
|
||||
sizeof(ic::EqualityICInfo) * equalityICs.length() +
|
||||
sizeof(ic::TraceICInfo) * traceICs.length() +
|
||||
#endif
|
||||
#if defined JS_POLYIC
|
||||
sizeof(ic::PICInfo) * pics.length() +
|
||||
sizeof(ic::GetElementIC) * getElemICs.length() +
|
||||
sizeof(ic::SetElementIC) * setElemICs.length() +
|
||||
sizeof(ic::PICInfo) * pics.length() +
|
||||
sizeof(ic::GetElementIC) * getElemICs.length() +
|
||||
sizeof(ic::SetElementIC) * setElemICs.length() +
|
||||
#endif
|
||||
sizeof(CallSite) * callSites.length();
|
||||
sizeof(CallSite) * callSites.length();
|
||||
|
||||
uint8 *cursor = (uint8 *)cx->calloc_(totalBytes);
|
||||
uint8 *cursor = (uint8 *)cx->calloc_(dataSize);
|
||||
if (!cursor) {
|
||||
execPool->release();
|
||||
js_ReportOutOfMemory(cx);
|
||||
|
@ -808,12 +808,12 @@ mjit::Compiler::finishThisUp(JITScript **jitp)
|
|||
to.initialize(codeOffset, from.pc - script->code, from.id);
|
||||
}
|
||||
|
||||
JS_ASSERT(size_t(cursor - (uint8*)jit) == totalBytes);
|
||||
JS_ASSERT(size_t(cursor - (uint8*)jit) == dataSize);
|
||||
|
||||
*jitp = jit;
|
||||
|
||||
/* We tolerate a race in the stats. */
|
||||
cx->runtime->mjitMemoryUsed += totalSize + totalBytes;
|
||||
cx->runtime->mjitDataSize += dataSize;
|
||||
|
||||
return Compile_Okay;
|
||||
}
|
||||
|
|
|
@ -909,7 +909,7 @@ mjit::ReleaseScriptCode(JSContext *cx, JSScript *script)
|
|||
JITScript *jscr;
|
||||
|
||||
if ((jscr = script->jitNormal)) {
|
||||
cx->runtime->mjitMemoryUsed -= jscr->scriptDataSize() + jscr->mainCodeSize();
|
||||
cx->runtime->mjitDataSize -= jscr->scriptDataSize();
|
||||
|
||||
jscr->~JITScript();
|
||||
cx->free_(jscr);
|
||||
|
@ -918,7 +918,7 @@ mjit::ReleaseScriptCode(JSContext *cx, JSScript *script)
|
|||
}
|
||||
|
||||
if ((jscr = script->jitCtor)) {
|
||||
cx->runtime->mjitMemoryUsed -= jscr->scriptDataSize() + jscr->mainCodeSize();
|
||||
cx->runtime->mjitDataSize -= jscr->scriptDataSize();
|
||||
|
||||
jscr->~JITScript();
|
||||
cx->free_(jscr);
|
||||
|
|
|
@ -415,8 +415,6 @@ struct JITScript {
|
|||
|
||||
size_t scriptDataSize();
|
||||
|
||||
size_t mainCodeSize() { return code.m_size; } /* doesn't account for fragmentation */
|
||||
|
||||
jsbytecode *nativeToPC(void *returnAddress) const;
|
||||
|
||||
private:
|
||||
|
|
|
@ -89,6 +89,7 @@ namespace nanojit
|
|||
if (mem) {
|
||||
Chunk* chunk = (Chunk*) mem;
|
||||
chunk->prev = current_chunk;
|
||||
chunk->size = chunkbytes;
|
||||
current_chunk = chunk;
|
||||
current_top = (char*)chunk->data;
|
||||
current_limit = (char*)mem + chunkbytes;
|
||||
|
@ -98,6 +99,17 @@ namespace nanojit
|
|||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
size_t Allocator::getBytesAllocated()
|
||||
{
|
||||
size_t n = 0;
|
||||
Chunk *c = current_chunk;
|
||||
while (c) {
|
||||
n += c->size;
|
||||
c = c->prev;
|
||||
}
|
||||
return n;
|
||||
}
|
||||
}
|
||||
|
||||
#endif // FEATURE_NANOJIT
|
||||
|
|
|
@ -90,6 +90,8 @@ namespace nanojit
|
|||
return p;
|
||||
}
|
||||
|
||||
size_t getBytesAllocated();
|
||||
|
||||
protected:
|
||||
void* allocSlow(size_t nbytes, bool fallible = false);
|
||||
bool fill(size_t minbytes, bool fallible);
|
||||
|
@ -97,6 +99,7 @@ namespace nanojit
|
|||
class Chunk {
|
||||
public:
|
||||
Chunk* prev;
|
||||
size_t size;
|
||||
int64_t data[1]; // int64_t forces 8-byte alignment.
|
||||
};
|
||||
|
||||
|
|
|
@ -98,10 +98,10 @@ namespace nanojit
|
|||
return (int)((x + 512) >> 10);
|
||||
}
|
||||
|
||||
void CodeAlloc::logStats() {
|
||||
size_t total = 0;
|
||||
size_t frag_size = 0;
|
||||
size_t free_size = 0;
|
||||
void CodeAlloc::getStats(size_t& total, size_t& frag_size, size_t& free_size) {
|
||||
total = 0;
|
||||
frag_size = 0;
|
||||
free_size = 0;
|
||||
int free_count = 0;
|
||||
for (CodeList* hb = heapblocks; hb != 0; hb = hb->next) {
|
||||
total += bytesPerAlloc;
|
||||
|
@ -114,6 +114,11 @@ namespace nanojit
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CodeAlloc::logStats() {
|
||||
size_t total, frag_size, free_size;
|
||||
getStats(total, frag_size, free_size);
|
||||
avmplus::AvmLog("code-heap: %dk free %dk fragmented %d\n",
|
||||
round(total), round(free_size), frag_size);
|
||||
}
|
||||
|
|
|
@ -217,6 +217,9 @@ namespace nanojit
|
|||
/** return the total number of bytes held by this CodeAlloc. */
|
||||
size_t size();
|
||||
|
||||
/** get stats about heap usage */
|
||||
void getStats(size_t& total, size_t& frag_size, size_t& free_size);
|
||||
|
||||
/** print out stats about heap usage */
|
||||
void logStats();
|
||||
|
||||
|
|
|
@ -4570,10 +4570,26 @@ Deserialize(JSContext *cx, uintN argc, jsval *vp)
|
|||
}
|
||||
|
||||
JSBool
|
||||
MJitStats(JSContext *cx, uintN argc, jsval *vp)
|
||||
MJitCodeStats(JSContext *cx, uintN argc, jsval *vp)
|
||||
{
|
||||
#ifdef JS_METHODJIT
|
||||
JS_SET_RVAL(cx, vp, INT_TO_JSVAL(cx->runtime->mjitMemoryUsed));
|
||||
JSRuntime *rt = cx->runtime;
|
||||
AutoLockGC lock(rt);
|
||||
size_t n = 0;
|
||||
for (JSCompartment **c = rt->compartments.begin(); c != rt->compartments.end(); ++c)
|
||||
n += (*c)->getMjitCodeSize();
|
||||
JS_SET_RVAL(cx, vp, INT_TO_JSVAL(n));
|
||||
#else
|
||||
JS_SET_RVAL(cx, vp, JSVAL_VOID);
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
JSBool
|
||||
MJitDataStats(JSContext *cx, uintN argc, jsval *vp)
|
||||
{
|
||||
#ifdef JS_METHODJIT
|
||||
JS_SET_RVAL(cx, vp, INT_TO_JSVAL(cx->runtime->mjitDataSize));
|
||||
#else
|
||||
JS_SET_RVAL(cx, vp, JSVAL_VOID);
|
||||
#endif
|
||||
|
@ -4733,7 +4749,8 @@ static JSFunctionSpec shell_functions[] = {
|
|||
JS_FN("serialize", Serialize, 1,0),
|
||||
JS_FN("deserialize", Deserialize, 1,0),
|
||||
#ifdef JS_METHODJIT
|
||||
JS_FN("mjitstats", MJitStats, 0,0),
|
||||
JS_FN("mjitcodestats", MJitCodeStats, 0,0),
|
||||
JS_FN("mjitdatastats", MJitDataStats, 0,0),
|
||||
#endif
|
||||
JS_FN("stringstats", StringStats, 0,0),
|
||||
JS_FN("newGlobal", NewGlobal, 1,0),
|
||||
|
@ -4867,7 +4884,8 @@ static const char *const shell_help_messages[] = {
|
|||
"serialize(sd) Serialize sd using JS_WriteStructuredClone. Returns a TypedArray.",
|
||||
"deserialize(a) Deserialize data generated by serialize.",
|
||||
#ifdef JS_METHODJIT
|
||||
"mjitstats() Return stats on mjit memory usage.",
|
||||
"mjitcodestats() Return stats on mjit code memory usage.",
|
||||
"mjitdatastats() Return stats on mjit data memory usage.",
|
||||
#endif
|
||||
"stringstats() Return stats on string memory usage.",
|
||||
"newGlobal(kind) Return a new global object, in the current\n"
|
||||
|
|
|
@ -1278,43 +1278,140 @@ protected:
|
|||
|
||||
static XPConnectGCChunkAllocator gXPCJSChunkAllocator;
|
||||
|
||||
NS_MEMORY_REPORTER_IMPLEMENT(XPConnectJSRuntimeGCChunks,
|
||||
"js/gc-heap",
|
||||
"Main JS GC heap",
|
||||
NS_MEMORY_REPORTER_IMPLEMENT(XPConnectJSGCHeap,
|
||||
"heap-used/js/gc-heap",
|
||||
"Memory used by the garbage-collected JavaScript "
|
||||
"heap.",
|
||||
XPConnectGCChunkAllocator::GetGCChunkBytesInUse,
|
||||
&gXPCJSChunkAllocator)
|
||||
|
||||
/* FIXME: use API provided by bug 623271 */
|
||||
#include "jscntxt.h"
|
||||
|
||||
static PRInt64
|
||||
GetJSMethodJitCodeMemoryInUse(void *data)
|
||||
GetPerCompartmentSize(PRInt64 (*f)(JSCompartment *c))
|
||||
{
|
||||
JSRuntime *rt = nsXPConnect::GetRuntimeInstance()->GetJSRuntime();
|
||||
#ifdef JS_METHODJIT
|
||||
return rt->mjitMemoryUsed;
|
||||
#else
|
||||
return 0;
|
||||
#endif
|
||||
js::AutoLockGC lock(rt);
|
||||
PRInt64 n = 0;
|
||||
for (JSCompartment **c = rt->compartments.begin(); c != rt->compartments.end(); ++c)
|
||||
n += f(*c);
|
||||
return n;
|
||||
}
|
||||
|
||||
NS_MEMORY_REPORTER_IMPLEMENT(XPConnectJSMethodJitCode,
|
||||
"js/mjit-code",
|
||||
"Memory in use by method-JIT for compiled code",
|
||||
GetJSMethodJitCodeMemoryInUse,
|
||||
NULL)
|
||||
#ifdef JS_METHODJIT
|
||||
|
||||
static PRInt64
|
||||
GetJSStringMemoryInUse(void *data)
|
||||
GetCompartmentMjitCodeSize(JSCompartment *c)
|
||||
{
|
||||
return c->getMjitCodeSize();
|
||||
}
|
||||
|
||||
static PRInt64
|
||||
GetJSMjitCode(void *data)
|
||||
{
|
||||
return GetPerCompartmentSize(GetCompartmentMjitCodeSize);
|
||||
}
|
||||
|
||||
static PRInt64
|
||||
GetJSMJitData(void *data)
|
||||
{
|
||||
JSRuntime *rt = nsXPConnect::GetRuntimeInstance()->GetJSRuntime();
|
||||
return rt->mjitDataSize;
|
||||
}
|
||||
|
||||
NS_MEMORY_REPORTER_IMPLEMENT(XPConnectJSMjitCode,
|
||||
"mapped/js/mjit-code",
|
||||
"Memory mapped by the method JIT to hold "
|
||||
"generated code.",
|
||||
GetJSMjitCode,
|
||||
NULL)
|
||||
|
||||
NS_MEMORY_REPORTER_IMPLEMENT(XPConnectJSMjitData,
|
||||
"heap-used/js/mjit-data",
|
||||
"Memory allocated by the method JIT for the "
|
||||
"following data: JITScripts, native maps, and "
|
||||
"inline cache structs.",
|
||||
GetJSMJitData,
|
||||
NULL)
|
||||
#endif // JS_METHODJIT
|
||||
|
||||
#ifdef JS_TRACER
|
||||
|
||||
static PRInt64
|
||||
GetCompartmentTjitCode(JSCompartment *c)
|
||||
{
|
||||
js::TraceMonitor &tm = c->traceMonitor;
|
||||
if (tm.codeAlloc) {
|
||||
size_t total, frag_size, free_size;
|
||||
tm.getCodeAllocStats(total, frag_size, free_size);
|
||||
return total;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static PRInt64
|
||||
GetCompartmentTjitDataAllocatorsMain(JSCompartment *c)
|
||||
{
|
||||
js::TraceMonitor &tm = c->traceMonitor;
|
||||
return tm.dataAlloc ? tm.getVMAllocatorsMainSize() : 0;
|
||||
}
|
||||
|
||||
static PRInt64
|
||||
GetCompartmentTjitDataAllocatorsReserve(JSCompartment *c)
|
||||
{
|
||||
js::TraceMonitor &tm = c->traceMonitor;
|
||||
return tm.dataAlloc ? tm.getVMAllocatorsReserveSize() : 0;
|
||||
}
|
||||
|
||||
static PRInt64
|
||||
GetJSTjitCode(void *data)
|
||||
{
|
||||
return GetPerCompartmentSize(GetCompartmentTjitCode);
|
||||
}
|
||||
|
||||
static PRInt64
|
||||
GetJSTjitDataAllocatorsMain(void *data)
|
||||
{
|
||||
return GetPerCompartmentSize(GetCompartmentTjitDataAllocatorsMain);
|
||||
}
|
||||
|
||||
static PRInt64
|
||||
GetJSTjitDataAllocatorsReserve(void *data)
|
||||
{
|
||||
return GetPerCompartmentSize(GetCompartmentTjitDataAllocatorsReserve);
|
||||
}
|
||||
|
||||
NS_MEMORY_REPORTER_IMPLEMENT(XPConnectJSTjitCode,
|
||||
"mapped/js/tjit-code",
|
||||
"Memory mapped by the trace JIT to hold "
|
||||
"generated code.",
|
||||
GetJSTjitCode,
|
||||
NULL)
|
||||
|
||||
NS_MEMORY_REPORTER_IMPLEMENT(XPConnectJSTjitDataAllocatorsMain,
|
||||
"heap-used/js/tjit-data/allocators/main",
|
||||
"Memory allocated by the trace JIT's "
|
||||
"VMAllocators.",
|
||||
GetJSTjitDataAllocatorsMain,
|
||||
NULL)
|
||||
|
||||
NS_MEMORY_REPORTER_IMPLEMENT(XPConnectJSTjitDataAllocatorsReserve,
|
||||
"heap-used/js/tjit-data/allocators/reserve",
|
||||
"Memory allocated by the trace JIT and held in "
|
||||
"reserve for VMAllocators in case of OOM.",
|
||||
GetJSTjitDataAllocatorsReserve,
|
||||
NULL)
|
||||
#endif // JS_TRACER
|
||||
|
||||
static PRInt64
|
||||
GetJSStringData(void *data)
|
||||
{
|
||||
JSRuntime *rt = nsXPConnect::GetRuntimeInstance()->GetJSRuntime();
|
||||
return rt->stringMemoryUsed;
|
||||
}
|
||||
|
||||
NS_MEMORY_REPORTER_IMPLEMENT(XPConnectJSStringMemory,
|
||||
"js/string-data",
|
||||
"Memory in use for string data",
|
||||
GetJSStringMemoryInUse,
|
||||
NS_MEMORY_REPORTER_IMPLEMENT(XPConnectJSStringData,
|
||||
"heap-used/js/string-data",
|
||||
"Memory allocated for JavaScript string data.",
|
||||
GetJSStringData,
|
||||
NULL)
|
||||
|
||||
XPCJSRuntime::XPCJSRuntime(nsXPConnect* aXPConnect)
|
||||
|
@ -1382,9 +1479,13 @@ XPCJSRuntime::XPCJSRuntime(nsXPConnect* aXPConnect)
|
|||
|
||||
mJSRuntime->setCustomGCChunkAllocator(&gXPCJSChunkAllocator);
|
||||
|
||||
NS_RegisterMemoryReporter(new NS_MEMORY_REPORTER_NAME(XPConnectJSRuntimeGCChunks));
|
||||
NS_RegisterMemoryReporter(new NS_MEMORY_REPORTER_NAME(XPConnectJSStringMemory));
|
||||
NS_RegisterMemoryReporter(new NS_MEMORY_REPORTER_NAME(XPConnectJSMethodJitCode));
|
||||
NS_RegisterMemoryReporter(new NS_MEMORY_REPORTER_NAME(XPConnectJSGCHeap));
|
||||
NS_RegisterMemoryReporter(new NS_MEMORY_REPORTER_NAME(XPConnectJSStringData));
|
||||
NS_RegisterMemoryReporter(new NS_MEMORY_REPORTER_NAME(XPConnectJSMjitCode));
|
||||
NS_RegisterMemoryReporter(new NS_MEMORY_REPORTER_NAME(XPConnectJSMjitData));
|
||||
NS_RegisterMemoryReporter(new NS_MEMORY_REPORTER_NAME(XPConnectJSTjitCode));
|
||||
NS_RegisterMemoryReporter(new NS_MEMORY_REPORTER_NAME(XPConnectJSTjitDataAllocatorsMain));
|
||||
NS_RegisterMemoryReporter(new NS_MEMORY_REPORTER_NAME(XPConnectJSTjitDataAllocatorsReserve));
|
||||
}
|
||||
|
||||
if(!JS_DHashTableInit(&mJSHolders, JS_DHashGetStubOps(), nsnull,
|
||||
|
|
|
@ -1646,14 +1646,15 @@ NS_NewPresShell(nsIPresShell** aInstancePtrResult)
|
|||
nsTHashtable<PresShell::PresShellPtrKey> *nsIPresShell::sLiveShells = 0;
|
||||
|
||||
NS_MEMORY_REPORTER_IMPLEMENT(LayoutPresShell,
|
||||
"layout/all",
|
||||
"Memory in use by layout PresShell, PresContext, and other related areas.",
|
||||
"heap-used/layout/all",
|
||||
"Memory used by layout PresShell, PresContext, "
|
||||
"and other related areas.",
|
||||
PresShell::SizeOfLayoutMemoryReporter,
|
||||
nsnull)
|
||||
|
||||
NS_MEMORY_REPORTER_IMPLEMENT(LayoutBidi,
|
||||
"layout/bidi",
|
||||
"Memory in use by layout Bidi processor.",
|
||||
"heap-used/layout/bidi",
|
||||
"Memory used by layout Bidi processor.",
|
||||
PresShell::SizeOfBidiMemoryReporter,
|
||||
nsnull)
|
||||
|
||||
|
|
|
@ -160,21 +160,21 @@ public:
|
|||
NS_IMETHOD GetPath(char **memoryPath)
|
||||
{
|
||||
if (mType == ChromeUsedRaw) {
|
||||
*memoryPath = strdup("images/chrome/used/raw");
|
||||
*memoryPath = strdup("heap-used/images/chrome/used/raw");
|
||||
} else if (mType == ChromeUsedUncompressed) {
|
||||
*memoryPath = strdup("images/chrome/used/uncompressed");
|
||||
*memoryPath = strdup("heap-used/images/chrome/used/uncompressed");
|
||||
} else if (mType == ChromeUnusedRaw) {
|
||||
*memoryPath = strdup("images/chrome/unused/raw");
|
||||
*memoryPath = strdup("heap-used/images/chrome/unused/raw");
|
||||
} else if (mType == ChromeUnusedUncompressed) {
|
||||
*memoryPath = strdup("images/chrome/unused/uncompressed");
|
||||
*memoryPath = strdup("heap-used/images/chrome/unused/uncompressed");
|
||||
} else if (mType == ContentUsedRaw) {
|
||||
*memoryPath = strdup("images/content/used/raw");
|
||||
*memoryPath = strdup("heap-used/images/content/used/raw");
|
||||
} else if (mType == ContentUsedUncompressed) {
|
||||
*memoryPath = strdup("images/content/used/uncompressed");
|
||||
*memoryPath = strdup("heap-used/images/content/used/uncompressed");
|
||||
} else if (mType == ContentUnusedRaw) {
|
||||
*memoryPath = strdup("images/content/unused/raw");
|
||||
*memoryPath = strdup("heap-used/images/content/unused/raw");
|
||||
} else if (mType == ContentUnusedUncompressed) {
|
||||
*memoryPath = strdup("images/content/unused/uncompressed");
|
||||
*memoryPath = strdup("heap-used/images/content/unused/uncompressed");
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
@ -182,21 +182,21 @@ public:
|
|||
NS_IMETHOD GetDescription(char **desc)
|
||||
{
|
||||
if (mType == ChromeUsedRaw) {
|
||||
*desc = strdup("Memory used by in-use chrome images, compressed data");
|
||||
*desc = strdup("Memory used by in-use chrome images (compressed data).");
|
||||
} else if (mType == ChromeUsedUncompressed) {
|
||||
*desc = strdup("Memory used by in-use chrome images, uncompressed data");
|
||||
*desc = strdup("Memory used by in-use chrome images (uncompressed data).");
|
||||
} else if (mType == ChromeUnusedRaw) {
|
||||
*desc = strdup("Memory used by not in-use chrome images, compressed data");
|
||||
*desc = strdup("Memory used by not in-use chrome images (compressed data).");
|
||||
} else if (mType == ChromeUnusedUncompressed) {
|
||||
*desc = strdup("Memory used by not in-use chrome images, uncompressed data");
|
||||
*desc = strdup("Memory used by not in-use chrome images (uncompressed data).");
|
||||
} else if (mType == ContentUsedRaw) {
|
||||
*desc = strdup("Memory used by in-use content images, compressed data");
|
||||
*desc = strdup("Memory used by in-use content images (compressed data).");
|
||||
} else if (mType == ContentUsedUncompressed) {
|
||||
*desc = strdup("Memory used by in-use content images, uncompressed data");
|
||||
*desc = strdup("Memory used by in-use content images (uncompressed data).");
|
||||
} else if (mType == ContentUnusedRaw) {
|
||||
*desc = strdup("Memory used by not in-use content images, compressed data");
|
||||
*desc = strdup("Memory used by not in-use content images (compressed data).");
|
||||
} else if (mType == ContentUnusedUncompressed) {
|
||||
*desc = strdup("Memory used by not in-use content images, uncompressed data");
|
||||
*desc = strdup("Memory used by not in-use content images (uncompressed data).");
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
|
|
@ -353,20 +353,20 @@ public:
|
|||
{
|
||||
nsCString path;
|
||||
|
||||
path.AppendLiteral("storage/");
|
||||
path.AppendLiteral("heap-used/storage/");
|
||||
path.Append(mDBConn.getFilename());
|
||||
|
||||
if (mType == LookAside_Used) {
|
||||
path.AppendLiteral("/LookAside_Used");
|
||||
path.AppendLiteral("/lookaside-used");
|
||||
}
|
||||
else if (mType == Cache_Used) {
|
||||
path.AppendLiteral("/Cache_Used");
|
||||
path.AppendLiteral("/cache-used");
|
||||
}
|
||||
else if (mType == Schema_Used) {
|
||||
path.AppendLiteral("/Schema_Used");
|
||||
path.AppendLiteral("/schema-used");
|
||||
}
|
||||
else if (mType == Stmt_Used) {
|
||||
path.AppendLiteral("/Stmt_Used");
|
||||
path.AppendLiteral("/stmt-used");
|
||||
}
|
||||
|
||||
*memoryPath = ::ToNewCString(path);
|
||||
|
@ -376,16 +376,16 @@ public:
|
|||
NS_IMETHOD GetDescription(char **desc)
|
||||
{
|
||||
if (mType == LookAside_Used) {
|
||||
*desc = ::strdup("Number of lookaside memory slots currently checked out");
|
||||
*desc = ::strdup("Number of lookaside memory slots currently checked out.");
|
||||
}
|
||||
else if (mType == Cache_Used) {
|
||||
*desc = ::strdup("Approximate number of bytes of heap memory used by all pager caches");
|
||||
*desc = ::strdup("Memory (approximate) used by all pager caches.");
|
||||
}
|
||||
else if (mType == Schema_Used) {
|
||||
*desc = ::strdup("Approximate number of bytes of heap memory used to store the schema for all databases associated with the connection");
|
||||
*desc = ::strdup("Memory (approximate) used to store the schema for all databases associated with the connection");
|
||||
}
|
||||
else if (mType == Stmt_Used) {
|
||||
*desc = ::strdup("Approximate number of bytes of heap and lookaside memory used by all prepared statements");
|
||||
*desc = ::strdup("Memory (approximate) used by all prepared statements");
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
@ -576,10 +576,16 @@ Connection::initialize(nsIFile *aDatabaseFile,
|
|||
}
|
||||
|
||||
nsRefPtr<nsIMemoryReporter> reporter;
|
||||
#if 0
|
||||
// FIXME: Bug 649867 explains why this is disabled.
|
||||
reporter =
|
||||
new StorageMemoryReporter(*this, StorageMemoryReporter::LookAside_Used);
|
||||
mMemoryReporters.AppendElement(reporter);
|
||||
#endif
|
||||
|
||||
// FIXME: These reporters overlap with storage/sqlite/pagecache and
|
||||
// storage/sqlite/other, and therefore double-count some memory. See bug
|
||||
// 653630 for details.
|
||||
reporter =
|
||||
new StorageMemoryReporter(*this, StorageMemoryReporter::Cache_Used);
|
||||
mMemoryReporters.AppendElement(reporter);
|
||||
|
@ -588,7 +594,8 @@ Connection::initialize(nsIFile *aDatabaseFile,
|
|||
new StorageMemoryReporter(*this, StorageMemoryReporter::Schema_Used);
|
||||
mMemoryReporters.AppendElement(reporter);
|
||||
|
||||
reporter = new StorageMemoryReporter(*this, StorageMemoryReporter::Stmt_Used);
|
||||
reporter =
|
||||
new StorageMemoryReporter(*this, StorageMemoryReporter::Stmt_Used);
|
||||
mMemoryReporters.AppendElement(reporter);
|
||||
|
||||
for (PRUint32 i = 0; i < mMemoryReporters.Length(); i++) {
|
||||
|
|
|
@ -151,14 +151,20 @@ GetStorageSQLiteOtherMemoryUsed(void *)
|
|||
}
|
||||
|
||||
NS_MEMORY_REPORTER_IMPLEMENT(StorageSQLitePageCacheMemoryUsed,
|
||||
"storage/sqlite/pagecache",
|
||||
"Memory in use by SQLite for the page cache",
|
||||
"heap-used/storage/sqlite/pagecache",
|
||||
"Memory used by SQLite for the page cache. "
|
||||
"This overlaps with the per-connection cache-used "
|
||||
"figure, thus over-counting some bytes. Bug "
|
||||
"653630 has the details.",
|
||||
GetStorageSQLitePageCacheMemoryUsed,
|
||||
nsnull)
|
||||
|
||||
NS_MEMORY_REPORTER_IMPLEMENT(StorageSQLiteOtherMemoryUsed,
|
||||
"storage/sqlite/other",
|
||||
"Memory in use by SQLite for other various reasons",
|
||||
"heap-used/storage/sqlite/other",
|
||||
"Memory used by SQLite for other various reasons."
|
||||
"This overlaps with the per-connection stmt-used "
|
||||
"and schema-used figures, thus over-counting some "
|
||||
"bytes. Bug 653630 has the details.",
|
||||
GetStorageSQLiteOtherMemoryUsed,
|
||||
nsnull)
|
||||
|
||||
|
|
|
@ -43,4 +43,8 @@ VPATH = @srcdir@
|
|||
|
||||
include $(DEPTH)/config/autoconf.mk
|
||||
|
||||
ifdef ENABLE_TESTS
|
||||
DIRS += tests
|
||||
endif
|
||||
|
||||
include $(topsrcdir)/config/rules.mk
|
||||
|
|
|
@ -13,13 +13,13 @@
|
|||
*
|
||||
* The Original Code is about:memory.
|
||||
*
|
||||
* The Initial Developer of the Original Code is
|
||||
* Mozilla Corporation
|
||||
* The Initial Developer of the Original Code is the Mozilla Foundation.
|
||||
* Portions created by the Initial Developer are Copyright (C) 2009
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
* Vladimir Vukicevic <vladimir@pobox.com>
|
||||
* Nicholas Nethercote <nnethercote@mozilla.com>
|
||||
*
|
||||
* 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
|
||||
|
@ -35,46 +35,24 @@
|
|||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
html {
|
||||
background: -moz-Dialog;
|
||||
color: -moz-DialogText;
|
||||
.mrValue {
|
||||
font-weight: bold;
|
||||
color: #400;
|
||||
}
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
padding: 0 1em;
|
||||
font: message-box;
|
||||
.mrPerc {
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-weight: bold;
|
||||
font-size: x-large;
|
||||
.mrName {
|
||||
font-style: italic;
|
||||
color: #004;
|
||||
}
|
||||
|
||||
label {
|
||||
-moz-user-select: text;
|
||||
.treeLine {
|
||||
color: #888;
|
||||
}
|
||||
|
||||
h2 {
|
||||
margin-top: 0;
|
||||
font-weight: bold;
|
||||
font-size: large;
|
||||
}
|
||||
|
||||
.memBox {
|
||||
max-width: 30em;
|
||||
background: -moz-Field;
|
||||
color: -moz-FieldText;
|
||||
margin-top: 25px;
|
||||
padding: 5px;
|
||||
border: 1px solid -moz-FieldText;
|
||||
border-radius: 10px 10px;
|
||||
}
|
||||
|
||||
.memValue {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.memOverview table {
|
||||
font-size: 120%;
|
||||
.option {
|
||||
font-size: 80%;
|
||||
-moz-user-select: none; /* no need to include this when cutting+pasting */
|
||||
}
|
||||
|
|
|
@ -14,13 +14,13 @@
|
|||
*
|
||||
* The Original Code is about:memory
|
||||
*
|
||||
* The Initial Developer of the Original Code is
|
||||
* the Mozilla Foundation.
|
||||
* The Initial Developer of the Original Code is the Mozilla Foundation.
|
||||
* Portions created by the Initial Developer are Copyright (C) 2009
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
* Vladimir Vukicevic <vladimir@pobox.com>
|
||||
* Nicholas Nethercote <nnethercote@mozilla.com>
|
||||
*
|
||||
* 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
|
||||
|
@ -36,180 +36,564 @@
|
|||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
"use strict";
|
||||
|
||||
const Cc = Components.classes;
|
||||
const Ci = Components.interfaces;
|
||||
|
||||
var gMemReporters = { };
|
||||
// Must use .href here instead of .search because "about:memory" is a
|
||||
// non-standard URL.
|
||||
var gVerbose = (location.href.split(/[\?,]/).indexOf("verbose") !== -1);
|
||||
|
||||
function $(n) {
|
||||
return document.getElementById(n);
|
||||
function onLoad()
|
||||
{
|
||||
var os = Cc["@mozilla.org/observer-service;1"].
|
||||
getService(Ci.nsIObserverService);
|
||||
os.notifyObservers(null, "child-memory-reporter-request", null);
|
||||
|
||||
os.addObserver(ChildMemoryListener, "child-memory-reporter-update", false);
|
||||
|
||||
update();
|
||||
}
|
||||
|
||||
function makeTableCell(content, c) {
|
||||
var td = document.createElement("td");
|
||||
if (typeof content == "string")
|
||||
content = document.createTextNode(content);
|
||||
td.appendChild(content);
|
||||
if (c)
|
||||
td.setAttribute("class", c);
|
||||
|
||||
return td;
|
||||
function onUnload()
|
||||
{
|
||||
var os = Cc["@mozilla.org/observer-service;1"].
|
||||
getService(Ci.nsIObserverService);
|
||||
os.removeObserver(ChildMemoryListener, "child-memory-reporter-update");
|
||||
}
|
||||
|
||||
function makeAbbrNode(str, title) {
|
||||
var abbr = document.createElement("abbr");
|
||||
var text = document.createTextNode(str);
|
||||
abbr.appendChild(text);
|
||||
abbr.setAttribute("title", title);
|
||||
|
||||
return abbr;
|
||||
function ChildMemoryListener(aSubject, aTopic, aData)
|
||||
{
|
||||
update();
|
||||
}
|
||||
|
||||
function makeTableRow() {
|
||||
var row = document.createElement("tr");
|
||||
|
||||
for (var i = 0; i < arguments.length; ++i) {
|
||||
var arg = arguments[i];
|
||||
if (typeof(arg) == "string") {
|
||||
row.appendChild(makeTableCell(arg));
|
||||
} else if (arg.__proto__ == Array.prototype) {
|
||||
row.appendChild(makeTableCell(makeAbbrNode(arg[0], arg[1])));
|
||||
} else {
|
||||
row.appendChild(arg);
|
||||
}
|
||||
}
|
||||
|
||||
return row;
|
||||
}
|
||||
|
||||
function setTextContent(node, s) {
|
||||
while (node.lastChild)
|
||||
node.removeChild(node.lastChild);
|
||||
|
||||
node.appendChild(document.createTextNode(s));
|
||||
}
|
||||
|
||||
function formatNumber(n) {
|
||||
var s = "";
|
||||
var neg = false;
|
||||
if (n < 0) {
|
||||
neg = true;
|
||||
n = -n;
|
||||
}
|
||||
|
||||
do {
|
||||
var k = n % 1000;
|
||||
if (n > 999) {
|
||||
if (k > 99)
|
||||
s = k + s;
|
||||
else if (k > 9)
|
||||
s = "0" + k + s;
|
||||
else
|
||||
s = "00" + k + s;
|
||||
} else {
|
||||
s = k + s;
|
||||
}
|
||||
|
||||
n = Math.floor(n / 1000);
|
||||
if (n > 0)
|
||||
s = "," + s;
|
||||
} while (n > 0);
|
||||
|
||||
return s;
|
||||
function $(n)
|
||||
{
|
||||
return document.getElementById(n);
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the content of the document with the most current memory information.
|
||||
* Top-level function that does the work of generating the page.
|
||||
*/
|
||||
function updateMemoryStatus()
|
||||
function update()
|
||||
{
|
||||
// if we have the standard reporters for mapped/allocated, put
|
||||
// those at the top
|
||||
if ("malloc/mapped" in gMemReporters &&
|
||||
"malloc/allocated" in gMemReporters)
|
||||
{
|
||||
// committed is the total amount of memory that we've touched, that is that we have
|
||||
// some kind of backing store for
|
||||
setTextContent($("memMappedValue"),
|
||||
formatNumber(gMemReporters["malloc/mapped"][0].memoryUsed));
|
||||
// First, clear the page contents. Necessary because update() might be
|
||||
// called more than once due to ChildMemoryListener.
|
||||
var content = $("content");
|
||||
content.parentNode.replaceChild(content.cloneNode(false), content);
|
||||
content = $("content");
|
||||
|
||||
// allocated is the amount of committed memory that we're actively using (i.e., that isn't free)
|
||||
setTextContent($("memInUseValue"),
|
||||
formatNumber(gMemReporters["malloc/allocated"][0].memoryUsed));
|
||||
var mgr = Cc["@mozilla.org/memory-reporter-manager;1"].
|
||||
getService(Ci.nsIMemoryReporterManager);
|
||||
|
||||
// Process each memory reporter:
|
||||
// - Make a copy of it into a sub-table indexed by its process. Each copy
|
||||
// looks like this:
|
||||
//
|
||||
// interface Tmr {
|
||||
// _tpath: string;
|
||||
// _description: string;
|
||||
// _memoryUsed: number;
|
||||
// }
|
||||
//
|
||||
// - The .path property is renamed ._tpath ("truncated path") in the copy
|
||||
// because the process name and ':' (if present) are removed.
|
||||
// - Note that copying mr.memoryUsed (which calls a C++ function under the
|
||||
// IDL covers) to tmr._memoryUsed for every reporter now means that the
|
||||
// results as consistent as possible -- measurements are made all at
|
||||
// once before most of the memory required to generate this page is
|
||||
// allocated.
|
||||
var tmrTable = {};
|
||||
var e = mgr.enumerateReporters();
|
||||
while (e.hasMoreElements()) {
|
||||
var mr = e.getNext().QueryInterface(Ci.nsIMemoryReporter);
|
||||
var process;
|
||||
var tmr = {};
|
||||
var i = mr.path.indexOf(':');
|
||||
if (i === -1) {
|
||||
process = "Main";
|
||||
tmr._tpath = mr.path;
|
||||
} else {
|
||||
$("memOverview").style.display = "none";
|
||||
process = mr.path.slice(0, i);
|
||||
tmr._tpath = mr.path.slice(i + 1);
|
||||
}
|
||||
tmr._description = mr.description;
|
||||
tmr._memoryUsed = mr.memoryUsed;
|
||||
|
||||
var mo = $("memOtherRows");
|
||||
while (mo.lastChild)
|
||||
mo.removeChild(mo.lastChild);
|
||||
|
||||
var otherCount = 0;
|
||||
|
||||
for each (var reporters in gMemReporters) {
|
||||
// There may be more than one reporter for each path. We take the sum
|
||||
// of them all for each description.
|
||||
var total = 0;
|
||||
reporters.forEach(function(reporter) {
|
||||
total += reporter.memoryUsed;
|
||||
});
|
||||
|
||||
var row = makeTableRow([reporters[0].path, reporters[0].description],
|
||||
makeTableCell(formatNumber(total), "memValue"));
|
||||
|
||||
mo.appendChild(row);
|
||||
|
||||
otherCount++;
|
||||
if (!tmrTable[process]) {
|
||||
tmrTable[process] = {};
|
||||
}
|
||||
|
||||
if (otherCount == 0) {
|
||||
var row = makeTableRow("No other information available.");
|
||||
mo.appendChild(row);
|
||||
var tmrs = tmrTable[process];
|
||||
if (tmrs[tmr._tpath]) {
|
||||
// Already an entry; must be a duplicated reporter. This can
|
||||
// happen legitimately. Sum the sizes.
|
||||
tmrs[tmr._tpath]._memoryUsed += tmr._memoryUsed;
|
||||
} else {
|
||||
tmrs[tmr._tpath] = tmr;
|
||||
}
|
||||
}
|
||||
|
||||
// Generate output for one process at a time. Always start with the
|
||||
// Main process.
|
||||
var text = genProcessText("Main", tmrTable["Main"]);
|
||||
for (var process in tmrTable) {
|
||||
if (process !== "Main") {
|
||||
text += genProcessText(process, tmrTable[process]);
|
||||
}
|
||||
}
|
||||
|
||||
// Generate verbosity option link at the bottom.
|
||||
text += gVerbose
|
||||
? "<span class='option'><a href='about:memory'>Less verbose</a></span>"
|
||||
: "<span class='option'><a href='about:memory?verbose'>More verbose</a></span>";
|
||||
|
||||
var div = document.createElement("div");
|
||||
div.innerHTML = text;
|
||||
content.appendChild(div);
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates gMemReporters to contain all the known memory reporters.
|
||||
* Generates the text for a single process.
|
||||
*
|
||||
* @param aProcess
|
||||
* The name of the process
|
||||
* @param aTmrs
|
||||
* Table of Tmrs for this process
|
||||
* @return The generated text
|
||||
*/
|
||||
function updateMemoryReporters()
|
||||
function genProcessText(aProcess, aTmrs)
|
||||
{
|
||||
gMemReporters = [];
|
||||
|
||||
var mgr = Cc["@mozilla.org/memory-reporter-manager;1"].
|
||||
getService(Ci.nsIMemoryReporterManager);
|
||||
|
||||
var e = mgr.enumerateReporters();
|
||||
while (e.hasMoreElements()) {
|
||||
var reporter = e.getNext().QueryInterface(Ci.nsIMemoryReporter);
|
||||
if (!gMemReporters[reporter.path]) {
|
||||
gMemReporters[reporter.path] = [];
|
||||
/**
|
||||
* From a list of memory reporters, builds a tree that mirrors the tree
|
||||
* structure that will be shown as output.
|
||||
*
|
||||
* @param aTreeName
|
||||
* The name of the tree; either "mapped" or "heap-used"
|
||||
* @param aTreeRootTpath
|
||||
* The tpath of the top node in the tree
|
||||
* @param aTreeRootDesc
|
||||
* The description of the top node in the tree
|
||||
* @param aOtherDescTail
|
||||
* Extra description for the end of the "aTreeName/other" entry
|
||||
* @param aOmitThresholdPerc
|
||||
* The threshold percentage; entries that account for less than
|
||||
* this fraction are aggregated
|
||||
* @return The built tree. The tree nodes have this structure:
|
||||
* interface Node {
|
||||
* _name: string;
|
||||
* _description: string;
|
||||
* _memoryUsed: number;
|
||||
* _kids: [Node];
|
||||
* }
|
||||
*/
|
||||
function buildTree(aTreeName, aTreeRootTpath, aTreeRootDesc, aOtherDescTail,
|
||||
aOmitThresholdPerc)
|
||||
{
|
||||
function findKid(aName, aKids)
|
||||
{
|
||||
for (var i = 0; i < aKids.length; i++) {
|
||||
if (aKids[i]._name === aName) {
|
||||
return aKids[i];
|
||||
}
|
||||
gMemReporters[reporter.path].push(reporter);
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
// We want to process all reporters that begin with 'aTreeName'.
|
||||
// First we build the tree but only filling in '_name' and '_kids'.
|
||||
var t = { _name:aTreeName, _kids:[] };
|
||||
for (var _tpath in aTmrs) {
|
||||
var tmr = aTmrs[_tpath];
|
||||
if (tmr._tpath.slice(0, aTreeName.length + 1) === aTreeName + "/") {
|
||||
var names = tmr._tpath.slice(aTreeName.length + 1).split('/');
|
||||
var u = t;
|
||||
for (var i = 0; i < names.length; i++) {
|
||||
var name = names[i];
|
||||
var uMatch = findKid(name, u._kids);
|
||||
if (uMatch) {
|
||||
u = uMatch;
|
||||
} else {
|
||||
var v = { _name:name, _kids:[] };
|
||||
u._kids.push(v);
|
||||
u = v;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Next, fill in '_description' and '_memoryUsed' for each node. For
|
||||
// interior nodes, '_memoryUsed' is computed by summing child nodes.
|
||||
function fillInTree(aT, aPretpath)
|
||||
{
|
||||
var tpath = aPretpath ? aPretpath + '/' + aT._name : aT._name;
|
||||
if (aT._kids.length === 0) {
|
||||
aT._memoryUsed = getBytes(aTmrs, tpath);
|
||||
aT._description = getDescription(aTmrs, tpath);
|
||||
} else {
|
||||
var bytes = 0;
|
||||
for (var i = 0; i < aT._kids.length; i++) {
|
||||
// Allow for -1 (ie. "unknown"), treat it like 0.
|
||||
var b = fillInTree(aT._kids[i], tpath);
|
||||
bytes += (b === -1 ? 0 : b);
|
||||
}
|
||||
aT._memoryUsed = bytes;
|
||||
aT._description = "The sum of all entries below " + tpath + ".";
|
||||
}
|
||||
return aT._memoryUsed;
|
||||
}
|
||||
fillInTree(t, "");
|
||||
|
||||
// Add the "aTreeName/other" node, which is derived from existing
|
||||
// nodes, then update the root node accordingly. (But don't do this
|
||||
// if the root node byte count is -1, ie. unknown).
|
||||
var nonOtherBytes = t._memoryUsed;
|
||||
var treeBytes = getBytes(aTmrs, aTreeRootTpath);
|
||||
if (treeBytes !== -1) {
|
||||
var otherBytes = treeBytes - nonOtherBytes;
|
||||
var other = {
|
||||
_name:"other",
|
||||
_description:"All unclassified " + aTreeName + " memory." +
|
||||
aOtherDescTail,
|
||||
_memoryUsed:otherBytes,
|
||||
_kids:[]
|
||||
};
|
||||
t._kids.push(other);
|
||||
}
|
||||
t._memoryUsed = treeBytes;
|
||||
t._description = aTreeRootDesc;
|
||||
|
||||
function shouldOmit(aBytes)
|
||||
{
|
||||
return !gVerbose &&
|
||||
treeBytes !== -1 &&
|
||||
(100 * aBytes / treeBytes) < aOmitThresholdPerc;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sort all kid nodes from largest to smallest and aggregate
|
||||
* insignificant nodes.
|
||||
*
|
||||
* @param aT
|
||||
* The tree
|
||||
*/
|
||||
function filterTree(aT)
|
||||
{
|
||||
var cmpTmrs = function(a, b) { return b._memoryUsed - a._memoryUsed };
|
||||
aT._kids.sort(cmpTmrs);
|
||||
|
||||
for (var i = 0; i < aT._kids.length; i++) {
|
||||
if (shouldOmit(aT._kids[i]._memoryUsed)) {
|
||||
// This sub-tree is below the significance threshold
|
||||
// Remove it and all remaining (smaller) sub-trees, and
|
||||
// replace them with a single aggregate node.
|
||||
var i0 = i;
|
||||
var aggBytes = 0;
|
||||
var aggNames = [];
|
||||
for ( ; i < aT._kids.length; i++) {
|
||||
aggBytes += aT._kids[i]._memoryUsed;
|
||||
aggNames.push(aT._kids[i]._name);
|
||||
}
|
||||
aT._kids.splice(i0);
|
||||
var n = i - i0;
|
||||
var tmrSub = {
|
||||
_name: "(" + n + " omitted)",
|
||||
_description: "Omitted sub-trees: " + aggNames.join(", ") + ".",
|
||||
_memoryUsed: aggBytes,
|
||||
_kids:[]
|
||||
};
|
||||
aT._kids[i0] = tmrSub;
|
||||
break;
|
||||
}
|
||||
filterTree(aT._kids[i]);
|
||||
}
|
||||
}
|
||||
filterTree(t);
|
||||
|
||||
return t;
|
||||
}
|
||||
|
||||
var mappedOtherDescTail =
|
||||
" This includes code and data segments, and thread stacks."
|
||||
var mappedRootDesc = getDescription(aTmrs, "mapped");
|
||||
// The threshold used here is much lower than the one for the heap-used
|
||||
// tree, because the "mapped" total size is so much bigger relative to
|
||||
// the interesting entries in the "mapped" tree.
|
||||
var mappedTree = buildTree("mapped", "mapped", mappedRootDesc,
|
||||
mappedOtherDescTail, 0.01);
|
||||
|
||||
var heapUsedOtherDescTail = "";
|
||||
var heapUsedRootDesc = "See mapped/heap/used above.";
|
||||
var heapUsedTree = buildTree("heap-used", "mapped/heap/used",
|
||||
heapUsedRootDesc, heapUsedOtherDescTail, 0.1);
|
||||
|
||||
// Nb: the newlines give nice spacing if we cut+paste into a text buffer.
|
||||
var text = "";
|
||||
text += "<h1>" + aProcess + " Process</h1>\n\n";
|
||||
text += genTreeText(mappedTree, "Mapped Memory");
|
||||
text += genTreeText(heapUsedTree, "Used Heap Memory");
|
||||
text += genOtherText(aTmrs);
|
||||
text += "<hr></hr>";
|
||||
return text;
|
||||
}
|
||||
|
||||
function ChildMemoryListener(subject, topic, data) {
|
||||
updateMemoryReporters();
|
||||
updateMemoryStatus();
|
||||
}
|
||||
|
||||
|
||||
function doLoad()
|
||||
/**
|
||||
* Converts a byte count to an appropriate string representation.
|
||||
*
|
||||
* @param aBytes
|
||||
* The byte count
|
||||
* @return The string representation
|
||||
*/
|
||||
function formatBytes(aBytes)
|
||||
{
|
||||
var os = Components.classes["@mozilla.org/observer-service;1"].
|
||||
getService(Components.interfaces.nsIObserverService);
|
||||
os.notifyObservers(null, "child-memory-reporter-request", null);
|
||||
var unit = gVerbose ? "B" : "MB";
|
||||
|
||||
os.addObserver(ChildMemoryListener, "child-memory-reporter-update", false);
|
||||
if (aBytes === -1) {
|
||||
return "??? " + unit;
|
||||
}
|
||||
|
||||
updateMemoryReporters();
|
||||
updateMemoryStatus();
|
||||
function formatInt(aN)
|
||||
{
|
||||
var neg = false;
|
||||
if (aN < 0) {
|
||||
neg = true;
|
||||
aN = -aN;
|
||||
}
|
||||
var s = "";
|
||||
while (true) {
|
||||
var k = aN % 1000;
|
||||
aN = Math.floor(aN / 1000);
|
||||
if (aN > 0) {
|
||||
if (k < 10) {
|
||||
s = ",00" + k + s;
|
||||
} else if (k < 100) {
|
||||
s = ",0" + k + s;
|
||||
} else {
|
||||
s = "," + k + s;
|
||||
}
|
||||
} else {
|
||||
s = k + s;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return neg ? "-" + s : s;
|
||||
}
|
||||
|
||||
var s;
|
||||
if (gVerbose) {
|
||||
s = formatInt(aBytes) + " " + unit;
|
||||
} else {
|
||||
var mbytes = (aBytes / (1024 * 1024)).toFixed(2);
|
||||
var a = String(mbytes).split(".");
|
||||
s = formatInt(a[0]) + "." + a[1] + " " + unit;
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
function doUnload()
|
||||
/**
|
||||
* Right-justifies a string in a field of a given width, padding as necessary
|
||||
*
|
||||
* @param aS
|
||||
* The string
|
||||
* @param aN
|
||||
* The field width
|
||||
* @param aC
|
||||
* The char used to pad
|
||||
* @return The string representation
|
||||
*/
|
||||
function pad(aS, aN, aC)
|
||||
{
|
||||
var os = Components.classes["@mozilla.org/observer-service;1"].
|
||||
getService(Components.interfaces.nsIObserverService);
|
||||
os.removeObserver(ChildMemoryListener, "child-memory-reporter-update");
|
||||
|
||||
var padding = "";
|
||||
var n2 = aN - aS.length;
|
||||
for (var i = 0; i < n2; i++) {
|
||||
padding += aC;
|
||||
}
|
||||
return padding + aS;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the byte count for a particular memory reporter.
|
||||
*
|
||||
* @param aTmrs
|
||||
* Table of Tmrs for this process
|
||||
* @param aTpath
|
||||
* The tpath of the memory reporter
|
||||
* @return The byte count
|
||||
*/
|
||||
function getBytes(aTmrs, aTpath)
|
||||
{
|
||||
var tmr = aTmrs[aTpath];
|
||||
if (tmr) {
|
||||
var bytes = tmr._memoryUsed;
|
||||
tmr.done = true;
|
||||
return bytes;
|
||||
}
|
||||
// Nb: this should never occur; "mapped" and "mapped/heap/used" should
|
||||
// always be registered, and all other tpaths have been extracted from
|
||||
// aTmrs and so the lookup will succeed. Return an obviously wrong
|
||||
// number that will likely be noticed.
|
||||
return -2 * 1024 * 1024;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the description for a particular memory reporter.
|
||||
*
|
||||
* @param aTmrs
|
||||
* Table of Tmrs for this process
|
||||
* @param aTpath
|
||||
* The tpath of the memory reporter
|
||||
* @return The description
|
||||
*/
|
||||
function getDescription(aTmrs, aTpath)
|
||||
{
|
||||
var r = aTmrs[aTpath];
|
||||
return r ? r._description : "???";
|
||||
}
|
||||
|
||||
function genMrValueText(aValue)
|
||||
{
|
||||
return "<span class='mrValue'>" + aValue + "</span>";
|
||||
}
|
||||
|
||||
function genMrNameText(aDesc, aName)
|
||||
{
|
||||
return "-- <span class='mrName' title=\"" + aDesc + "\">" +
|
||||
aName + "</span>\n";
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates the text for a particular tree, including its heading.
|
||||
*
|
||||
* @param aT
|
||||
* The tree
|
||||
* @param aTreeName
|
||||
* The tree's name
|
||||
* @return The generated text
|
||||
*/
|
||||
function genTreeText(aT, aTreeName)
|
||||
{
|
||||
var treeBytes = aT._memoryUsed;
|
||||
var treeBytesLength = formatBytes(treeBytes).length;
|
||||
|
||||
/**
|
||||
* Generates the text for a particular tree, without a heading.
|
||||
*
|
||||
* @param aT
|
||||
* The tree
|
||||
* @param aIndentGuide
|
||||
* Records what indentation is required for this tree. It has one
|
||||
* entry per level of indentation. For each entry, ._isLastKid
|
||||
* records whether the node in question is the last child, and
|
||||
* ._depth records how many chars of indentation are required.
|
||||
* @param aParentBytesLength
|
||||
* The length of the formatted byte count of the top node in the tree
|
||||
* @return The generated text
|
||||
*/
|
||||
function genTreeText2(aT, aIndentGuide, aParentBytesLength)
|
||||
{
|
||||
function repeatStr(aC, aN)
|
||||
{
|
||||
var s = "";
|
||||
for (var i = 0; i < aN; i++) {
|
||||
s += aC;
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
// Generate the indent. There's a subset of the Unicode "light"
|
||||
// box-drawing chars that are widely implemented in terminals, and
|
||||
// this code sticks to that subset to maximize the chance that
|
||||
// cutting and pasting about:memory output to a terminal will work
|
||||
// correctly:
|
||||
const kHorizontal = "\u2500",
|
||||
kVertical = "\u2502",
|
||||
kUpAndRight = "\u2514",
|
||||
kVerticalAndRight = "\u251c";
|
||||
var indent = "<span class='treeLine'>";
|
||||
if (aIndentGuide.length > 0) {
|
||||
for (var i = 0; i < aIndentGuide.length - 1; i++) {
|
||||
indent += aIndentGuide[i]._isLastKid ? " " : kVertical;
|
||||
indent += repeatStr(" ", aIndentGuide[i]._depth - 1);
|
||||
}
|
||||
indent += aIndentGuide[i]._isLastKid ? kUpAndRight : kVerticalAndRight;
|
||||
indent += repeatStr(kHorizontal, aIndentGuide[i]._depth - 1);
|
||||
}
|
||||
|
||||
// Indent more if this entry is narrower than its parent, and update
|
||||
// aIndentGuide accordingly.
|
||||
var tMemoryUsedStr = formatBytes(aT._memoryUsed);
|
||||
var tBytesLength = tMemoryUsedStr.length;
|
||||
var extraIndentLength = Math.max(aParentBytesLength - tBytesLength, 0);
|
||||
if (extraIndentLength > 0) {
|
||||
for (var i = 0; i < extraIndentLength; i++) {
|
||||
indent += kHorizontal;
|
||||
}
|
||||
aIndentGuide[aIndentGuide.length - 1]._depth += extraIndentLength;
|
||||
}
|
||||
indent += "</span>";
|
||||
|
||||
// Generate the percentage.
|
||||
var perc = "";
|
||||
if (treeBytes !== -1) {
|
||||
if (aT._memoryUsed === treeBytes) {
|
||||
perc = "100.0";
|
||||
} else {
|
||||
perc = (100 * aT._memoryUsed / treeBytes).toFixed(2);
|
||||
perc = pad(perc, 5, '0');
|
||||
}
|
||||
perc = "<span class='mrPerc'>(" + perc + "%)</span> ";
|
||||
}
|
||||
|
||||
var text = indent + genMrValueText(tMemoryUsedStr) + " " + perc +
|
||||
genMrNameText(aT._description, aT._name);
|
||||
|
||||
for (var i = 0; i < aT._kids.length; i++) {
|
||||
// 3 is the standard depth, the callee adjusts it if necessary.
|
||||
aIndentGuide.push({ _isLastKid:(i === aT._kids.length - 1), _depth:3 });
|
||||
text += genTreeText2(aT._kids[i], aIndentGuide, tBytesLength);
|
||||
aIndentGuide.pop();
|
||||
}
|
||||
return text;
|
||||
}
|
||||
|
||||
var text = genTreeText2(aT, [], treeBytesLength);
|
||||
// Nb: the newlines give nice spacing if we cut+paste into a text buffer.
|
||||
return "<h2>" + aTreeName + "</h2>\n<pre>" + text + "</pre>\n";
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates the text for the "Other Measurements" section.
|
||||
*
|
||||
* @param aTmrs
|
||||
* Table of Tmrs for this process
|
||||
* @return The generated text
|
||||
*/
|
||||
function genOtherText(aTmrs)
|
||||
{
|
||||
// Get the biggest not-yet-printed value, to determine the field width
|
||||
// for all these entries. These should all be "other" values, assuming
|
||||
// all paths are well-formed.
|
||||
var maxBytes = 0;
|
||||
for (var tpath in aTmrs) {
|
||||
var tmr = aTmrs[tpath];
|
||||
if (!tmr.done && tmr._memoryUsed > maxBytes) {
|
||||
maxBytes = tmr._memoryUsed;
|
||||
}
|
||||
}
|
||||
|
||||
// Generate text for the not-yet-yet-printed values.
|
||||
var maxBytesLength = formatBytes(maxBytes).length;
|
||||
var text = "";
|
||||
for (var tpath in aTmrs) {
|
||||
var tmr = aTmrs[tpath];
|
||||
if (!tmr.done) {
|
||||
text += genMrValueText(
|
||||
pad(formatBytes(tmr._memoryUsed), maxBytesLength, ' ')) + " ";
|
||||
text += genMrNameText(tmr._description, tmr._tpath);
|
||||
}
|
||||
}
|
||||
|
||||
// Nb: the newlines give nice spacing if we cut+paste into a text buffer.
|
||||
return "<h2>Other Measurements</h2>\n<pre>" + text + "</pre>\n";
|
||||
}
|
||||
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
-
|
||||
- Contributor(s):
|
||||
- Vladimir Vukicevic <vladimir@pobox.com>
|
||||
- Nicholas Nethercote <nnethercote@mozilla.com>
|
||||
-
|
||||
- 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
|
||||
|
@ -39,45 +40,12 @@
|
|||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
<head>
|
||||
<title>about:memory</title>
|
||||
|
||||
<link rel="stylesheet" href="chrome://global/skin/aboutMemory.css"
|
||||
type="text/css"/>
|
||||
|
||||
<script type="text/javascript"
|
||||
src="chrome://global/content/aboutMemory.js"/>
|
||||
<link rel="stylesheet" href="chrome://global/skin/aboutMemory.css" type="text/css"/>
|
||||
<link rel="stylesheet" href="chrome://global/skin/about.css" type="text/css"/>
|
||||
<script type="text/javascript" src="chrome://global/content/aboutMemory.js"/>
|
||||
</head>
|
||||
|
||||
<body onload="doLoad()" onunload="doUnload()">
|
||||
<h1>Memory Usage</h1>
|
||||
|
||||
<div id="memOverview" class="memOverview memBox">
|
||||
<h2>Overview</h2>
|
||||
<table border="0">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td width="100%">Memory mapped:</td>
|
||||
<td id="memMappedValue" class="memValue"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Memory in use:</td>
|
||||
<td id="memInUseValue" class="memValue"></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div id="memOther" class="memOther memBox">
|
||||
<h2>Other Information</h2>
|
||||
<table border="0">
|
||||
<thead>
|
||||
<tr>
|
||||
<th style="text-align: left" width="100%">Description</th>
|
||||
<th>Value</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="memOtherRows">
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</body>
|
||||
<!-- No newline before the div element! This avoids extraneous spaces when
|
||||
pasting the entire output after selecting it with Ctrl-a. -->
|
||||
<body onload="onLoad()" onunload="onUnload()"><div id="content"></div></body>
|
||||
</html>
|
||||
|
|
|
@ -0,0 +1,51 @@
|
|||
#
|
||||
# ***** 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 mozilla.org code.
|
||||
#
|
||||
# The Initial Developer of the Original Code is
|
||||
# Mozilla.org.
|
||||
# Portions created by the Initial Developer are Copyright (C) 2011
|
||||
# the Initial Developer. All Rights Reserved.
|
||||
#
|
||||
# Contributor(s):
|
||||
# Boris Zbarsky <bzbarsky@mit.edu> (Original author)
|
||||
#
|
||||
# Alternatively, the contents of this file may be used under the terms of
|
||||
# either of 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 *****
|
||||
|
||||
DEPTH = ../../../..
|
||||
topsrcdir = @top_srcdir@
|
||||
srcdir = @srcdir@
|
||||
VPATH = @srcdir@
|
||||
relativesrcdir = toolkit/components/aboutmemory/tests
|
||||
|
||||
include $(DEPTH)/config/autoconf.mk
|
||||
|
||||
DIRS = \
|
||||
chrome \
|
||||
$(NULL)
|
||||
|
||||
include $(topsrcdir)/config/rules.mk
|
|
@ -0,0 +1,53 @@
|
|||
#
|
||||
# ***** 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 mozilla.org code.
|
||||
#
|
||||
# The Initial Developer of the Original Code is
|
||||
# Mozilla.org.
|
||||
# Portions created by the Initial Developer are Copyright (C) 2005
|
||||
# the Initial Developer. All Rights Reserved.
|
||||
#
|
||||
# Contributor(s):
|
||||
#
|
||||
# Alternatively, the contents of this file may be used under the terms of
|
||||
# either of 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 *****
|
||||
|
||||
DEPTH = ../../../../..
|
||||
topsrcdir = @top_srcdir@
|
||||
srcdir = @srcdir@
|
||||
VPATH = @srcdir@
|
||||
relativesrcdir = toolkit/components/aboutmemory/tests/chrome
|
||||
|
||||
include $(DEPTH)/config/autoconf.mk
|
||||
include $(topsrcdir)/config/rules.mk
|
||||
|
||||
_CHROME_FILES = \
|
||||
test_aboutmemory.xul \
|
||||
$(NULL)
|
||||
|
||||
libs:: $(_CHROME_FILES)
|
||||
$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/chrome/$(relativesrcdir)
|
||||
|
|
@ -0,0 +1,267 @@
|
|||
<?xml version="1.0"?>
|
||||
<?xml-stylesheet type="text/css" href="chrome://global/skin"?>
|
||||
<?xml-stylesheet type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"?>
|
||||
<window title="about:memory"
|
||||
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
|
||||
<script type="application/javascript" src="chrome://mochikit/content/MochiKit/packed.js"/>
|
||||
<script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
|
||||
<script type="text/javascript" src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script>
|
||||
|
||||
<!-- test results are displayed in the html:body -->
|
||||
<body xmlns="http://www.w3.org/1999/xhtml"></body>
|
||||
|
||||
<!-- test code goes here -->
|
||||
<script type="application/javascript">
|
||||
<![CDATA[
|
||||
const Cc = Components.classes;
|
||||
const Ci = Components.interfaces;
|
||||
var mgr = Cc["@mozilla.org/memory-reporter-manager;1"].
|
||||
getService(Ci.nsIMemoryReporterManager);
|
||||
|
||||
// Remove all the real reporters; save them to restore at the end.
|
||||
var e = mgr.enumerateReporters();
|
||||
var realReporters = [];
|
||||
while (e.hasMoreElements()) {
|
||||
var mr = e.getNext().QueryInterface(Ci.nsIMemoryReporter);
|
||||
mgr.unregisterReporter(mr);
|
||||
realReporters.push(mr);
|
||||
}
|
||||
|
||||
// Setup various fake-but-deterministic reporters.
|
||||
const KB = 1024;
|
||||
const MB = KB * KB;
|
||||
fakeReporters = [
|
||||
{ path: "mapped", memoryUsed: 1000 * MB },
|
||||
{ path: "mapped/heap/used", memoryUsed: 500 * MB },
|
||||
{ path: "mapped/heap/unused", memoryUsed: 100 * MB },
|
||||
{ path: "mapped/a", memoryUsed: 222 * MB },
|
||||
{ path: "heap-used/a", memoryUsed: 99 * MB },
|
||||
{ path: "heap-used/b/a", memoryUsed: 80 * MB },
|
||||
{ path: "heap-used/b/b", memoryUsed: 75 * MB },
|
||||
{ path: "heap-used/b/c/a", memoryUsed: 44 * MB },
|
||||
{ path: "heap-used/b/c/b", memoryUsed: 33 * MB }, // aggregated
|
||||
{ path: "heap-used/c", memoryUsed: 123 * MB },
|
||||
{ path: "heap-used/d", memoryUsed: 499 * KB }, // aggregated
|
||||
{ path: "heap-used/e", memoryUsed: 100 * KB }, // aggregated
|
||||
{ path: "heap-used/f/g/h/i", memoryUsed: 20 * MB },
|
||||
{ path: "other1", memoryUsed: 111 * MB },
|
||||
{ path: "other2", memoryUsed: 222 * MB },
|
||||
|
||||
{ path: "2nd:mapped", memoryUsed: 1000 * MB },
|
||||
{ path: "2nd:mapped/heap/used", memoryUsed: 500 * MB },
|
||||
{ path: "2nd:mapped/a/b/c", memoryUsed: 499 * MB },
|
||||
{ path: "2nd:heap-used/a", memoryUsed: 400 * MB },
|
||||
{ path: "2nd:other1", memoryUsed: 777 * MB },
|
||||
|
||||
// -1 means "don't know"; this should be handled gracefully for
|
||||
// "mapped" and "mapped/heap/used".
|
||||
{ path: "3rd:mapped", memoryUsed: -1 },
|
||||
{ path: "3rd:mapped/heap/used", memoryUsed: -1 },
|
||||
{ path: "3rd:mapped/a/b", memoryUsed: 333 * MB },
|
||||
{ path: "3rd:heap-used/a/b", memoryUsed: 444 * MB },
|
||||
{ path: "3rd:other1", memoryUsed: 555 * MB }
|
||||
];
|
||||
for (var i = 0; i < fakeReporters.length; i++) {
|
||||
mgr.registerReporter(fakeReporters[i]);
|
||||
}
|
||||
]]>
|
||||
</script>
|
||||
|
||||
<iframe id="amFrame" src="about:memory"></iframe>
|
||||
<iframe id="amvFrame" src="about:memory?verbose"></iframe>
|
||||
|
||||
<script type="application/javascript">
|
||||
<![CDATA[
|
||||
var amExpectedText =
|
||||
"\
|
||||
Main Process\n\
|
||||
\n\
|
||||
Mapped Memory\n\
|
||||
1,000.00 MB (100.0%) -- mapped\n\
|
||||
├────600.00 MB (60.00%) -- heap\n\
|
||||
│ ├──500.00 MB (50.00%) -- used\n\
|
||||
│ └──100.00 MB (10.00%) -- unused\n\
|
||||
├────222.00 MB (22.20%) -- a\n\
|
||||
└────178.00 MB (17.80%) -- other\n\
|
||||
\n\
|
||||
Used Heap Memory\n\
|
||||
500.00 MB (100.0%) -- heap-used\n\
|
||||
├──232.00 MB (46.40%) -- b\n\
|
||||
│ ├───80.00 MB (16.00%) -- a\n\
|
||||
│ ├───77.00 MB (15.40%) -- c\n\
|
||||
│ │ ├──44.00 MB (08.80%) -- a\n\
|
||||
│ │ └──33.00 MB (06.60%) -- b\n\
|
||||
│ └───75.00 MB (15.00%) -- b\n\
|
||||
├──123.00 MB (24.60%) -- c\n\
|
||||
├───99.00 MB (19.80%) -- a\n\
|
||||
├───25.42 MB (05.08%) -- other\n\
|
||||
├───20.00 MB (04.00%) -- f\n\
|
||||
│ └──20.00 MB (04.00%) -- g\n\
|
||||
│ └──20.00 MB (04.00%) -- h\n\
|
||||
│ └──20.00 MB (04.00%) -- i\n\
|
||||
└────0.58 MB (00.12%) -- (2 omitted)\n\
|
||||
\n\
|
||||
Other Measurements\n\
|
||||
111.00 MB -- other1\n\
|
||||
222.00 MB -- other2\n\
|
||||
\n\
|
||||
2nd Process\n\
|
||||
\n\
|
||||
Mapped Memory\n\
|
||||
1,000.00 MB (100.0%) -- mapped\n\
|
||||
├────500.00 MB (50.00%) -- heap\n\
|
||||
│ └──500.00 MB (50.00%) -- used\n\
|
||||
├────499.00 MB (49.90%) -- a\n\
|
||||
│ └──499.00 MB (49.90%) -- b\n\
|
||||
│ └──499.00 MB (49.90%) -- c\n\
|
||||
└──────1.00 MB (00.10%) -- other\n\
|
||||
\n\
|
||||
Used Heap Memory\n\
|
||||
500.00 MB (100.0%) -- heap-used\n\
|
||||
├──400.00 MB (80.00%) -- a\n\
|
||||
└──100.00 MB (20.00%) -- other\n\
|
||||
\n\
|
||||
Other Measurements\n\
|
||||
777.00 MB -- other1\n\
|
||||
\n\
|
||||
3rd Process\n\
|
||||
\n\
|
||||
Mapped Memory\n\
|
||||
??? MB -- mapped\n\
|
||||
├──333.00 MB -- a\n\
|
||||
│ └──333.00 MB -- b\n\
|
||||
└──0.00 MB -- heap\n\
|
||||
└───??? MB -- used\n\
|
||||
\n\
|
||||
Used Heap Memory\n\
|
||||
??? MB -- heap-used\n\
|
||||
└──444.00 MB -- a\n\
|
||||
└──444.00 MB -- b\n\
|
||||
\n\
|
||||
Other Measurements\n\
|
||||
555.00 MB -- other1\n\
|
||||
\n\
|
||||
";
|
||||
|
||||
var amvExpectedText =
|
||||
"\
|
||||
Main Process\n\
|
||||
\n\
|
||||
Mapped Memory\n\
|
||||
1,048,576,000 B (100.0%) -- mapped\n\
|
||||
├────629,145,600 B (60.00%) -- heap\n\
|
||||
│ ├──524,288,000 B (50.00%) -- used\n\
|
||||
│ └──104,857,600 B (10.00%) -- unused\n\
|
||||
├────232,783,872 B (22.20%) -- a\n\
|
||||
└────186,646,528 B (17.80%) -- other\n\
|
||||
\n\
|
||||
Used Heap Memory\n\
|
||||
524,288,000 B (100.0%) -- heap-used\n\
|
||||
├──243,269,632 B (46.40%) -- b\n\
|
||||
│ ├───83,886,080 B (16.00%) -- a\n\
|
||||
│ ├───80,740,352 B (15.40%) -- c\n\
|
||||
│ │ ├──46,137,344 B (08.80%) -- a\n\
|
||||
│ │ └──34,603,008 B (06.60%) -- b\n\
|
||||
│ └───78,643,200 B (15.00%) -- b\n\
|
||||
├──128,974,848 B (24.60%) -- c\n\
|
||||
├──103,809,024 B (19.80%) -- a\n\
|
||||
├───26,649,600 B (05.08%) -- other\n\
|
||||
├───20,971,520 B (04.00%) -- f\n\
|
||||
│ └──20,971,520 B (04.00%) -- g\n\
|
||||
│ └──20,971,520 B (04.00%) -- h\n\
|
||||
│ └──20,971,520 B (04.00%) -- i\n\
|
||||
├──────510,976 B (00.10%) -- d\n\
|
||||
└──────102,400 B (00.02%) -- e\n\
|
||||
\n\
|
||||
Other Measurements\n\
|
||||
116,391,936 B -- other1\n\
|
||||
232,783,872 B -- other2\n\
|
||||
\n\
|
||||
2nd Process\n\
|
||||
\n\
|
||||
Mapped Memory\n\
|
||||
1,048,576,000 B (100.0%) -- mapped\n\
|
||||
├────524,288,000 B (50.00%) -- heap\n\
|
||||
│ └──524,288,000 B (50.00%) -- used\n\
|
||||
├────523,239,424 B (49.90%) -- a\n\
|
||||
│ └──523,239,424 B (49.90%) -- b\n\
|
||||
│ └──523,239,424 B (49.90%) -- c\n\
|
||||
└──────1,048,576 B (00.10%) -- other\n\
|
||||
\n\
|
||||
Used Heap Memory\n\
|
||||
524,288,000 B (100.0%) -- heap-used\n\
|
||||
├──419,430,400 B (80.00%) -- a\n\
|
||||
└──104,857,600 B (20.00%) -- other\n\
|
||||
\n\
|
||||
Other Measurements\n\
|
||||
814,743,552 B -- other1\n\
|
||||
\n\
|
||||
3rd Process\n\
|
||||
\n\
|
||||
Mapped Memory\n\
|
||||
??? B -- mapped\n\
|
||||
├──349,175,808 B -- a\n\
|
||||
│ └──349,175,808 B -- b\n\
|
||||
└────0 B -- heap\n\
|
||||
└──??? B -- used\n\
|
||||
\n\
|
||||
Used Heap Memory\n\
|
||||
??? B -- heap-used\n\
|
||||
└──465,567,744 B -- a\n\
|
||||
└──465,567,744 B -- b\n\
|
||||
\n\
|
||||
Other Measurements\n\
|
||||
581,959,680 B -- other1\n\
|
||||
\n\
|
||||
"
|
||||
|
||||
function finish()
|
||||
{
|
||||
// Unregister fake reporters, re-register the real reporters, just in
|
||||
// case subsequent tests rely on them.
|
||||
for (var i = 0; i < fakeReporters.length; i++) {
|
||||
mgr.unregisterReporter(fakeReporters[i]);
|
||||
}
|
||||
for (var i = 0; i < realReporters.length; i++) {
|
||||
mgr.registerReporter(realReporters[i]);
|
||||
}
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
// Cut+paste the entire page and check that the cut text matches what we
|
||||
// expect. This tests the output in general and also that the cutting and
|
||||
// pasting works as expected.
|
||||
function test(aFrame, aExpectedText, aNext) {
|
||||
document.querySelector("#" + aFrame).focus();
|
||||
SimpleTest.waitForClipboard(aExpectedText,
|
||||
function() {
|
||||
synthesizeKey("A", {accelKey: true});
|
||||
synthesizeKey("C", {accelKey: true});
|
||||
},
|
||||
aNext,
|
||||
function() {
|
||||
ok(false, "pasted text doesn't match for " + aFrame);
|
||||
finish();
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
addLoadEvent(function() {
|
||||
test(
|
||||
"amFrame",
|
||||
amExpectedText,
|
||||
function() {
|
||||
test(
|
||||
"amvFrame",
|
||||
amvExpectedText,
|
||||
function() {
|
||||
finish()
|
||||
}
|
||||
)
|
||||
}
|
||||
);
|
||||
});
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
]]>
|
||||
</script>
|
||||
</window>
|
|
@ -44,9 +44,39 @@ interface nsISimpleEnumerator;
|
|||
interface nsIMemoryReporter : nsISupports
|
||||
{
|
||||
/*
|
||||
* The path that this memory usage should be reported under.
|
||||
* The path that this memory usage should be reported under. Paths can
|
||||
* begin with a process name plus a colon, eg "Content:", but this is not
|
||||
* necessary for the main process. After the process name, paths are
|
||||
* '/'-delimited, eg. "a/b/c". There are three categories of paths.
|
||||
*
|
||||
* Normally "/"-delimited for organization.
|
||||
* - Paths starting with "mapped" represent non-overlapping regions of mapped
|
||||
* memory. Each one can be viewed as representing a path in a tree from
|
||||
* the root node ("mapped") to a leaf node. The one exception is the
|
||||
* special "mapped" path which represents the root of that tree. So, for
|
||||
* example, "mapped/heap/used", "mapped/heap/unused",
|
||||
* "mapped/js/mjit-code", mapped/js/tjit-code", plus the special "mapped"
|
||||
* path, define this tree:
|
||||
*
|
||||
* mapped
|
||||
* |--heap
|
||||
* | |--used
|
||||
* | \--unused
|
||||
* \--js
|
||||
* |--mjit-code
|
||||
* \--tjit-code
|
||||
*
|
||||
* "mapped/heap" would not be a valid path in this example because "heap"
|
||||
* is not a leaf node.
|
||||
*
|
||||
* - Paths starting with "heap-used" represent non-overlapping regions of
|
||||
* used heap memory. The "mapped" rules above apply equally here. The
|
||||
* exceptional path for this tree is "mapped/heap/used" (and so this tree
|
||||
* is a sub-tree of the "mapped" tree). When shown in about:memory, this
|
||||
* tree's root node is given the synonym "heap-used".
|
||||
*
|
||||
* - All other paths represent cross-cuttings memory regions, ie. ones that
|
||||
* may overlap arbitrarily with regions in the "mapped" and "heap-used"
|
||||
* trees.
|
||||
*/
|
||||
readonly attribute string path;
|
||||
|
||||
|
|
|
@ -41,6 +41,135 @@
|
|||
#include "nsMemoryReporterManager.h"
|
||||
#include "nsArrayEnumerator.h"
|
||||
|
||||
#if defined(XP_LINUX)
|
||||
|
||||
#include <unistd.h>
|
||||
static PRInt64 GetProcSelfStatmField(int n)
|
||||
{
|
||||
// There are more than two fields, but we're only interested in the first
|
||||
// two.
|
||||
static const int MAX_FIELD = 2;
|
||||
size_t fields[MAX_FIELD];
|
||||
NS_ASSERTION(n < MAX_FIELD, "bad field number");
|
||||
FILE *f = fopen("/proc/self/statm", "r");
|
||||
if (f) {
|
||||
int nread = fscanf(f, "%lu %lu", &fields[0], &fields[1]);
|
||||
fclose(f);
|
||||
return (PRInt64) ((nread == MAX_FIELD) ? fields[n]*getpagesize() : -1);
|
||||
}
|
||||
return (PRInt64) -1;
|
||||
}
|
||||
|
||||
static PRInt64 GetMapped(void *)
|
||||
{
|
||||
return GetProcSelfStatmField(0);
|
||||
}
|
||||
|
||||
static PRInt64 GetResident(void *)
|
||||
{
|
||||
return GetProcSelfStatmField(1);
|
||||
}
|
||||
|
||||
#elif defined(XP_MACOSX)
|
||||
|
||||
#include <mach/mach_init.h>
|
||||
#include <mach/task.h>
|
||||
|
||||
static bool GetTaskBasicInfo(struct task_basic_info *ti)
|
||||
{
|
||||
mach_msg_type_number_t count = TASK_BASIC_INFO_COUNT;
|
||||
kern_return_t kr = task_info(mach_task_self(), TASK_BASIC_INFO,
|
||||
(task_info_t)ti, &count);
|
||||
return kr == KERN_SUCCESS;
|
||||
}
|
||||
|
||||
// Getting a sensible "mapped" number on Mac is difficult. The following is
|
||||
// easy and (I think) corresponds to the VSIZE figure reported by 'top' and
|
||||
// 'ps', but that includes shared memory and so is always absurdly high. This
|
||||
// doesn't really matter as the "mapped" figure is never that useful.
|
||||
static PRInt64 GetMapped(void *)
|
||||
{
|
||||
task_basic_info ti;
|
||||
return (PRInt64) (GetTaskBasicInfo(&ti) ? ti.virtual_size : -1);
|
||||
}
|
||||
|
||||
static PRInt64 GetResident(void *)
|
||||
{
|
||||
task_basic_info ti;
|
||||
return (PRInt64) (GetTaskBasicInfo(&ti) ? ti.resident_size : -1);
|
||||
}
|
||||
|
||||
#elif defined(XP_WIN)
|
||||
|
||||
#include <windows.h>
|
||||
#include <psapi.h>
|
||||
|
||||
static PRInt64 GetMapped(void *)
|
||||
{
|
||||
#if MOZ_WINSDK_TARGETVER >= MOZ_NTDDI_LONGHORN
|
||||
PROCESS_MEMORY_COUNTERS_EX pmcex;
|
||||
pmcex.cb = sizeof(PROCESS_MEMORY_COUNTERS_EX);
|
||||
|
||||
if (!GetProcessMemoryInfo(GetCurrentProcess(),
|
||||
(PPROCESS_MEMORY_COUNTERS) &pmcex, sizeof(pmcex)))
|
||||
return (PRInt64) -1;
|
||||
|
||||
return pmcex.PrivateUsage;
|
||||
#else
|
||||
return (PRInt64) -1;
|
||||
#endif
|
||||
}
|
||||
|
||||
static PRInt64 GetResident(void *)
|
||||
{
|
||||
PROCESS_MEMORY_COUNTERS pmc;
|
||||
pmc.cb = sizeof(PROCESS_MEMORY_COUNTERS);
|
||||
|
||||
if (!GetProcessMemoryInfo(GetCurrentProcess(), &pmc, sizeof(pmc)))
|
||||
return (PRInt64) -1;
|
||||
|
||||
return pmc.WorkingSetSize;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
static PRInt64 GetMapped(void *)
|
||||
{
|
||||
return (PRInt64) -1;
|
||||
}
|
||||
|
||||
static PRInt64 GetResident(void *)
|
||||
{
|
||||
return (PRInt64) -1;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
// aboutMemory.js requires that this reporter always be registered, even if the
|
||||
// byte count returned is always -1.
|
||||
NS_MEMORY_REPORTER_IMPLEMENT(Mapped,
|
||||
"mapped",
|
||||
"Memory mapped by the process. Note that 'resident' is a better measure "
|
||||
"of memory resources used by the process. "
|
||||
"On Windows (XP SP2 or later only) this is the private usage and does not "
|
||||
"include memory shared with other processes. "
|
||||
"On Mac and Linux this is the vsize figure as reported by 'top' or 'ps' "
|
||||
"and includes memory shared with other processes; on Mac the amount of "
|
||||
"shared memory can be very high and so this figure is of limited use.",
|
||||
GetMapped,
|
||||
NULL)
|
||||
|
||||
NS_MEMORY_REPORTER_IMPLEMENT(Resident,
|
||||
"resident",
|
||||
"Memory mapped by the process that is present in physical memory, "
|
||||
"also known as the resident set size (RSS). This is the best single "
|
||||
"figure to use when considering the memory resources used by the process, "
|
||||
"but it depends both on other processes being run and details of the OS "
|
||||
"kernel and so is best used for comparing the memory usage of a single "
|
||||
"process at different points in time.",
|
||||
GetResident,
|
||||
NULL)
|
||||
|
||||
/**
|
||||
** memory reporter implementation for jemalloc and OSX malloc,
|
||||
** to obtain info on total memory in use (that we know about,
|
||||
|
@ -66,145 +195,128 @@ extern void jemalloc_stats(jemalloc_stats_t* stats)
|
|||
#endif // MOZ_MEMORY
|
||||
|
||||
#if HAVE_JEMALLOC_STATS
|
||||
# define HAVE_MALLOC_REPORTERS 1
|
||||
|
||||
PRInt64 getMallocMapped(void *) {
|
||||
jemalloc_stats_t stats;
|
||||
jemalloc_stats(&stats);
|
||||
return (PRInt64) stats.mapped;
|
||||
}
|
||||
|
||||
PRInt64 getMallocAllocated(void *) {
|
||||
static PRInt64 GetMappedHeapUsed(void *)
|
||||
{
|
||||
jemalloc_stats_t stats;
|
||||
jemalloc_stats(&stats);
|
||||
return (PRInt64) stats.allocated;
|
||||
}
|
||||
|
||||
PRInt64 getMallocCommitted(void *) {
|
||||
static PRInt64 GetMappedHeapUnused(void *)
|
||||
{
|
||||
jemalloc_stats_t stats;
|
||||
jemalloc_stats(&stats);
|
||||
return (PRInt64) (stats.mapped - stats.allocated);
|
||||
}
|
||||
|
||||
static PRInt64 GetHeapCommitted(void *)
|
||||
{
|
||||
jemalloc_stats_t stats;
|
||||
jemalloc_stats(&stats);
|
||||
return (PRInt64) stats.committed;
|
||||
}
|
||||
|
||||
PRInt64 getMallocDirty(void *) {
|
||||
static PRInt64 GetHeapDirty(void *)
|
||||
{
|
||||
jemalloc_stats_t stats;
|
||||
jemalloc_stats(&stats);
|
||||
return (PRInt64) stats.dirty;
|
||||
}
|
||||
|
||||
NS_MEMORY_REPORTER_IMPLEMENT(HeapCommitted,
|
||||
"heap-committed",
|
||||
"Memory mapped by the heap allocator that is "
|
||||
"committed, i.e. in physical memory or paged to "
|
||||
"disk.",
|
||||
GetHeapCommitted,
|
||||
NULL)
|
||||
|
||||
NS_MEMORY_REPORTER_IMPLEMENT(HeapDirty,
|
||||
"heap-dirty",
|
||||
"Memory mapped by the heap allocator that is "
|
||||
"committed but unused.",
|
||||
GetHeapDirty,
|
||||
NULL)
|
||||
|
||||
#elif defined(XP_MACOSX) && !defined(MOZ_MEMORY)
|
||||
#define HAVE_MALLOC_REPORTERS 1
|
||||
#include <malloc/malloc.h>
|
||||
|
||||
static PRInt64 getMallocAllocated(void *) {
|
||||
static PRInt64 GetMappedHeapUsed(void *)
|
||||
{
|
||||
struct mstats stats = mstats();
|
||||
return (PRInt64) stats.bytes_used;
|
||||
}
|
||||
|
||||
static PRInt64 getMallocMapped(void *) {
|
||||
static PRInt64 GetMappedHeapUnused(void *)
|
||||
{
|
||||
struct mstats stats = mstats();
|
||||
return (PRInt64) stats.bytes_total;
|
||||
return (PRInt64) (stats.bytes_total - stats.bytes_used);
|
||||
}
|
||||
|
||||
static PRInt64 getMallocDefaultCommitted(void *) {
|
||||
static PRInt64 GetHeapZone0Committed(void *)
|
||||
{
|
||||
malloc_statistics_t stats;
|
||||
malloc_zone_statistics(malloc_default_zone(), &stats);
|
||||
return stats.size_in_use;
|
||||
}
|
||||
|
||||
static PRInt64 getMallocDefaultAllocated(void *) {
|
||||
static PRInt64 GetHeapZone0Used(void *)
|
||||
{
|
||||
malloc_statistics_t stats;
|
||||
malloc_zone_statistics(malloc_default_zone(), &stats);
|
||||
return stats.size_allocated;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
#ifdef HAVE_MALLOC_REPORTERS
|
||||
NS_MEMORY_REPORTER_IMPLEMENT(MallocAllocated,
|
||||
"malloc/allocated",
|
||||
"Malloc bytes allocated (in use by application)",
|
||||
getMallocAllocated,
|
||||
NS_MEMORY_REPORTER_IMPLEMENT(HeapZone0Committed,
|
||||
"heap-zone0-committed",
|
||||
"Memory mapped by the heap allocator that is "
|
||||
"committed in the default zone.",
|
||||
GetHeapZone0Committed,
|
||||
NULL)
|
||||
|
||||
NS_MEMORY_REPORTER_IMPLEMENT(MallocMapped,
|
||||
"malloc/mapped",
|
||||
"Malloc bytes mapped (not necessarily committed)",
|
||||
getMallocMapped,
|
||||
NS_MEMORY_REPORTER_IMPLEMENT(HeapZone0Used,
|
||||
"heap-zone0-used",
|
||||
"Memory mapped by the heap allocator in the "
|
||||
"default zone that is available for use by the "
|
||||
"application.",
|
||||
GetHeapZone0Used,
|
||||
NULL)
|
||||
|
||||
#if defined(HAVE_JEMALLOC_STATS)
|
||||
NS_MEMORY_REPORTER_IMPLEMENT(MallocCommitted,
|
||||
"malloc/committed",
|
||||
"Malloc bytes committed (readable/writable)",
|
||||
getMallocCommitted,
|
||||
NULL)
|
||||
|
||||
NS_MEMORY_REPORTER_IMPLEMENT(MallocDirty,
|
||||
"malloc/dirty",
|
||||
"Malloc bytes dirty (committed unused pages)",
|
||||
getMallocDirty,
|
||||
NULL)
|
||||
#elif defined(XP_MACOSX) && !defined(MOZ_MEMORY)
|
||||
NS_MEMORY_REPORTER_IMPLEMENT(MallocDefaultCommitted,
|
||||
"malloc/zone0/committed",
|
||||
"Malloc bytes committed (r/w) in default zone",
|
||||
getMallocDefaultCommitted,
|
||||
NULL)
|
||||
|
||||
NS_MEMORY_REPORTER_IMPLEMENT(MallocDefaultAllocated,
|
||||
"malloc/zone0/allocated",
|
||||
"Malloc bytes allocated (in use) in default zone",
|
||||
getMallocDefaultAllocated,
|
||||
NULL)
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef XP_WIN
|
||||
#include <windows.h>
|
||||
#include <psapi.h>
|
||||
|
||||
static PRInt64 GetWin32PrivateBytes(void *) {
|
||||
#if MOZ_WINSDK_TARGETVER >= MOZ_NTDDI_LONGHORN
|
||||
PROCESS_MEMORY_COUNTERS_EX pmcex;
|
||||
pmcex.cb = sizeof(PROCESS_MEMORY_COUNTERS_EX);
|
||||
|
||||
if (!GetProcessMemoryInfo(GetCurrentProcess(),
|
||||
(PPROCESS_MEMORY_COUNTERS) &pmcex,
|
||||
sizeof(PROCESS_MEMORY_COUNTERS_EX)))
|
||||
return 0;
|
||||
|
||||
return pmcex.PrivateUsage;
|
||||
#else
|
||||
return 0;
|
||||
#endif
|
||||
|
||||
static PRInt64 GetMappedHeapUsed(void *)
|
||||
{
|
||||
return (PRInt64) -1;
|
||||
}
|
||||
|
||||
static PRInt64 GetWin32WorkingSetSize(void *) {
|
||||
PROCESS_MEMORY_COUNTERS pmc;
|
||||
pmc.cb = sizeof(PROCESS_MEMORY_COUNTERS);
|
||||
|
||||
if (!GetProcessMemoryInfo(GetCurrentProcess(), &pmc, sizeof(pmc)))
|
||||
return 0;
|
||||
|
||||
return pmc.WorkingSetSize;
|
||||
static PRInt64 GetMappedHeapUnused(void *)
|
||||
{
|
||||
return (PRInt64) -1;
|
||||
}
|
||||
|
||||
NS_MEMORY_REPORTER_IMPLEMENT(Win32WorkingSetSize,
|
||||
"win32/workingset",
|
||||
"Win32 working set size",
|
||||
GetWin32WorkingSetSize,
|
||||
nsnull);
|
||||
|
||||
NS_MEMORY_REPORTER_IMPLEMENT(Win32PrivateBytes,
|
||||
"win32/privatebytes",
|
||||
"Win32 private bytes (cannot be shared with other processes). (Available only on Windows XP SP2 or later.)",
|
||||
GetWin32PrivateBytes,
|
||||
nsnull);
|
||||
#endif
|
||||
|
||||
// aboutMemory.js requires that this reporter always be registered, even if the
|
||||
// byte count returned is always -1.
|
||||
NS_MEMORY_REPORTER_IMPLEMENT(MappedHeapUsed,
|
||||
"mapped/heap/used",
|
||||
"Memory mapped by the heap allocator that is available for use by the "
|
||||
"application. This may exceed the amount of memory requested by the "
|
||||
"application due to the allocator rounding up request sizes. "
|
||||
"(The exact amount requested is not measured.) "
|
||||
"This is usually the best figure for developers to focus on when trying "
|
||||
"to reduce memory consumption.",
|
||||
GetMappedHeapUsed,
|
||||
NULL)
|
||||
|
||||
NS_MEMORY_REPORTER_IMPLEMENT(MappedHeapUnused,
|
||||
"mapped/heap/unused",
|
||||
"Memory mapped by the heap allocator and not available for use by the "
|
||||
"application. This can grow large if the heap allocator is holding onto "
|
||||
"memory that the application has freed.",
|
||||
GetMappedHeapUnused,
|
||||
NULL)
|
||||
|
||||
/**
|
||||
** nsMemoryReporterManager implementation
|
||||
**/
|
||||
|
@ -218,32 +330,20 @@ nsMemoryReporterManager::Init()
|
|||
if (!jemalloc_stats)
|
||||
return NS_ERROR_FAILURE;
|
||||
#endif
|
||||
/*
|
||||
* Register our core reporters
|
||||
*/
|
||||
|
||||
#define REGISTER(_x) RegisterReporter(new NS_MEMORY_REPORTER_NAME(_x))
|
||||
|
||||
/*
|
||||
* Register our core jemalloc/malloc reporters
|
||||
*/
|
||||
#ifdef HAVE_MALLOC_REPORTERS
|
||||
REGISTER(MallocAllocated);
|
||||
REGISTER(MallocMapped);
|
||||
REGISTER(Mapped);
|
||||
REGISTER(MappedHeapUsed);
|
||||
REGISTER(MappedHeapUnused);
|
||||
REGISTER(Resident);
|
||||
|
||||
#if defined(HAVE_JEMALLOC_STATS)
|
||||
REGISTER(MallocCommitted);
|
||||
REGISTER(MallocDirty);
|
||||
REGISTER(HeapCommitted);
|
||||
REGISTER(HeapDirty);
|
||||
#elif defined(XP_MACOSX) && !defined(MOZ_MEMORY)
|
||||
REGISTER(MallocDefaultCommitted);
|
||||
REGISTER(MallocDefaultAllocated);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef XP_WIN
|
||||
#if MOZ_WINSDK_TARGETVER >= MOZ_NTDDI_LONGHORN
|
||||
REGISTER(Win32PrivateBytes);
|
||||
#endif
|
||||
REGISTER(Win32WorkingSetSize);
|
||||
REGISTER(HeapZone0Committed);
|
||||
REGISTER(HeapZone0Used);
|
||||
#endif
|
||||
|
||||
return NS_OK;
|
||||
|
@ -262,7 +362,7 @@ NS_IMETHODIMP
|
|||
nsMemoryReporterManager::EnumerateReporters(nsISimpleEnumerator **result)
|
||||
{
|
||||
nsresult rv;
|
||||
mozilla::MutexAutoLock autoLock(mMutex);
|
||||
mozilla::MutexAutoLock autoLock(mMutex);
|
||||
rv = NS_NewArrayEnumerator(result, mReporters);
|
||||
return rv;
|
||||
}
|
||||
|
@ -270,7 +370,7 @@ nsMemoryReporterManager::EnumerateReporters(nsISimpleEnumerator **result)
|
|||
NS_IMETHODIMP
|
||||
nsMemoryReporterManager::RegisterReporter(nsIMemoryReporter *reporter)
|
||||
{
|
||||
mozilla::MutexAutoLock autoLock(mMutex);
|
||||
mozilla::MutexAutoLock autoLock(mMutex);
|
||||
if (mReporters.IndexOf(reporter) != -1)
|
||||
return NS_ERROR_FAILURE;
|
||||
|
||||
|
@ -281,7 +381,7 @@ nsMemoryReporterManager::RegisterReporter(nsIMemoryReporter *reporter)
|
|||
NS_IMETHODIMP
|
||||
nsMemoryReporterManager::UnregisterReporter(nsIMemoryReporter *reporter)
|
||||
{
|
||||
mozilla::MutexAutoLock autoLock(mMutex);
|
||||
mozilla::MutexAutoLock autoLock(mMutex);
|
||||
if (!mReporters.RemoveObject(reporter))
|
||||
return NS_ERROR_FAILURE;
|
||||
|
||||
|
@ -295,11 +395,11 @@ nsMemoryReporter::nsMemoryReporter(nsCString& prefix,
|
|||
nsCString& desc,
|
||||
PRInt64 memoryUsed)
|
||||
: mDesc(desc)
|
||||
, mMemoryUsed(memoryUsed)
|
||||
, mMemoryUsed(memoryUsed)
|
||||
{
|
||||
if (!prefix.IsEmpty()) {
|
||||
mPath.Append(prefix);
|
||||
mPath.Append(NS_LITERAL_CSTRING(" - "));
|
||||
mPath.Append(prefix);
|
||||
mPath.Append(NS_LITERAL_CSTRING(":"));
|
||||
}
|
||||
mPath.Append(path);
|
||||
}
|
||||
|
@ -310,20 +410,20 @@ nsMemoryReporter::~nsMemoryReporter()
|
|||
|
||||
NS_IMETHODIMP nsMemoryReporter::GetPath(char **aPath)
|
||||
{
|
||||
*aPath = strdup(mPath.get());
|
||||
return NS_OK;
|
||||
*aPath = strdup(mPath.get());
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP nsMemoryReporter::GetDescription(char **aDescription)
|
||||
{
|
||||
*aDescription = strdup(mDesc.get());
|
||||
return NS_OK;
|
||||
*aDescription = strdup(mDesc.get());
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP nsMemoryReporter::GetMemoryUsed(PRInt64 *aMemoryUsed)
|
||||
{
|
||||
*aMemoryUsed = mMemoryUsed;
|
||||
return NS_OK;
|
||||
*aMemoryUsed = mMemoryUsed;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче