mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-29 15:52:07 +00:00
Bug 653630 - Allow non-leaf reporters in about:memory. r=sdwilsh.
This commit is contained in:
parent
d258fef2cf
commit
82a8ac704a
@ -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();
|
||||||
}
|
}
|
||||||
|
@ -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\
|
||||||
|
@ -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"
|
||||||
|
@ -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' "
|
||||||
|
Loading…
Reference in New Issue
Block a user