Bug 657327 - about:memory: merge the "mapped" and "heap used" trees, and make the resulting tree flatter. r=sdwilsh,Jesse, sr=roc.

This commit is contained in:
Nicholas Nethercote 2011-05-22 19:49:56 -07:00
parent 1f12055bca
commit e942f8f50a
18 changed files with 608 additions and 437 deletions

View File

@ -172,15 +172,16 @@ static PRInt64 GetCanvasMemoryUsed(void *) {
return gCanvasMemoryUsed;
}
// This isn't "heap-used/content/canvas/2d-pixel-bytes" because the pixels of a
// canvas may not be stored on the heap. And if they are, they will be tracked
// by the underlying surface implementations. See bug 655638 for details.
// This is MR_OTHER because it's not always clear where in memory the pixels of
// a canvas are stored. Furthermore, this memory will be tracked by the
// underlying surface implementations. See bug 655638 for details.
NS_MEMORY_REPORTER_IMPLEMENT(CanvasMemory,
"canvas-2d-pixel-bytes",
"Memory used by 2D canvases. Each canvas "
"requires (width * height * 4) bytes.",
GetCanvasMemoryUsed,
NULL)
"canvas-2d-pixel-bytes",
MR_OTHER,
"Memory used by 2D canvases. Each canvas requires (width * height * 4) "
"bytes.",
GetCanvasMemoryUsed,
NULL)
static void
CopyContext(gfxContext* dest, gfxContext* src)

View File

@ -313,9 +313,11 @@ ContentChild::RecvPMemoryReportRequestConstructor(PMemoryReportRequestChild* chi
r->GetNext(getter_AddRefs(report));
nsCString path;
PRInt32 kind;
nsCString desc;
PRInt64 memoryUsed;
report->GetPath(getter_Copies(path));
report->GetKind(&kind);
report->GetDescription(getter_Copies(desc));
report->GetMemoryUsed(&memoryUsed);
@ -323,6 +325,7 @@ ContentChild::RecvPMemoryReportRequestConstructor(PMemoryReportRequestChild* chi
MemoryReport memreport(nsPrintfCString(maxLength, "Content (%d)",
getpid()),
path,
kind,
desc,
memoryUsed);

View File

@ -692,11 +692,13 @@ ContentParent::SetChildMemoryReporters(const InfallibleTArray<MemoryReport>& rep
nsCString prefix = report[i].prefix();
nsCString path = report[i].path();
PRInt32 kind = report[i].kind();
nsCString desc = report[i].desc();
PRInt64 memoryUsed = report[i].memoryUsed();
nsRefPtr<nsMemoryReporter> r = new nsMemoryReporter(prefix,
path,
kind,
desc,
memoryUsed);
mMemoryReporters.AppendObject(r);

View File

@ -44,6 +44,7 @@ namespace dom {
struct MemoryReport {
nsCString prefix;
nsCString path;
PRInt32 kind;
nsCString desc;
PRInt64 memoryUsed;
};

View File

@ -543,30 +543,30 @@ gfxASurface::MovePixels(const nsIntRect& aSourceRect,
/** Memory reporting **/
static const char *sSurfaceNamesForSurfaceType[] = {
"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"
"explicit/gfx/surface/image",
"explicit/gfx/surface/pdf",
"explicit/gfx/surface/ps",
"explicit/gfx/surface/xlib",
"explicit/gfx/surface/xcb",
"explicit/gfx/surface/glitz",
"explicit/gfx/surface/quartz",
"explicit/gfx/surface/win32",
"explicit/gfx/surface/beos",
"explicit/gfx/surface/directfb",
"explicit/gfx/surface/svg",
"explicit/gfx/surface/os2",
"explicit/gfx/surface/win32printing",
"explicit/gfx/surface/quartzimage",
"explicit/gfx/surface/script",
"explicit/gfx/surface/qpainter",
"explicit/gfx/surface/recording",
"explicit/gfx/surface/vg",
"explicit/gfx/surface/gl",
"explicit/gfx/surface/drm",
"explicit/gfx/surface/tee",
"explicit/gfx/surface/xml",
"explicit/gfx/surface/skia",
"explicit/gfx/surface/d2d"
};
PR_STATIC_ASSERT(NS_ARRAY_LENGTH(sSurfaceNamesForSurfaceType) == gfxASurface::SurfaceTypeMax);
@ -580,7 +580,7 @@ SurfaceMemoryReporterPathForType(gfxASurface::gfxSurfaceType aType)
{
if (aType < 0 ||
aType >= gfxASurface::SurfaceTypeMax)
return "heap-used/gfx/surface/unknown";
return "explicit/gfx/surface/unknown";
return sSurfaceNamesForSurfaceType[aType];
}
@ -604,8 +604,13 @@ public:
return NS_OK;
}
NS_IMETHOD GetKind(PRInt32 *kind) {
*kind = MR_HEAP;
return NS_OK;
}
NS_IMETHOD GetDescription(char **desc) {
*desc = strdup("Memory used by gfx surface of given type.");
*desc = strdup("Memory used by gfx surface of the given type.");
return NS_OK;
}

View File

@ -100,6 +100,11 @@ public:
return NS_OK;
}
NS_IMETHOD GetKind(PRInt32 *kind) {
*kind = MR_OTHER;
return NS_OK;
}
NS_IMETHOD GetDescription(char **desc) {
*desc = strdup("Memory used by the Direct2D internal surface cache.");
return NS_OK;
@ -127,6 +132,11 @@ public:
return NS_OK;
}
NS_IMETHOD GetKind(PRInt32 *kind) {
*kind = MR_OTHER;
return NS_OK;
}
NS_IMETHOD GetDescription(char **desc) {
*desc = strdup("Video memory used by D2D surfaces");
return NS_OK;

View File

@ -52,17 +52,18 @@ static PRInt64 GetShmemAllocated(void*) { return gShmemAllocated; }
static PRInt64 GetShmemMapped(void*) { return gShmemMapped; }
NS_MEMORY_REPORTER_IMPLEMENT(ShmemAllocated,
"shmem-allocated",
"Memory shared with other processes that is "
"accessible (but not necessarily mapped).",
GetShmemAllocated,
nsnull)
"shmem-allocated",
MR_OTHER,
"Memory shared with other processes that is accessible (but not "
"necessarily mapped).",
GetShmemAllocated,
nsnull)
NS_MEMORY_REPORTER_IMPLEMENT(ShmemMapped,
"shmem-mapped",
"Memory shared with other processes that is "
"mapped into the address space.",
GetShmemMapped,
nsnull)
"shmem-mapped",
MR_OTHER,
"Memory shared with other processes that is mapped into the address space.",
GetShmemMapped,
nsnull)
SharedMemory::SharedMemory()
: mAllocSize(0)

View File

@ -1278,16 +1278,18 @@ protected:
static XPConnectGCChunkAllocator gXPCJSChunkAllocator;
NS_MEMORY_REPORTER_IMPLEMENT(XPConnectJSGCHeap,
#ifdef MOZ_MEMORY
"heap-used/js/gc-heap",
#define JS_GC_HEAP_KIND MR_HEAP
#else
"mapped/js/gc-heap",
#define JS_GC_HEAP_KIND MR_MAPPED
#endif
"Memory used by the garbage-collected JavaScript "
"heap.",
XPConnectGCChunkAllocator::GetGCChunkBytesInUse,
&gXPCJSChunkAllocator)
NS_MEMORY_REPORTER_IMPLEMENT(XPConnectJSGCHeap,
"explicit/js/gc-heap",
JS_GC_HEAP_KIND,
"Memory used by the garbage-collected JavaScript heap.",
XPConnectGCChunkAllocator::GetGCChunkBytesInUse,
&gXPCJSChunkAllocator)
static PRInt64
GetPerCompartmentSize(PRInt64 (*f)(JSCompartment *c))
@ -1322,19 +1324,19 @@ GetJSMJitData(void *data)
}
NS_MEMORY_REPORTER_IMPLEMENT(XPConnectJSMjitCode,
"mapped/js/mjit-code",
"Memory mapped by the method JIT to hold "
"generated code.",
GetJSMjitCode,
NULL)
"explicit/js/mjit-code",
MR_MAPPED,
"Memory used 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)
"explicit/js/mjit-data",
MR_HEAP,
"Memory used by the method JIT for the following data: "
"JITScripts, native maps, and inline cache structs.",
GetJSMJitData,
NULL)
#endif // JS_METHODJIT
#ifdef JS_TRACER
@ -1384,25 +1386,26 @@ GetJSTjitDataAllocatorsReserve(void *data)
}
NS_MEMORY_REPORTER_IMPLEMENT(XPConnectJSTjitCode,
"mapped/js/tjit-code",
"Memory mapped by the trace JIT to hold "
"generated code.",
GetJSTjitCode,
NULL)
"explicit/js/tjit-code",
MR_MAPPED,
"Memory used 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)
"explicit/js/tjit-data/allocators-main",
MR_HEAP,
"Memory used 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)
"explicit/js/tjit-data/allocators-reserve",
MR_HEAP,
"Memory used by the trace JIT and held in reserve for VMAllocators "
"in case of OOM.",
GetJSTjitDataAllocatorsReserve,
NULL)
#endif // JS_TRACER
XPCJSRuntime::XPCJSRuntime(nsXPConnect* aXPConnect)

