diff --git a/content/canvas/src/nsCanvasRenderingContext2D.cpp b/content/canvas/src/nsCanvasRenderingContext2D.cpp index 47503f98ebf7..245d51d934c1 100644 --- a/content/canvas/src/nsCanvasRenderingContext2D.cpp +++ b/content/canvas/src/nsCanvasRenderingContext2D.cpp @@ -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) diff --git a/dom/ipc/ContentChild.cpp b/dom/ipc/ContentChild.cpp index 23808b809604..ba61c2ec47e3 100644 --- a/dom/ipc/ContentChild.cpp +++ b/dom/ipc/ContentChild.cpp @@ -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); diff --git a/dom/ipc/ContentParent.cpp b/dom/ipc/ContentParent.cpp index 0281012a00eb..2eab028f7a68 100644 --- a/dom/ipc/ContentParent.cpp +++ b/dom/ipc/ContentParent.cpp @@ -692,11 +692,13 @@ ContentParent::SetChildMemoryReporters(const InfallibleTArray& 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 r = new nsMemoryReporter(prefix, path, + kind, desc, memoryUsed); mMemoryReporters.AppendObject(r); diff --git a/dom/ipc/PMemoryReportRequest.ipdl b/dom/ipc/PMemoryReportRequest.ipdl index e4da42612f51..935b452b5570 100644 --- a/dom/ipc/PMemoryReportRequest.ipdl +++ b/dom/ipc/PMemoryReportRequest.ipdl @@ -44,6 +44,7 @@ namespace dom { struct MemoryReport { nsCString prefix; nsCString path; + PRInt32 kind; nsCString desc; PRInt64 memoryUsed; }; diff --git a/gfx/thebes/gfxASurface.cpp b/gfx/thebes/gfxASurface.cpp index fe169e64e35e..2f3d5bbfce18 100644 --- a/gfx/thebes/gfxASurface.cpp +++ b/gfx/thebes/gfxASurface.cpp @@ -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; } diff --git a/gfx/thebes/gfxWindowsPlatform.cpp b/gfx/thebes/gfxWindowsPlatform.cpp index c898fd332fca..476eafc3b58e 100644 --- a/gfx/thebes/gfxWindowsPlatform.cpp +++ b/gfx/thebes/gfxWindowsPlatform.cpp @@ -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; diff --git a/ipc/glue/SharedMemory.cpp b/ipc/glue/SharedMemory.cpp index a28520b1eb28..70cc10fd0dd3 100644 --- a/ipc/glue/SharedMemory.cpp +++ b/ipc/glue/SharedMemory.cpp @@ -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) diff --git a/js/src/xpconnect/src/xpcjsruntime.cpp b/js/src/xpconnect/src/xpcjsruntime.cpp index 0818a7211b39..ff271a68dbf6 100644 --- a/js/src/xpconnect/src/xpcjsruntime.cpp +++ b/js/src/xpconnect/src/xpcjsruntime.cpp @@ -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) diff --git a/layout/base/nsPresShell.cpp b/layout/base/nsPresShell.cpp index cf5f1c835c51..0d2421c3772c 100644 --- a/layout/base/nsPresShell.cpp +++ b/layout/base/nsPresShell.cpp @@ -1676,17 +1676,18 @@ NS_NewPresShell(nsIPresShell** aInstancePtrResult) nsTHashtable *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) diff --git a/modules/libpr0n/src/imgLoader.cpp b/modules/libpr0n/src/imgLoader.cpp index 6e0f7fc33368..eb0fe39a0ca4 100644 --- a/modules/libpr0n/src/imgLoader.cpp +++ b/modules/libpr0n/src/imgLoader.cpp @@ -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) { diff --git a/storage/src/mozStorageConnection.cpp b/storage/src/mozStorageConnection.cpp index 95a72a612f1f..d846989f263c 100644 --- a/storage/src/mozStorageConnection.cpp +++ b/storage/src/mozStorageConnection.cpp @@ -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"); diff --git a/storage/src/mozStorageService.cpp b/storage/src/mozStorageService.cpp index 1ff0d72e6ca0..73b0a922266e 100644 --- a/storage/src/mozStorageService.cpp +++ b/storage/src/mozStorageService.cpp @@ -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 diff --git a/toolkit/components/aboutmemory/content/aboutMemory.css b/toolkit/components/aboutmemory/content/aboutMemory.css index be4668187a36..99950ce9c8d7 100644 --- a/toolkit/components/aboutmemory/content/aboutMemory.css +++ b/toolkit/components/aboutmemory/content/aboutMemory.css @@ -48,6 +48,11 @@ color: #004; } +.mrStar { + font-style: italic; + color: #604; +} + .treeLine { color: #888; } diff --git a/toolkit/components/aboutmemory/content/aboutMemory.js b/toolkit/components/aboutmemory/content/aboutMemory.js index ebc197e93bad..8285ad659fde 100644 --- a/toolkit/components/aboutmemory/content/aboutMemory.js +++ b/toolkit/components/aboutmemory/content/aboutMemory.js @@ -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 += "
" + "" + @@ -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 += "

" + aProcess + " Process

\n\n"; - text += genTreeText(mappedTree, "Mapped Memory"); - text += genTreeText(heapUsedTree, "Used Heap Memory"); + text += genTreeText(buildTree()); text += genOtherText(aTmrs); text += "
"; 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 "" + aValue + ""; } -function genMrNameText(aDesc, aName) +function kindToString(aKind) { - return "-- " + - aName + "\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, '''); +} + +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 = "-- " + aName + ""; + text += aHasProblem + ? " [*]\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 = "(" + perc + "%) "; + if (aT._memoryUsed === treeBytes) { + perc = "100.0"; + } else { + perc = (100 * aT._memoryUsed / treeBytes).toFixed(2); + perc = pad(perc, 5, '0'); } + perc = "(" + perc + "%) "; 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 "

" + aTreeName + "

\n
" + text + "
\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 "

Explicit Allocations

\n" + "
" + text + "
\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 "

Other Measurements

\n
" + text + "
\n"; + const desc = "This list contains other memory measurements that cross-cut " + + "the requested memory measurements above." + return "

Other Measurements

\n" + + "
" + text + "
\n"; } diff --git a/toolkit/components/aboutmemory/tests/chrome/test_aboutmemory.xul b/toolkit/components/aboutmemory/tests/chrome/test_aboutmemory.xul index ae540a69f6f6..f29a7e596e0f 100644 --- a/toolkit/components/aboutmemory/tests/chrome/test_aboutmemory.xul +++ b/toolkit/components/aboutmemory/tests/chrome/test_aboutmemory.xul @@ -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\ " diff --git a/xpcom/base/nsIMemoryReporter.idl b/xpcom/base/nsIMemoryReporter.idl index 2cfa0b7d6c32..1712e58459ca 100644 --- a/xpcom/base/nsIMemoryReporter.idl +++ b/xpcom/base/nsIMemoryReporter.idl @@ -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; } \ }; \ diff --git a/xpcom/base/nsMemoryReporterManager.cpp b/xpcom/base/nsMemoryReporterManager.cpp index d02159549b4a..ef2b3ff3b927 100644 --- a/xpcom/base/nsMemoryReporterManager.cpp +++ b/xpcom/base/nsMemoryReporterManager.cpp @@ -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 #include -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 -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()); diff --git a/xpcom/base/nsMemoryReporterManager.h b/xpcom/base/nsMemoryReporterManager.h index 274195812c37..951399e7b244 100644 --- a/xpcom/base/nsMemoryReporterManager.h +++ b/xpcom/base/nsMemoryReporterManager.h @@ -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; };