Bug 653630 - Allow non-leaf reporters in about:memory. r=sdwilsh.

This commit is contained in:
Nicholas Nethercote 2011-05-11 16:09:50 -07:00
parent d258fef2cf
commit 82a8ac704a
4 changed files with 96 additions and 80 deletions

View File

@ -164,18 +164,21 @@ function update()
*/ */
function genProcessText(aProcess, aTmrs) 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 * From a list of memory reporters, builds a tree that mirrors the tree
* structure that will be shown as output. * structure that will be shown as output.
* *
* @param aTreeName * @param aTreeName
* The name of the tree; either "mapped" or "heap-used" * The name of the tree; either "mapped" or "heap-used"
* @param aTreeRootTpath
* The tpath of the top node in the tree
* @param aTreeRootDesc
* The description of the top node in the tree
* @param aOtherDescTail
* Extra description for the end of the "aTreeName/other" entry
* @param aOmitThresholdPerc * @param aOmitThresholdPerc
* The threshold percentage; entries that account for less than * The threshold percentage; entries that account for less than
* this fraction are aggregated * this fraction are aggregated
@ -185,10 +188,10 @@ function genProcessText(aProcess, aTmrs)
* _description: string; * _description: string;
* _memoryUsed: number; * _memoryUsed: number;
* _kids: [Node]; * _kids: [Node];
* _hasReporter: boolean; (might not be defined)
* } * }
*/ */
function buildTree(aTreeName, aTreeRootTpath, aTreeRootDesc, aOtherDescTail, function buildTree(aTreeName, aOmitThresholdPerc)
aOmitThresholdPerc)
{ {
function findKid(aName, aKids) function findKid(aName, aKids)
{ {
@ -201,12 +204,13 @@ function genProcessText(aProcess, aTmrs)
} }
// We want to process all reporters that begin with 'aTreeName'. // We want to process all reporters that begin with 'aTreeName'.
// First we build the tree but only filling in '_name' and '_kids'. // First we build the tree but only filling in '_name', '_kids' and
var t = { _name:aTreeName, _kids:[] }; // maybe '._hasReporter'. This is done top-down from the reporters.
for (var _tpath in aTmrs) { var t = { _name: "falseRoot", _kids: [] };
var tmr = aTmrs[_tpath]; for (var tpath in aTmrs) {
if (tmr._tpath.slice(0, aTreeName.length + 1) === aTreeName + "/") { var tmr = aTmrs[tpath];
var names = tmr._tpath.slice(aTreeName.length + 1).split('/'); if (tmr._tpath.slice(0, aTreeName.length) === aTreeName) {
var names = tmr._tpath.split('/');
var u = t; var u = t;
for (var i = 0; i < names.length; i++) { for (var i = 0; i < names.length; i++) {
var name = names[i]; var name = names[i];
@ -214,60 +218,66 @@ function genProcessText(aProcess, aTmrs)
if (uMatch) { if (uMatch) {
u = uMatch; u = uMatch;
} else { } else {
var v = { _name:name, _kids:[] }; var v = { _name: name, _kids: [] };
u._kids.push(v); u._kids.push(v);
u = v; u = v;
} }
} }
u._hasReporter = true;
} }
} }
// Using falseRoot makes the above code simpler. Now discard it, leaving
// aTreeName at the root.
t = t._kids[0];
// Next, fill in '_description' and '_memoryUsed' for each node. For // Next, fill in '_description' and '_memoryUsed' for each node. This is
// interior nodes, '_memoryUsed' is computed by summing child nodes. // done bottom-up because for most non-leaf nodes '_memoryUsed' and
// '_description' are determined from the child nodes.
function fillInTree(aT, aPretpath) function fillInTree(aT, aPretpath)
{ {
var tpath = aPretpath ? aPretpath + '/' + aT._name : aT._name; var tpath = aPretpath ? aPretpath + '/' + aT._name : aT._name;
if (aT._kids.length === 0) { if (aT._kids.length === 0) {
// Leaf node. Must have a reporter.
aT._memoryUsed = getBytes(aTmrs, tpath); aT._memoryUsed = getBytes(aTmrs, tpath);
aT._description = getDescription(aTmrs, tpath); aT._description = getDescription(aTmrs, tpath);
} else { } else {
var bytes = 0; // Non-leaf node. Get the size of the children.
var childrenBytes = 0;
for (var i = 0; i < aT._kids.length; i++) { for (var i = 0; i < aT._kids.length; i++) {
// Allow for -1 (ie. "unknown"), treat it like 0. // Allow for -1 (ie. "unknown"), treat it like 0.
var b = fillInTree(aT._kids[i], tpath); var b = fillInTree(aT._kids[i], tpath);
bytes += (b === -1 ? 0 : b); childrenBytes += (b === -1 ? 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 other = {
_name: "other",
_description: "All unclassified " + aT._name + " memory.",
_memoryUsed: aT._memoryUsed - childrenBytes,
_kids: []
};
aT._kids.push(other);
}
} 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._memoryUsed = bytes;
aT._description = "The sum of all entries below " + tpath + ".";
} }
return aT._memoryUsed; return aT._memoryUsed;
} }
fillInTree(t, ""); fillInTree(t, "");
// Add the "aTreeName/other" node, which is derived from existing
// nodes, then update the root node accordingly. (But don't do this
// if the root node byte count is -1, ie. unknown).
var nonOtherBytes = t._memoryUsed;
var treeBytes = getBytes(aTmrs, aTreeRootTpath);
if (treeBytes !== -1) {
var otherBytes = treeBytes - nonOtherBytes;
var other = {
_name:"other",
_description:"All unclassified " + aTreeName + " memory." +
aOtherDescTail,
_memoryUsed:otherBytes,
_kids:[]
};
t._kids.push(other);
}
t._memoryUsed = treeBytes;
t._description = aTreeRootDesc;
function shouldOmit(aBytes) function shouldOmit(aBytes)
{ {
return !gVerbose && return !gVerbose &&
treeBytes !== -1 && t._memoryUsed !== -1 &&
(100 * aBytes / treeBytes) < aOmitThresholdPerc; (100 * aBytes / t._memoryUsed) < aOmitThresholdPerc;
} }
/** /**
@ -300,7 +310,7 @@ function genProcessText(aProcess, aTmrs)
_name: "(" + n + " omitted)", _name: "(" + n + " omitted)",
_description: "Omitted sub-trees: " + aggNames.join(", ") + ".", _description: "Omitted sub-trees: " + aggNames.join(", ") + ".",
_memoryUsed: aggBytes, _memoryUsed: aggBytes,
_kids:[] _kids: []
}; };
aT._kids[i0] = tmrSub; aT._kids[i0] = tmrSub;
break; break;
@ -313,19 +323,11 @@ function genProcessText(aProcess, aTmrs)
return t; return t;
} }
var mappedOtherDescTail = // The threshold used for the "mapped" tree is lower than the one for the
" This includes code and data segments, and thread stacks." // "heap-used" tree, because the "mapped" total size is dominated (especially
var mappedRootDesc = getDescription(aTmrs, "mapped"); // on Mac) by memory usage that isn't covered by more specific reporters.
// The threshold used here is much lower than the one for the heap-used var mappedTree = buildTree("mapped", 0.01);
// tree, because the "mapped" total size is so much bigger relative to var heapUsedTree = buildTree("heap-used", 0.1);
// the interesting entries in the "mapped" tree.
var mappedTree = buildTree("mapped", "mapped", mappedRootDesc,
mappedOtherDescTail, 0.01);
var heapUsedOtherDescTail = "";
var heapUsedRootDesc = "See mapped/heap/used above.";
var heapUsedTree = buildTree("heap-used", "mapped/heap/used",
heapUsedRootDesc, heapUsedOtherDescTail, 0.1);
// Nb: the newlines give nice spacing if we cut+paste into a text buffer. // Nb: the newlines give nice spacing if we cut+paste into a text buffer.
var text = ""; var text = "";
@ -535,7 +537,9 @@ function genTreeText(aT, aTreeName)
// Generate the percentage. // Generate the percentage.
var perc = ""; var perc = "";
if (treeBytes !== -1) { if (treeBytes !== -1) {
if (aT._memoryUsed === treeBytes) { if (aT._memoryUsed === -1) {
perc = "??.??";
} else if (aT._memoryUsed === treeBytes) {
perc = "100.0"; perc = "100.0";
} else { } else {
perc = (100 * aT._memoryUsed / treeBytes).toFixed(2); perc = (100 * aT._memoryUsed / treeBytes).toFixed(2);
@ -549,7 +553,7 @@ function genTreeText(aT, aTreeName)
for (var i = 0; i < aT._kids.length; i++) { for (var i = 0; i < aT._kids.length; i++) {
// 3 is the standard depth, the callee adjusts it if necessary. // 3 is the standard depth, the callee adjusts it if necessary.
aIndentGuide.push({ _isLastKid:(i === aT._kids.length - 1), _depth:3 }); aIndentGuide.push({ _isLastKid: (i === aT._kids.length - 1), _depth: 3 });
text += genTreeText2(aT._kids[i], aIndentGuide, tBytesLength); text += genTreeText2(aT._kids[i], aIndentGuide, tBytesLength);
aIndentGuide.pop(); aIndentGuide.pop();
} }

View File

@ -44,6 +44,9 @@
{ path: "heap-used/d", memoryUsed: 499 * KB }, // aggregated { path: "heap-used/d", memoryUsed: 499 * KB }, // aggregated
{ path: "heap-used/e", memoryUsed: 100 * KB }, // aggregated { path: "heap-used/e", memoryUsed: 100 * KB }, // aggregated
{ path: "heap-used/f/g/h/i", memoryUsed: 20 * MB }, { 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: "other1", memoryUsed: 111 * MB },
{ path: "other2", memoryUsed: 222 * MB }, { path: "other2", memoryUsed: 222 * MB },
@ -67,8 +70,8 @@
]]> ]]>
</script> </script>
<iframe id="amFrame" src="about:memory"></iframe> <iframe id="amFrame" height="300" src="about:memory"></iframe>
<iframe id="amvFrame" src="about:memory?verbose"></iframe> <iframe id="amvFrame" height="300" src="about:memory?verbose"></iframe>
<script type="application/javascript"> <script type="application/javascript">
<![CDATA[ <![CDATA[
@ -94,11 +97,15 @@ Used Heap Memory\n\
│ └───75.00 MB (15.00%) -- b\n\ │ └───75.00 MB (15.00%) -- b\n\
├──123.00 MB (24.60%) -- c\n\ ├──123.00 MB (24.60%) -- c\n\
├───99.00 MB (19.80%) -- a\n\ ├───99.00 MB (19.80%) -- a\n\
├───25.42 MB (05.08%) -- other\n\
├───20.00 MB (04.00%) -- f\n\ ├───20.00 MB (04.00%) -- f\n\
│ └──20.00 MB (04.00%) -- g\n\ │ └──20.00 MB (04.00%) -- g\n\
│ └──20.00 MB (04.00%) -- h\n\ │ └──20.00 MB (04.00%) -- h\n\
│ └──20.00 MB (04.00%) -- i\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\ └────0.58 MB (00.12%) -- (2 omitted)\n\
\n\ \n\
Other Measurements\n\ Other Measurements\n\
@ -165,11 +172,15 @@ Used Heap Memory\n\
│ └───78,643,200 B (15.00%) -- b\n\ │ └───78,643,200 B (15.00%) -- b\n\
├──128,974,848 B (24.60%) -- c\n\ ├──128,974,848 B (24.60%) -- c\n\
├──103,809,024 B (19.80%) -- a\n\ ├──103,809,024 B (19.80%) -- a\n\
├───26,649,600 B (05.08%) -- other\n\
├───20,971,520 B (04.00%) -- f\n\ ├───20,971,520 B (04.00%) -- f\n\
│ └──20,971,520 B (04.00%) -- g\n\ │ └──20,971,520 B (04.00%) -- g\n\
│ └──20,971,520 B (04.00%) -- h\n\ │ └──20,971,520 B (04.00%) -- h\n\
│ └──20,971,520 B (04.00%) -- i\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\ ├──────510,976 B (00.10%) -- d\n\
└──────102,400 B (00.02%) -- e\n\ └──────102,400 B (00.02%) -- e\n\
\n\ \n\

View File

@ -51,28 +51,26 @@ interface nsIMemoryReporter : nsISupports
* *
* - Paths starting with "mapped" represent non-overlapping regions of mapped * - Paths starting with "mapped" represent non-overlapping regions of mapped
* memory. Each one can be viewed as representing a path in a tree from * memory. Each one can be viewed as representing a path in a tree from
* the root node ("mapped") to a leaf node. The one exception is the * the root node ("mapped") to a node lower in the tree; this lower node
* special "mapped" path which represents the root of that tree. So, for * may be a non-leaf or a leaf node. So, for example, "mapped",
* example, "mapped/heap/used", "mapped/heap/unused", * "mapped/heap/used", "mapped/heap/unused", "mapped/js/mjit-code",
* "mapped/js/mjit-code", mapped/js/tjit-code", plus the special "mapped" * and "mapped/js/tjit-code" define this tree:
* path, define this tree:
* *
* mapped * mapped [*]
* |--heap * |--heap
* | |--used * | |--used [*]
* | \--unused * | \--unused [*]
* \--js * \--js
* |--mjit-code * |--mjit-code [*]
* \--tjit-code * \--tjit-code [*]
* *
* "mapped/heap" would not be a valid path in this example because "heap" * Nodes marked with a [*] have a reporter.
* is not a leaf node.
* *
* - Paths starting with "heap-used" represent non-overlapping regions of * - Paths starting with "heap-used" represent non-overlapping regions of
* used heap memory. The "mapped" rules above apply equally here. The * used heap memory. The "mapped" rules above apply equally here. These
* exceptional path for this tree is "mapped/heap/used" (and so this tree * paths are actually sub-paths of "mapped/heap/used", but that reporter
* is a sub-tree of the "mapped" tree). When shown in about:memory, this * is duplicated under the name "heap-used" to make the processing for
* tree's root node is given the synonym "heap-used". * about:memory simpler.
* *
* - All other paths represent cross-cuttings memory regions, ie. ones that * - All other paths represent cross-cuttings memory regions, ie. ones that
* may overlap arbitrarily with regions in the "mapped" and "heap-used" * may overlap arbitrarily with regions in the "mapped" and "heap-used"

View File

@ -149,8 +149,11 @@ static PRInt64 GetResident(void *)
// byte count returned is always -1. // byte count returned is always -1.
NS_MEMORY_REPORTER_IMPLEMENT(Mapped, NS_MEMORY_REPORTER_IMPLEMENT(Mapped,
"mapped", "mapped",
"Memory mapped by the process. Note that 'resident' is a better measure " "Memory mapped by the process, including the heap, code and data segments, "
"of memory resources used by the process. " "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 " "On Windows (XP SP2 or later only) this is the private usage and does not "
"include memory shared with other processes. " "include memory shared with other processes. "
"On Mac and Linux this is the vsize figure as reported by 'top' or 'ps' " "On Mac and Linux this is the vsize figure as reported by 'top' or 'ps' "