View File

@ -1676,17 +1676,18 @@ NS_NewPresShell(nsIPresShell** aInstancePtrResult)
nsTHashtable<PresShell::PresShellPtrKey> *nsIPresShell::sLiveShells = 0;
NS_MEMORY_REPORTER_IMPLEMENT(LayoutPresShell,
"heap-used/layout/all",
"Memory used by layout PresShell, PresContext, "
"and other related areas.",
PresShell::SizeOfLayoutMemoryReporter,
nsnull)
"explicit/layout/all",
MR_HEAP,
"Memory used by layout PresShell, PresContext, and other related areas.",
PresShell::SizeOfLayoutMemoryReporter,
nsnull)
NS_MEMORY_REPORTER_IMPLEMENT(LayoutBidi,
"heap-used/layout/bidi",
"Memory used by layout Bidi processor.",
PresShell::SizeOfBidiMemoryReporter,
nsnull)
"explicit/layout/bidi",
MR_HEAP,
"Memory used by layout Bidi processor.",
PresShell::SizeOfBidiMemoryReporter,
nsnull)
PresShell::PresShell()
: mMouseLocation(NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE)

View File

@ -160,25 +160,31 @@ public:
NS_IMETHOD GetPath(char **memoryPath)
{
if (mType == ChromeUsedRaw) {
*memoryPath = strdup("heap-used/images/chrome/used/raw");
*memoryPath = strdup("explicit/images/chrome/used/raw");
} else if (mType == ChromeUsedUncompressed) {
*memoryPath = strdup("heap-used/images/chrome/used/uncompressed");
*memoryPath = strdup("explicit/images/chrome/used/uncompressed");
} else if (mType == ChromeUnusedRaw) {
*memoryPath = strdup("heap-used/images/chrome/unused/raw");
*memoryPath = strdup("explicit/images/chrome/unused/raw");
} else if (mType == ChromeUnusedUncompressed) {
*memoryPath = strdup("heap-used/images/chrome/unused/uncompressed");
*memoryPath = strdup("explicit/images/chrome/unused/uncompressed");
} else if (mType == ContentUsedRaw) {
*memoryPath = strdup("heap-used/images/content/used/raw");
*memoryPath = strdup("explicit/images/content/used/raw");
} else if (mType == ContentUsedUncompressed) {
*memoryPath = strdup("heap-used/images/content/used/uncompressed");
*memoryPath = strdup("explicit/images/content/used/uncompressed");
} else if (mType == ContentUnusedRaw) {
*memoryPath = strdup("heap-used/images/content/unused/raw");
*memoryPath = strdup("explicit/images/content/unused/raw");
} else if (mType == ContentUnusedUncompressed) {
*memoryPath = strdup("heap-used/images/content/unused/uncompressed");
*memoryPath = strdup("explicit/images/content/unused/uncompressed");
}
return NS_OK;
}
NS_IMETHOD GetKind(PRInt32 *kind)
{
*kind = MR_HEAP;
return NS_OK;
}
NS_IMETHOD GetDescription(char **desc)
{
if (mType == ChromeUsedRaw) {

View File

@ -353,7 +353,7 @@ public:
{
nsCString path;
path.AppendLiteral("heap-used/storage/sqlite/");
path.AppendLiteral("explicit/storage/sqlite/");
path.Append(mDBConn.getFilename());
if (mType == LookAside_Used) {
@ -373,6 +373,12 @@ public:
return NS_OK;
}
NS_IMETHOD GetKind(PRInt32 *kind)
{
*kind = MR_HEAP;
return NS_OK;
}
NS_IMETHOD GetDescription(char **desc)
{
if (mType == LookAside_Used) {
@ -382,7 +388,8 @@ public:
*desc = ::strdup("Memory (approximate) used by all pager caches.");
}
else if (mType == Schema_Used) {
*desc = ::strdup("Memory (approximate) 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("Memory (approximate) used by all prepared statements");

View File

@ -139,10 +139,11 @@ GetStorageSQLiteMemoryUsed(void *)
}
NS_MEMORY_REPORTER_IMPLEMENT(StorageSQLiteMemoryUsed,
"heap-used/storage/sqlite",
"Memory used by SQLite.",
GetStorageSQLiteMemoryUsed,
nsnull)
"explicit/storage/sqlite",
MR_HEAP,
"Memory used by SQLite.",
GetStorageSQLiteMemoryUsed,
nsnull)
////////////////////////////////////////////////////////////////////////////////
//// Helpers

View File

@ -48,6 +48,11 @@
color: #004;
}
.mrStar {
font-style: italic;
color: #604;
}
.treeLine {
color: #888;
}

View File

@ -48,6 +48,12 @@ var gVerbose = (location.href.split(/[\?,]/).indexOf("verbose") !== -1);
var gAddedObserver = false;
const MR_MAPPED = Ci.nsIMemoryReporter.MR_MAPPED;
const MR_HEAP = Ci.nsIMemoryReporter.MR_HEAP;
const MR_OTHER = Ci.nsIMemoryReporter.MR_OTHER;
const kUnknown = -1; // used for _memoryUsed if a memory reporter failed
function onLoad()
{
var os = Cc["@mozilla.org/observer-service;1"].
@ -145,8 +151,9 @@ function update()
//
// interface Tmr {
// _tpath: string;
// _kind: number;
// _description: string;
// _memoryUsed: number;
// _memoryUsed: number;
// }
//
// - The .path property is renamed ._tpath ("truncated path") in the copy
@ -170,6 +177,7 @@ function update()
process = mr.path.slice(0, i);
tmr._tpath = mr.path.slice(i + 1);
}
tmr._kind = mr.kind;
tmr._description = mr.description;
tmr._memoryUsed = mr.memoryUsed;
@ -201,10 +209,11 @@ function update()
const CCDesc = "Do a global garbage collection followed by a cycle " +
"collection. (It currently is not possible to do a cycle " +
"collection on its own, see bug 625302.)";
const MPDesc = "Send three \"heap-minimize\" notifications in a row. Each " +
"notification triggers a global garbage collection followed " +
"by a cycle collection, and causes the process to reduce " +
"memory usage in other ways, e.g. by flushing various caches.";
const MPDesc = "Send three \"heap-minimize\" notifications in a " +
"row. Each notification triggers a global garbage " +
"collection followed by a cycle collection, and causes the " +
"process to reduce memory usage in other ways, e.g. by " +
"flushing various caches.";
text += "<div>" +
"<button title='" + GCDesc + "' onclick='doGlobalGC()'>GC</button>" +
@ -222,6 +231,11 @@ function update()
content.appendChild(div);
}
function cmpTmrs(a, b)
{
return b._memoryUsed - a._memoryUsed
};
/**
* Generates the text for a single process.
*
@ -233,35 +247,26 @@ function update()
*/
function genProcessText(aProcess, aTmrs)
{
// First, duplicate the "mapped/heap/used" reporter as "heap-used"; this
// value shows up in both the "mapped" and the "heap-used" trees.
var mappedHeapUsedTmr = aTmrs["mapped/heap/used"];
aTmrs["heap-used"] = {
_tpath: "heap-used",
_description: mappedHeapUsedTmr._description,
_memoryUsed: mappedHeapUsedTmr._memoryUsed
};
/**
* 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 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;
* _kind: number;
* _description: string;
* _memoryUsed: number;
* _kids: [Node];
* _hasReporter: boolean; (might not be defined)
* _memoryUsed: number; (non-negative or 'kUnknown')
* _kids: [Node];
* _hasReporter: boolean; (only defined if 'true')
* _hasProblem: boolean; (only defined if 'true')
* }
*/
function buildTree(aTreeName, aOmitThresholdPerc)
function buildTree()
{
const treeName = "explicit";
const omitThresholdPerc = 0.5; /* percent */
function findKid(aName, aKids)
{
for (var i = 0; i < aKids.length; i++) {
@ -272,13 +277,17 @@ function genProcessText(aProcess, aTmrs)
return undefined;
}
// We want to process all reporters that begin with 'aTreeName'.
// First we build the tree but only filling in '_name', '_kids' and
// maybe '._hasReporter'. This is done top-down from the reporters.
var t = { _name: "falseRoot", _kids: [] };
// We want to process all reporters that begin with 'treeName'.
// First we build the tree but only filling in '_name', '_kind', '_kids'
// and maybe '._hasReporter'. This is done top-down from the reporters.
var t = {
_name: "falseRoot",
_kind: MR_OTHER,
_kids: []
};
for (var tpath in aTmrs) {
var tmr = aTmrs[tpath];
if (tmr._tpath.slice(0, aTreeName.length) === aTreeName) {
if (tmr._tpath.slice(0, treeName.length) === treeName) {
var names = tmr._tpath.split('/');
var u = t;
for (var i = 0; i < names.length; i++) {
@ -287,66 +296,126 @@ function genProcessText(aProcess, aTmrs)
if (uMatch) {
u = uMatch;
} else {
var v = { _name: name, _kids: [] };
var v = {
_name: name,
_kind: MR_OTHER,
_kids: []
};
u._kids.push(v);
u = v;
}
}
u._kind = tmr._kind;
u._hasReporter = true;
}
}
// Using falseRoot makes the above code simpler. Now discard it, leaving
// aTreeName at the root.
// treeName at the root.
t = t._kids[0];
// Next, fill in '_description' and '_memoryUsed' for each node. This is
// done bottom-up because for most non-leaf nodes '_memoryUsed' and
// '_description' are determined from the child nodes.
// Next, fill in '_description' and '_memoryUsed', and maybe '_hasProblem'
// for each node. This is done bottom-up because for most non-leaf nodes
// '_memoryUsed' and '_description' are determined from the child nodes.
function fillInTree(aT, aPretpath)
{
var tpath = aPretpath ? aPretpath + '/' + aT._name : aT._name;
if (aT._kids.length === 0) {
// Leaf node. Must have a reporter.
aT._memoryUsed = getBytes(aTmrs, tpath);
aT._description = getDescription(aTmrs, tpath);
var memoryUsed = getBytes(aTmrs, tpath);
if (memoryUsed !== kUnknown) {
aT._memoryUsed = memoryUsed;
} else {
aT._memoryUsed = 0;
aT._hasProblem = true;
}
} else {
// Non-leaf node. Get the size of the children.
var childrenBytes = 0;
for (var i = 0; i < aT._kids.length; i++) {
// Allow for -1 (ie. "unknown"), treat it like 0.
// Allow for kUnknown, treat it like 0.
var b = fillInTree(aT._kids[i], tpath);
childrenBytes += (b === -1 ? 0 : b);
childrenBytes += (b === kUnknown ? 0 : b);
}
if (aT._hasReporter === true) {
// Non-leaf node with its own reporter. Use the reporter and add an
// "other" child node (unless the byte count is -1, ie. unknown).
aT._memoryUsed = getBytes(aTmrs, tpath);
aT._description = getDescription(aTmrs, tpath);
if (aT._memoryUsed !== -1) {
var memoryUsed = getBytes(aTmrs, tpath);
if (memoryUsed !== kUnknown) {
// Non-leaf node with its own reporter. Use the reporter and add
// an "other" child node.
aT._memoryUsed = memoryUsed;
var other = {
_name: "other",
_kind: MR_OTHER,
_description: "All unclassified " + aT._name + " memory.",
_memoryUsed: aT._memoryUsed - childrenBytes,
_kids: []
};
aT._kids.push(other);
} else {
// Non-leaf node with a reporter that returns kUnknown.
// Use the sum of the children and mark it as problematic.
aT._memoryUsed = childrenBytes;
aT._hasProblem = true;
}
} else {
// Non-leaf node without its own reporter. Derive its size and
// description entirely from its children.
aT._memoryUsed = childrenBytes;
aT._description = "The sum of all entries below " + aT._name + ".";
aT._description = "The sum of all entries below '" + aT._name + "'.";
}
}
return aT._memoryUsed;
}
fillInTree(t, "");
// Determine how many bytes are reported by heap reporters. Be careful
// with non-leaf reporters; if we count a non-leaf reporter we don't want
// to count any of its child reporters.
var s = "";
function getKnownHeapUsedBytes(aT)
{
if (aT._kind === MR_HEAP) {
return aT._memoryUsed;
} else {
var n = 0;
for (var i = 0; i < aT._kids.length; i++) {
n += getKnownHeapUsedBytes(aT._kids[i]);
}
return n;
}
}
// A special case: compute the derived "heap-unclassified" value. Don't
// mark "heap-used" when we get its size because we want it to appear in
// the "Other Measurements" list.
var heapUsedBytes = getBytes(aTmrs, "heap-used", true);
var unknownHeapUsedBytes = 0;
var hasProblem = true;
if (heapUsedBytes !== kUnknown) {
unknownHeapUsedBytes = heapUsedBytes - getKnownHeapUsedBytes(t);
hasProblem = false;
}
var heapUnclassified = {
_name: "heap-unclassified",
_kind: MR_HEAP,
_description:
"Memory not classified by a more specific reporter. This includes " +
"memory allocated by the heap allocator in excess of that requested " +
"by the application; this can happen when the heap allocator rounds " +
"up request sizes.",
_memoryUsed: unknownHeapUsedBytes,
_hasProblem: hasProblem,
_kids: []
}
t._kids.push(heapUnclassified);
t._memoryUsed += unknownHeapUsedBytes;
function shouldOmit(aBytes)
{
return !gVerbose &&
t._memoryUsed !== -1 &&
(100 * aBytes / t._memoryUsed) < aOmitThresholdPerc;
t._memoryUsed !== kUnknown &&
(100 * aBytes / t._memoryUsed) < omitThresholdPerc;
}
/**
@ -358,7 +427,6 @@ function genProcessText(aProcess, aTmrs)
*/
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++) {
@ -377,6 +445,7 @@ function genProcessText(aProcess, aTmrs)
var n = i - i0;
var tmrSub = {
_name: "(" + n + " omitted)",
_kind: MR_OTHER,
_description: "Omitted sub-trees: " + aggNames.join(", ") + ".",
_memoryUsed: aggBytes,
_kids: []
@ -392,17 +461,10 @@ function genProcessText(aProcess, aTmrs)
return t;
}
// The threshold used for the "mapped" tree is lower than the one for the
// "heap-used" tree, because the "mapped" total size is dominated (especially
// on Mac) by memory usage that isn't covered by more specific reporters.
var mappedTree = buildTree("mapped", 0.01);
var heapUsedTree = buildTree("heap-used", 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 += genTreeText(buildTree());
text += genOtherText(aTmrs);
text += "<hr></hr>";
return text;
@ -419,10 +481,6 @@ function formatBytes(aBytes)
{
var unit = gVerbose ? "B" : "MB";
if (aBytes === -1) {
return "??? " + unit;
}
function formatInt(aN)
{
var neg = false;
@ -483,26 +541,30 @@ function pad(aS, aN, aC)
}
/**
* Gets the byte count for a particular memory reporter.
* Gets the byte count for a particular memory reporter and sets its _done
* property.
*
* @param aTmrs
* Table of Tmrs for this process
* @param aTpath
* The tpath of the memory reporter
* @param aDoNotMark
* If set, the _done property is not set.
* @return The byte count
*/
function getBytes(aTmrs, aTpath)
function getBytes(aTmrs, aTpath, aDoNotMark)
{
var tmr = aTmrs[aTpath];
if (tmr) {
var bytes = tmr._memoryUsed;
tmr.done = true;
if (!aDoNotMark) {
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.
// Nb: this should never occur; all 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;
}
@ -526,22 +588,44 @@ function genMrValueText(aValue)
return "<span class='mrValue'>" + aValue + "</span>";
}
function genMrNameText(aDesc, aName)
function kindToString(aKind)
{
return "-- <span class='mrName' title=\"" + aDesc + "\">" +
aName + "</span>\n";
switch (aKind) {
case MR_MAPPED: return "(Mapped) ";
case MR_HEAP: return "(Heap) ";
case MR_OTHER: return "";
default: return "(???) ";
}
}
function escapeQuotes(aStr)
{
return aStr.replace(/'/g, '&#39;');
}
function genMrNameText(aKind, aDesc, aName, aHasProblem)
{
const problemDesc =
"Warning: this memory reporter was unable to compute a useful value. " +
"The reported value is the sum of all entries below '" + aName + "', " +
"which is probably less than the true value.";
var text = "-- <span class='mrName' title='" +
kindToString(aKind) + escapeQuotes(aDesc) +
"'>" + aName + "</span>";
text += aHasProblem
? " <span class='mrStar' title=\"" + problemDesc + "\">[*]</span>\n"
: "\n";
return text;
}
/**
* Generates the text for a particular tree, including its heading.
* Generates the text for the tree, including its heading.
*
* @param aT
* The tree
* @param aTreeName
* The tree's name
* @return The generated text
*/
function genTreeText(aT, aTreeName)
function genTreeText(aT)
{
var treeBytes = aT._memoryUsed;
var treeBytesLength = formatBytes(treeBytes).length;
@ -605,20 +689,17 @@ function genTreeText(aT, aTreeName)
// Generate the percentage.
var perc = "";
if (treeBytes !== -1) {
if (aT._memoryUsed === -1) {
perc = "??.??";
} else 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> ";
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);
genMrNameText(aT._kind, aT._description, aT._name,
aT._hasProblem);
for (var i = 0; i < aT._kids.length; i++) {
// 3 is the standard depth, the callee adjusts it if necessary.
@ -631,7 +712,21 @@ function genTreeText(aT, aTreeName)
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";
const desc =
"This tree covers explicit memory allocations by the application, " +
"both at the operating system level (via calls to functions such as " +
"VirtualAlloc, vm_allocate, and mmap), and at the heap allocation level " +
"(via functions such as malloc, calloc, realloc, memalign, operator " +
"new, and operator new[]). It excludes memory that is mapped implicitly " +
"such as code and data segments, and thread stacks. It also excludes " +
"heap memory that has been freed by the application but is still being " +
"held onto by the heap allocator. It is not guaranteed to cover every " +
"explicit allocation, but it does cover most (including the entire " +
"heap), and therefore it is the single best number to focus on when " +
"trying to reduce memory usage.";
return "<h2 title='" + escapeQuotes(desc) +
"'>Explicit Allocations</h2>\n" + "<pre>" + text + "</pre>\n";
}
/**
@ -643,30 +738,48 @@ function genTreeText(aT, aTreeName)
*/
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;
// Generate an array of tmr-like elements, stripping out all the tmrs that
// have already been handled. Also find the width of the widest element, so
// we can format things nicely.
var maxBytesLength = 0;
var tmrArray = [];
for (var tpath in aTmrs) {
var tmr = aTmrs[tpath];
if (!tmr.done && tmr._memoryUsed > maxBytes) {
maxBytes = tmr._memoryUsed;
if (!tmr._done) {
var hasProblem = false;
if (tmr._memoryUsed === kUnknown) {
hasProblem = true;
}
var elem = {
_tpath: tmr._tpath,
_kind: tmr._kind,
_description: tmr._description,
_memoryUsed: hasProblem ? 0 : tmr._memoryUsed,
_hasProblem: hasProblem
};
tmrArray.push(elem);
var thisBytesLength = formatBytes(elem._memoryUsed).length;
if (thisBytesLength > maxBytesLength) {
maxBytesLength = thisBytesLength;
}
}
}
tmrArray.sort(cmpTmrs);
// Generate text for the not-yet-yet-printed values.
var maxBytesLength = formatBytes(maxBytes).length;
// Generate text for the not-yet-printed values.
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);
}
for (var i = 0; i < tmrArray.length; i++) {
var elem = tmrArray[i];
text += genMrValueText(
pad(formatBytes(elem._memoryUsed), maxBytesLength, ' ')) + " ";
text += genMrNameText(elem._kind, elem._description, elem._tpath,
elem._hasProblem);
}
// Nb: the newlines give nice spacing if we cut+paste into a text buffer.
return "<h2>Other Measurements</h2>\n<pre>" + text + "</pre>\n";
const desc = "This list contains other memory measurements that cross-cut " +
"the requested memory measurements above."
return "<h2 title='" + desc + "'>Other Measurements</h2>\n" +
"<pre>" + text + "</pre>\n";
}

View File

@ -30,41 +30,49 @@
// Setup various fake-but-deterministic reporters.
const KB = 1024;
const MB = KB * KB;
const kUnknown = -1;
const MAPPED = Ci.nsIMemoryReporter.MR_MAPPED;
const HEAP = Ci.nsIMemoryReporter.MR_HEAP;
const OTHER = Ci.nsIMemoryReporter.MR_OTHER;
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: "heap-used/g", memoryUsed: 15 * MB }, // internal
{ path: "heap-used/g/a", memoryUsed: 6 * MB },
{ path: "heap-used/g/b", memoryUsed: 5 * MB },
{ path: "other1", memoryUsed: 111 * MB },
{ path: "other2", memoryUsed: 222 * MB },
{ path: "heap-used", kind: OTHER, memoryUsed: 500 * MB },
{ path: "heap-unused", kind: OTHER, memoryUsed: 100 * MB },
{ path: "explicit/a", kind: HEAP, memoryUsed: 222 * MB },
{ path: "explicit/b/a", kind: HEAP, memoryUsed: 85 * MB },
{ path: "explicit/b/b", kind: HEAP, memoryUsed: 75 * MB },
{ path: "explicit/b/c/a", kind: HEAP, memoryUsed: 70 * MB },
{ path: "explicit/b/c/b", kind: HEAP, memoryUsed: 2 * MB }, // omitted
{ path: "explicit/c", kind: MAPPED,memoryUsed: 123 * MB },
{ path: "explicit/d", kind: MAPPED,memoryUsed: 499 * KB }, // omitted
{ path: "explicit/e", kind: MAPPED,memoryUsed: 100 * KB }, // omitted
{ path: "explicit/f/g/h/i", kind: HEAP, memoryUsed: 20 * MB },
{ path: "explicit/g", kind: HEAP, memoryUsed: 14 * MB }, // internal
{ path: "explicit/g", kind: HEAP, memoryUsed: 1 * MB }, // internal, dup: merge
{ path: "explicit/g/a", kind: HEAP, memoryUsed: 6 * MB },
{ path: "explicit/g/b", kind: HEAP, memoryUsed: 5 * MB },
{ path: "other1", kind: OTHER, memoryUsed: 111 * MB },
{ path: "other2", kind: OTHER, 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 },
{ path: "2nd:heap-used", kind: OTHER, memoryUsed: 1000 * MB },
{ path: "2nd:heap-unused", kind: OTHER, memoryUsed: 100 * MB },
{ path: "2nd:explicit/a/b/c",kind: HEAP, memoryUsed: 498 * MB },
{ path: "2nd:explicit/a/b/c",kind: HEAP, memoryUsed: 1 * MB }, // dup: merge
{ path: "2nd:explicit/b", kind: HEAP, memoryUsed: 400 * MB },
{ path: "2nd:other1", kind: OTHER, 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 }
// kUnknown should be handled gracefully for "heap-used", non-leaf
// reporters, leaf-reporters, and "other" reporters.
{ path: "3rd:heap-used", kind: OTHER, memoryUsed: kUnknown },
{ path: "3rd:explicit/a", kind: HEAP, memoryUsed: kUnknown },
{ path: "3rd:explicit/a/b", kind: HEAP, memoryUsed: 333 * MB },
{ path: "3rd:explicit/a/c", kind: HEAP, memoryUsed: 444 * MB },
{ path: "3rd:explicit/a/d", kind: HEAP, memoryUsed: kUnknown },
{ path: "3rd:explicit/b", kind: MAPPED,memoryUsed: kUnknown },
{ path: "3rd:other1", kind: OTHER, memoryUsed: kUnknown }
];
for (var i = 0; i < fakeReporters.length; i++) {
fakeReporters[i].description = "(description)";
mgr.registerReporter(fakeReporters[i]);
}
]]>
@ -79,74 +87,61 @@
"\
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\
├───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\
├───15.00 MB (03.00%) -- g\n\
│ ├───6.00 MB (01.20%) -- a\n\
│ ├───5.00 MB (01.00%) -- b\n\
│ └───4.00 MB (00.80%) -- other\n\
├───10.42 MB (02.08%) -- other\n\
└────0.58 MB (00.12%) -- (2 omitted)\n\
Explicit Allocations\n\
623.58 MB (100.0%) -- explicit\n\
├──232.00 MB (37.20%) -- b\n\
│ ├───85.00 MB (13.63%) -- a\n\
│ ├───75.00 MB (12.03%) -- b\n\
│ └───72.00 MB (11.55%) -- c\n\
│ ├──70.00 MB (11.23%) -- a\n\
│ └───2.00 MB (00.32%) -- (1 omitted)\n\
├──222.00 MB (35.60%) -- a\n\
├──123.00 MB (19.72%) -- c\n\
├───20.00 MB (03.21%) -- f\n\
│ └──20.00 MB (03.21%) -- g\n\
│ └──20.00 MB (03.21%) -- h\n\
│ └──20.00 MB (03.21%) -- i\n\
├───15.00 MB (02.41%) -- g\n\
│ ├───6.00 MB (00.96%) -- a\n\
│ ├───5.00 MB (00.80%) -- b\n\
│ └───4.00 MB (00.64%) -- other\n\
├───11.00 MB (01.76%) -- heap-unclassified\n\
└────0.58 MB (00.09%) -- (2 omitted)\n\
\n\
Other Measurements\n\
111.00 MB -- other1\n\
500.00 MB -- heap-used\n\
222.00 MB -- other2\n\
111.00 MB -- other1\n\
100.00 MB -- heap-unused\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\
Explicit Allocations\n\
1,000.00 MB (100.0%) -- explicit\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\
├────400.00 MB (40.00%) -- b\n\
└────101.00 MB (10.10%) -- heap-unclassified\n\
\n\
Other Measurements\n\
777.00 MB -- other1\n\
1,000.00 MB -- heap-used\n\
777.00 MB -- other1\n\
100.00 MB -- heap-unused\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\
Explicit Allocations\n\
777.00 MB (100.0%) -- explicit\n\
├──777.00 MB (100.0%) -- a [*]\n\
│ ├──444.00 MB (57.14%) -- c\n\
│ ├──333.00 MB (42.86%) -- b\n\
│ └────0.00 MB (00.00%) -- (1 omitted)\n\
└────0.00 MB (00.00%) -- (2 omitted)\n\
\n\
Other Measurements\n\
555.00 MB -- other1\n\
0.00 MB -- heap-used [*]\n\
0.00 MB -- other1 [*]\n\
\n\
";
@ -154,75 +149,63 @@ Other Measurements\n\
"\
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\
├───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\
├───15,728,640 B (03.00%) -- g\n\
│ ├───6,291,456 B (01.20%) -- a\n\
│ ├───5,242,880 B (01.00%) -- b\n\
│ └───4,194,304 B (00.80%) -- other\n\
├───10,920,960 B (02.08%) -- other\n\
├──────510,976 B (00.10%) -- d\n\
Explicit Allocations\n\
653,876,224 B (100.0%) -- explicit\n\
├──243,269,632 B (37.20%) -- b\n\
│ ├───89,128,960 B (13.63%) -- a\n\
│ ├───78,643,200 B (12.03%) -- b\n\
│ └───75,497,472 B (11.55%) -- c\n\
│ ├──73,400,320 B (11.23%) -- a\n\
│ └───2,097,152 B (00.32%) -- b\n\
├──232,783,872 B (35.60%) -- a\n\
├──128,974,848 B (19.72%) -- c\n\
├───20,971,520 B (03.21%) -- f\n\
│ └──20,971,520 B (03.21%) -- g\n\
│ └──20,971,520 B (03.21%) -- h\n\
│ └──20,971,520 B (03.21%) -- i\n\
├───15,728,640 B (02.41%) -- g\n\
│ ├───6,291,456 B (00.96%) -- a\n\
│ ├───5,242,880 B (00.80%) -- b\n\
│ └───4,194,304 B (00.64%) -- other\n\
├───11,534,336 B (01.76%) -- heap-unclassified\n\
├──────510,976 B (00.08%) -- d\n\
└──────102,400 B (00.02%) -- e\n\
\n\
Other Measurements\n\
116,391,936 B -- other1\n\
524,288,000 B -- heap-used\n\
232,783,872 B -- other2\n\
116,391,936 B -- other1\n\
104,857,600 B -- heap-unused\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\
Explicit Allocations\n\
1,048,576,000 B (100.0%) -- explicit\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\
├────419,430,400 B (40.00%) -- b\n\
└────105,906,176 B (10.10%) -- heap-unclassified\n\
\n\
Other Measurements\n\
814,743,552 B -- other1\n\
1,048,576,000 B -- heap-used\n\
814,743,552 B -- other1\n\
104,857,600 B -- heap-unused\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\
Explicit Allocations\n\
814,743,552 B (100.0%) -- explicit\n\
├──814,743,552 B (100.0%) -- a [*]\n\
│ ├──465,567,744 B (57.14%) -- c\n\
│ ├──349,175,808 B (42.86%) -- b\n\
│ └────────────0 B (00.00%) -- d [*]\n\
├────────────0 B (00.00%) -- b [*]\n\
└────────────0 B (00.00%) -- heap-unclassified [*]\n\
\n\
Other Measurements\n\
581,959,680 B -- other1\n\
0 B -- heap-used [*]\n\
0 B -- other1 [*]\n\
\n\
"

View File

@ -47,39 +47,52 @@ interface nsIMemoryReporter : nsISupports
* 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.
* '/'-delimited, eg. "a/b/c". There are two categories of paths.
*
* - 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 node lower in the tree; this lower node
* may be a non-leaf or a leaf node. So, for example, "mapped",
* "mapped/heap/used", "mapped/heap/unused", "mapped/js/mjit-code",
* and "mapped/js/tjit-code" define this tree:
* - Paths starting with "explicit" represent non-overlapping regions of
* memory that have been explicitly allocated with an OS-level allocation
* (eg. mmap/VirtualAlloc/vm_allocate) or a heap-level allocation (eg.
* malloc/calloc/operator new). Each one can be viewed as representing a
* path in a tree from the root node ("explicit") to a node lower in the
* tree; this lower node does not have to be a leaf node.
*
* mapped [*]
* |--heap
* | |--used [*]
* | \--unused [*]
* \--js
* |--mjit-code [*]
* \--tjit-code [*]
* So, for example, "explicit/a/b", "explicit/a/c", "explicit/d",
* "explicit/d/e", and "explicit/d/f" define this tree:
*
* explicit
* |--a
* | |--b [*]
* | \--c [*]
* \--d [*]
* |--e [*]
* \--f [*]
*
* Nodes marked with a [*] have a reporter.
*
* - Paths starting with "heap-used" represent non-overlapping regions of
* used heap memory. The "mapped" rules above apply equally here. These
* paths are actually sub-paths of "mapped/heap/used", but that reporter
* is duplicated under the name "heap-used" to make the processing for
* about:memory simpler.
*
* - All other paths represent cross-cuttings memory regions, ie. ones that
* may overlap arbitrarily with regions in the "mapped" and "heap-used"
* trees.
* may overlap arbitrarily with regions in the "explicit" tree.
*/
readonly attribute string path;
/*
* A human-readable description of this memory usage report
* Allocation kinds. "MAPPED" means it is allocated directly by the OS, eg.
* by calling mmap, VirtualAlloc, vm_allocate, etc. "HEAP" means it is
* allocated by the heap allocator, eg. by calling malloc, calloc, realloc,
* memalign, operator new, operator new[], etc. "OTHER" means it doesn't fit
* into either of these categories; such reporters should have a path that
* does *not* start with "explicit".
*/
const PRInt32 MR_MAPPED = 0;
const PRInt32 MR_HEAP = 1;
const PRInt32 MR_OTHER = 2;
/*
* The memory kind, see MR_* above.
*/
readonly attribute PRInt32 kind;
/*
* A human-readable description of this memory usage report.
*/
readonly attribute string description;
@ -119,11 +132,12 @@ interface nsIMemoryReporterManager : nsISupports
%{C++
#define NS_MEMORY_REPORTER_IMPLEMENT(_classname,_path,_desc,_usageFunction,_dataptr) \
#define NS_MEMORY_REPORTER_IMPLEMENT(_classname,_path,_kind,_desc,_usageFunction,_dataptr) \
class MemoryReporter_##_classname : public nsIMemoryReporter { \
public: \
NS_DECL_ISUPPORTS \
NS_IMETHOD GetPath(char **memoryPath) { *memoryPath = strdup(_path); return NS_OK; } \
NS_IMETHOD GetKind(int *kind) { *kind = _kind; return NS_OK; } \
NS_IMETHOD GetDescription(char **desc) { *desc = strdup(_desc); return NS_OK; } \
NS_IMETHOD GetMemoryUsed(PRInt64 *memoryUsed) { *memoryUsed = _usageFunction(_dataptr); return NS_OK; } \
}; \

View File

@ -60,7 +60,7 @@ static PRInt64 GetProcSelfStatmField(int n)
return (PRInt64) -1;
}
static PRInt64 GetMapped(void *)
static PRInt64 GetVsize(void *)
{
return GetProcSelfStatmField(0);
}
@ -83,11 +83,10 @@ static bool GetTaskBasicInfo(struct task_basic_info *ti)
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 *)
// The VSIZE figure on Mac includes huge amounts of shared memory and is always
// absurdly high, eg. 2GB+ even at start-up. But both 'top' and 'ps' report
// it, so we might as well too.
static PRInt64 GetVsize(void *)
{
task_basic_info ti;
return (PRInt64) (GetTaskBasicInfo(&ti) ? ti.virtual_size : -1);
@ -104,22 +103,29 @@ static PRInt64 GetResident(void *)
#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);
static PRInt64 GetPrivate(void *)
{
PROCESS_MEMORY_COUNTERS_EX pmcex;
pmcex.cb = sizeof(PROCESS_MEMORY_COUNTERS_EX);
if (!GetProcessMemoryInfo(GetCurrentProcess(),
(PPROCESS_MEMORY_COUNTERS) &pmcex, sizeof(pmcex)))
return (PRInt64) -1;
if (!GetProcessMemoryInfo(GetCurrentProcess(),
(PPROCESS_MEMORY_COUNTERS) &pmcex, sizeof(pmcex)))
return (PRInt64) -1;
return pmcex.PrivateUsage;
#else
return (PRInt64) -1;
#endif
return pmcex.PrivateUsage;
}
NS_MEMORY_REPORTER_IMPLEMENT(Private,
"private",
MR_OTHER,
"Memory that cannot be shared with other processes, including memory that "
"is committed and marked MEM_PRIVATE, data that is not mapped, and "
"executable pages that have been written to.",
GetPrivate,
NULL)
#endif
static PRInt64 GetResident(void *)
{
PROCESS_MEMORY_COUNTERS pmc;
@ -133,11 +139,6 @@ static PRInt64 GetResident(void *)
#else
static PRInt64 GetMapped(void *)
{
return (PRInt64) -1;
}
static PRInt64 GetResident(void *)
{
return (PRInt64) -1;
@ -145,25 +146,25 @@ static PRInt64 GetResident(void *)
#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, including the heap, code and data segments, "
"thread stacks, and memory explicitly mapped by the process via "
"mmap, VirtualAlloc and similar operations. "
"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,
#if defined(XP_LINUX) || defined(XP_MACOSX)
NS_MEMORY_REPORTER_IMPLEMENT(Vsize,
"vsize",
MR_OTHER,
"Memory mapped by the process, including code and data segments, the "
"heap, thread stacks, memory explicitly mapped by the process via mmap "
"and similar operations, and memory shared with other processes. "
"(Note that 'resident' is a better measure of the memory resources used "
"by the process.) "
"This is the vsize figure as reported by 'top' or 'ps'; on Mac the amount "
"of memory shared with other processes is very high and so this figure is "
"of limited use.",
GetVsize,
NULL)
#endif
NS_MEMORY_REPORTER_IMPLEMENT(Resident,
"resident",
MR_OTHER,
"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, "
@ -199,14 +200,14 @@ extern void jemalloc_stats(jemalloc_stats_t* stats)
#if HAVE_JEMALLOC_STATS
static PRInt64 GetMappedHeapUsed(void *)
static PRInt64 GetHeapUsed(void *)
{
jemalloc_stats_t stats;
jemalloc_stats(&stats);
return (PRInt64) stats.allocated;
}
static PRInt64 GetMappedHeapUnused(void *)
static PRInt64 GetHeapUnused(void *)
{
jemalloc_stats_t stats;
jemalloc_stats(&stats);
@ -228,30 +229,30 @@ static PRInt64 GetHeapDirty(void *)
}
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)
"heap-committed",
MR_OTHER,
"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)
"heap-dirty",
MR_OTHER,
"Memory mapped by the heap allocator that is committed but unused.",
GetHeapDirty,
NULL)
#elif defined(XP_MACOSX) && !defined(MOZ_MEMORY)
#include <malloc/malloc.h>
static PRInt64 GetMappedHeapUsed(void *)
static PRInt64 GetHeapUsed(void *)
{
struct mstats stats = mstats();
return (PRInt64) stats.bytes_used;
}
static PRInt64 GetMappedHeapUnused(void *)
static PRInt64 GetHeapUnused(void *)
{
struct mstats stats = mstats();
return (PRInt64) (stats.bytes_total - stats.bytes_used);
@ -272,52 +273,51 @@ static PRInt64 GetHeapZone0Used(void *)
}
NS_MEMORY_REPORTER_IMPLEMENT(HeapZone0Committed,
"heap-zone0-committed",
"Memory mapped by the heap allocator that is "
"committed in the default zone.",
GetHeapZone0Committed,
NULL)
"heap-zone0-committed",
MR_OTHER,
"Memory mapped by the heap allocator that is committed in the default "
"zone.",
GetHeapZone0Committed,
NULL)
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)
"heap-zone0-used",
MR_OTHER,
"Memory mapped by the heap allocator in the default zone that is "
"available for use by the application.",
GetHeapZone0Used,
NULL)
#else
static PRInt64 GetMappedHeapUsed(void *)
static PRInt64 GetHeapUsed(void *)
{
return (PRInt64) -1;
}
static PRInt64 GetMappedHeapUnused(void *)
static PRInt64 GetHeapUnused(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(MappedHeapUsed,
"mapped/heap/used",
NS_MEMORY_REPORTER_IMPLEMENT(HeapUsed,
"heap-used",
MR_OTHER,
"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,
"application due to the allocator rounding up request sizes. "
"(The exact amount requested is not measured.) ",
GetHeapUsed,
NULL)
NS_MEMORY_REPORTER_IMPLEMENT(MappedHeapUnused,
"mapped/heap/unused",
NS_MEMORY_REPORTER_IMPLEMENT(HeapUnused,
"heap-unused",
MR_OTHER,
"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,
GetHeapUnused,
NULL)
/**
@ -336,11 +336,16 @@ nsMemoryReporterManager::Init()
#define REGISTER(_x) RegisterReporter(new NS_MEMORY_REPORTER_NAME(_x))
REGISTER(Mapped);
REGISTER(MappedHeapUsed);
REGISTER(MappedHeapUnused);
REGISTER(HeapUsed);
REGISTER(HeapUnused);
REGISTER(Resident);
#if defined(XP_LINUX) || defined(XP_MACOSX)
REGISTER(Vsize);
#elif defined(XP_WIN) && MOZ_WINSDK_TARGETVER >= MOZ_NTDDI_LONGHORN
REGISTER(Private);
#endif
#if defined(HAVE_JEMALLOC_STATS)
REGISTER(HeapCommitted);
REGISTER(HeapDirty);
@ -395,9 +400,11 @@ NS_IMPL_ISUPPORTS1(nsMemoryReporter, nsIMemoryReporter)
nsMemoryReporter::nsMemoryReporter(nsCString& prefix,
nsCString& path,
PRInt32 kind,
nsCString& desc,
PRInt64 memoryUsed)
: mDesc(desc)
: mKind(kind)
, mDesc(desc)
, mMemoryUsed(memoryUsed)
{
if (!prefix.IsEmpty()) {
@ -417,6 +424,12 @@ NS_IMETHODIMP nsMemoryReporter::GetPath(char **aPath)
return NS_OK;
}
NS_IMETHODIMP nsMemoryReporter::GetKind(PRInt32 *aKind)
{
*aKind = mKind;
return NS_OK;
}
NS_IMETHODIMP nsMemoryReporter::GetDescription(char **aDescription)
{
*aDescription = strdup(mDesc.get());

View File

@ -13,6 +13,7 @@ public:
nsMemoryReporter(nsCString& prefix,
nsCString& path,
PRInt32 kind,
nsCString& desc,
PRInt64 memoryUsed);
@ -20,6 +21,7 @@ public:
protected:
nsCString mPath, mDesc;
PRInt32 mKind;
PRInt64 mMemoryUsed;
};