зеркало из https://github.com/mozilla/gecko-dev.git
Bug 755583 (part 4) - Split up the smaps/ memory reporter tree. r=jlebar.
This commit is contained in:
Родитель
e1b88c680a
Коммит
9976c1f421
|
@ -229,11 +229,11 @@ function processMemoryReporters(aMgr, aIgnoreSingle, aIgnoreMulti,
|
||||||
|
|
||||||
let e = aMgr.enumerateMultiReporters();
|
let e = aMgr.enumerateMultiReporters();
|
||||||
while (e.hasMoreElements()) {
|
while (e.hasMoreElements()) {
|
||||||
let mrOrig = e.getNext().QueryInterface(Ci.nsIMemoryMultiReporter);
|
let mr = e.getNext().QueryInterface(Ci.nsIMemoryMultiReporter);
|
||||||
let name = mrOrig.name;
|
let name = mr.name;
|
||||||
try {
|
try {
|
||||||
if (!aIgnoreMulti(name)) {
|
if (!aIgnoreMulti(name)) {
|
||||||
mrOrig.collectReports(handleReport, null);
|
mr.collectReports(handleReport, null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (ex) {
|
catch (ex) {
|
||||||
|
@ -269,14 +269,13 @@ function checkReport(aUnsafePath, aKind, aUnits, aAmount, aDescription)
|
||||||
assert(gSentenceRegExp.test(aDescription),
|
assert(gSentenceRegExp.test(aDescription),
|
||||||
"non-sentence explicit description");
|
"non-sentence explicit description");
|
||||||
|
|
||||||
} else if (aUnsafePath.startsWith("smaps/")) {
|
} else if (isSmapsPath(aUnsafePath)) {
|
||||||
assert(aKind === KIND_NONHEAP, "bad smaps kind");
|
assert(aKind === KIND_NONHEAP, "bad smaps kind");
|
||||||
assert(aUnits === UNITS_BYTES, "bad smaps units");
|
assert(aUnits === UNITS_BYTES, "bad smaps units");
|
||||||
assert(aDescription !== "", "empty smaps description");
|
assert(aDescription !== "", "empty smaps description");
|
||||||
|
|
||||||
} else if (aKind === KIND_SUMMARY) {
|
} else if (aKind === KIND_SUMMARY) {
|
||||||
assert(!aUnsafePath.startsWith("explicit/") &&
|
assert(!aUnsafePath.startsWith("explicit/") && !isSmapsPath(aUnsafePath),
|
||||||
!aUnsafePath.startsWith("smaps/"),
|
|
||||||
"bad SUMMARY path");
|
"bad SUMMARY path");
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
@ -379,7 +378,7 @@ mappings is currently using. Mappings which are not in the swap file (i.e., \
|
||||||
nodes which would have a value of 0 in this tree) are omitted."
|
nodes which would have a value of 0 in this tree) are omitted."
|
||||||
};
|
};
|
||||||
|
|
||||||
const kTreeNames = {
|
const kSectionNames = {
|
||||||
'explicit': 'Explicit Allocations',
|
'explicit': 'Explicit Allocations',
|
||||||
'resident': 'Resident Set Size (RSS) Breakdown',
|
'resident': 'Resident Set Size (RSS) Breakdown',
|
||||||
'pss': 'Proportional Set Size (PSS) Breakdown',
|
'pss': 'Proportional Set Size (PSS) Breakdown',
|
||||||
|
@ -388,8 +387,17 @@ const kTreeNames = {
|
||||||
'other': 'Other Measurements'
|
'other': 'Other Measurements'
|
||||||
};
|
};
|
||||||
|
|
||||||
const kMapTreePaths =
|
const kSmapsTreePrefixes = ['resident/', 'pss/', 'vsize/', 'swap/'];
|
||||||
['smaps/resident', 'smaps/pss', 'smaps/vsize', 'smaps/swap'];
|
|
||||||
|
function isSmapsPath(aUnsafePath)
|
||||||
|
{
|
||||||
|
for (let i = 0; i < kSmapsTreePrefixes.length; i++) {
|
||||||
|
if (aUnsafePath.startsWith(kSmapsTreePrefixes[i])) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
//---------------------------------------------------------------------------
|
//---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
@ -519,13 +527,6 @@ Report.prototype = {
|
||||||
this._amount += r._amount;
|
this._amount += r._amount;
|
||||||
this._nMerged = this._nMerged ? this._nMerged + 1 : 2;
|
this._nMerged = this._nMerged ? this._nMerged + 1 : 2;
|
||||||
},
|
},
|
||||||
|
|
||||||
treeNameMatches: function(aTreeName) {
|
|
||||||
// Nb: the '/' must be present, because we have a KIND_OTHER reporter
|
|
||||||
// called "explicit" which is not part of the "explicit" tree.
|
|
||||||
return this._unsafePath.startsWith(aTreeName) &&
|
|
||||||
this._unsafePath.charAt(aTreeName.length) === '/';
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
function getReportsByProcess(aMgr)
|
function getReportsByProcess(aMgr)
|
||||||
|
@ -535,18 +536,18 @@ function getReportsByProcess(aMgr)
|
||||||
// that reports from these multi-reporters can reach here as single reports
|
// that reports from these multi-reporters can reach here as single reports
|
||||||
// if they were in the child process.)
|
// if they were in the child process.)
|
||||||
|
|
||||||
function ignoreSingle(aPath)
|
function ignoreSingle(aUnsafePath)
|
||||||
{
|
{
|
||||||
return (aPath.startsWith("smaps/") && !gVerbose) ||
|
return (isSmapsPath(aUnsafePath) && !gVerbose) ||
|
||||||
aPath.startsWith("compartments/") ||
|
aUnsafePath.startsWith("compartments/") ||
|
||||||
aPath.startsWith("ghost-windows/");
|
aUnsafePath.startsWith("ghost-windows/");
|
||||||
}
|
}
|
||||||
|
|
||||||
function ignoreMulti(aName)
|
function ignoreMulti(aMRName)
|
||||||
{
|
{
|
||||||
return (aName === "smaps" && !gVerbose) ||
|
return (aMRName === "smaps" && !gVerbose) ||
|
||||||
aName === "compartments" ||
|
aMRName === "compartments" ||
|
||||||
aName === "ghost-windows";
|
aMRName === "ghost-windows";
|
||||||
}
|
}
|
||||||
|
|
||||||
let reportsByProcess = {};
|
let reportsByProcess = {};
|
||||||
|
@ -623,36 +624,26 @@ TreeNode.compare = function(a, b) {
|
||||||
*
|
*
|
||||||
* @param aReports
|
* @param aReports
|
||||||
* The table of Reports, indexed by _unsafePath.
|
* The table of Reports, indexed by _unsafePath.
|
||||||
* @param aTreeName
|
* @param aTreePrefix
|
||||||
* The name of the tree being built.
|
* The prefix (name) of the tree being built. Must have '/' on the end.
|
||||||
* @return The built tree.
|
* @return The built tree.
|
||||||
*/
|
*/
|
||||||
function buildTree(aReports, aTreeName)
|
function buildTree(aReports, aTreePrefix)
|
||||||
{
|
{
|
||||||
// We want to process all reports that begin with |aTreeName|. First we
|
assert(aTreePrefix.indexOf('/') == aTreePrefix.length - 1,
|
||||||
|
"aTreePrefix doesn't end in '/'");
|
||||||
|
|
||||||
|
// We want to process all reports that begin with |aTreePrefix|. First we
|
||||||
// build the tree but only fill the properties that we can with a top-down
|
// build the tree but only fill the properties that we can with a top-down
|
||||||
// traversal.
|
// traversal.
|
||||||
|
|
||||||
// There should always be at least one matching Report object when
|
|
||||||
// |aTreeName| is "explicit". But there may be zero for "smaps" trees; if
|
|
||||||
// that happens, bail.
|
|
||||||
let foundReport = false;
|
let foundReport = false;
|
||||||
for (let unsafePath in aReports) {
|
|
||||||
if (aReports[unsafePath].treeNameMatches(aTreeName)) {
|
|
||||||
foundReport = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!foundReport) {
|
|
||||||
assert(aTreeName !== 'explicit', "aTreeName !== 'explicit'");
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
let t = new TreeNode("falseRoot");
|
let t = new TreeNode("falseRoot");
|
||||||
for (let unsafePath in aReports) {
|
for (let unsafePath in aReports) {
|
||||||
// Add any missing nodes in the tree implied by the unsafePath.
|
// Add any missing nodes in the tree implied by the unsafePath.
|
||||||
|
if (unsafePath.startsWith(aTreePrefix)) {
|
||||||
|
foundReport = true;
|
||||||
let r = aReports[unsafePath];
|
let r = aReports[unsafePath];
|
||||||
if (r.treeNameMatches(aTreeName)) {
|
|
||||||
let unsafeNames = r._unsafePath.split('/');
|
let unsafeNames = r._unsafePath.split('/');
|
||||||
let u = t;
|
let u = t;
|
||||||
for (let i = 0; i < unsafeNames.length; i++) {
|
for (let i = 0; i < unsafeNames.length; i++) {
|
||||||
|
@ -677,8 +668,16 @@ function buildTree(aReports, aTreeName)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// There should always be at least one matching Report object when
|
||||||
|
// |aTreePrefix| is "explicit/". But there may be zero for smaps trees; if
|
||||||
|
// that happens, bail.
|
||||||
|
if (!foundReport) {
|
||||||
|
assert(aTreePrefix !== 'explicit/', "aTreePrefix !== 'explicit/'");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
// Using falseRoot makes the above code simpler. Now discard it, leaving
|
// Using falseRoot makes the above code simpler. Now discard it, leaving
|
||||||
// aTreeName at the root.
|
// aTreePrefix at the root.
|
||||||
t = t._kids[0];
|
t = t._kids[0];
|
||||||
|
|
||||||
// Next, fill in the remaining properties bottom-up.
|
// Next, fill in the remaining properties bottom-up.
|
||||||
|
@ -704,16 +703,6 @@ function buildTree(aReports, aTreeName)
|
||||||
|
|
||||||
fillInNonLeafNodes(t);
|
fillInNonLeafNodes(t);
|
||||||
|
|
||||||
// Reduce the depth of the tree by the number of occurrences of '/' in
|
|
||||||
// aTreeName. (Thus the tree named 'foo/bar/baz' will be rooted at 'baz'.)
|
|
||||||
let slashCount = 0;
|
|
||||||
for (let i = 0; i < aTreeName.length; i++) {
|
|
||||||
if (aTreeName[i] == '/') {
|
|
||||||
assert(t._kids.length == 1, "Not expecting multiple kids here.");
|
|
||||||
t = t._kids[0];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set the (unsafe) description on the root node.
|
// Set the (unsafe) description on the root node.
|
||||||
t._description = kTreeDescriptions[t._unsafeName];
|
t._description = kTreeDescriptions[t._unsafeName];
|
||||||
|
|
||||||
|
@ -721,7 +710,7 @@ function buildTree(aReports, aTreeName)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Ignore all the memory reports that belong to a "smaps" tree; this involves
|
* Ignore all the memory reports that belong to a smaps tree; this involves
|
||||||
* explicitly marking them as done.
|
* explicitly marking them as done.
|
||||||
*
|
*
|
||||||
* @param aReports
|
* @param aReports
|
||||||
|
@ -731,7 +720,7 @@ function ignoreSmapsTrees(aReports)
|
||||||
{
|
{
|
||||||
for (let unsafePath in aReports) {
|
for (let unsafePath in aReports) {
|
||||||
let r = aReports[unsafePath];
|
let r = aReports[unsafePath];
|
||||||
if (r.treeNameMatches("smaps")) {
|
if (isSmapsPath(r._unsafePath)) {
|
||||||
r._done = true;
|
r._done = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -940,22 +929,22 @@ function appendProcessReportsElements(aP, aProcess, aReports,
|
||||||
// We'll fill this in later.
|
// We'll fill this in later.
|
||||||
let warningsDiv = appendElement(aP, "div", "accuracyWarning");
|
let warningsDiv = appendElement(aP, "div", "accuracyWarning");
|
||||||
|
|
||||||
let explicitTree = buildTree(aReports, 'explicit');
|
let explicitTree = buildTree(aReports, 'explicit/');
|
||||||
let hasKnownHeapAllocated = fixUpExplicitTree(explicitTree, aReports);
|
let hasKnownHeapAllocated = fixUpExplicitTree(explicitTree, aReports);
|
||||||
sortTreeAndInsertAggregateNodes(explicitTree._amount, explicitTree);
|
sortTreeAndInsertAggregateNodes(explicitTree._amount, explicitTree);
|
||||||
appendTreeElements(aP, explicitTree, aProcess);
|
appendTreeElements(aP, explicitTree, aProcess);
|
||||||
|
|
||||||
// We only show these breakdown trees in verbose mode.
|
// We only show these breakdown trees in verbose mode.
|
||||||
if (gVerbose) {
|
if (gVerbose) {
|
||||||
kMapTreePaths.forEach(function(t) {
|
kSmapsTreePrefixes.forEach(function(aTreePrefix) {
|
||||||
let tree = buildTree(aReports, t);
|
let t = buildTree(aReports, aTreePrefix);
|
||||||
|
|
||||||
// |tree| will be null if we don't have any reports for the given
|
// |t| will be null if we don't have any reports for the given
|
||||||
// unsafePath.
|
// unsafePath.
|
||||||
if (tree) {
|
if (t) {
|
||||||
sortTreeAndInsertAggregateNodes(tree._amount, tree);
|
sortTreeAndInsertAggregateNodes(t._amount, t);
|
||||||
tree._hideKids = true; // smaps trees are always initially collapsed
|
t._hideKids = true; // smaps trees are always initially collapsed
|
||||||
appendTreeElements(aP, tree, aProcess);
|
appendTreeElements(aP, t, aProcess);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
|
@ -1339,7 +1328,7 @@ function appendTreeElements(aPOuter, aT, aProcess)
|
||||||
appendMrValueSpan(d, tString, tIsInvalid);
|
appendMrValueSpan(d, tString, tIsInvalid);
|
||||||
appendElementWithText(d, "span", "mrPerc", percText);
|
appendElementWithText(d, "span", "mrPerc", percText);
|
||||||
|
|
||||||
// We don't want to show '(nonheap)' on a tree like 'smaps/vsize', since
|
// We don't want to show '(nonheap)' on a tree like 'vsize/', since
|
||||||
// the whole tree is non-heap.
|
// the whole tree is non-heap.
|
||||||
let kind = isExplicitTree ? aT._kind : undefined;
|
let kind = isExplicitTree ? aT._kind : undefined;
|
||||||
appendMrNameSpan(d, kind, kidsState, aT._description, aT._unsafeName,
|
appendMrNameSpan(d, kind, kidsState, aT._description, aT._unsafeName,
|
||||||
|
@ -1381,7 +1370,7 @@ function appendTreeElements(aPOuter, aT, aProcess)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
appendSectionHeader(aPOuter, kTreeNames[aT._unsafeName]);
|
appendSectionHeader(aPOuter, kSectionNames[aT._unsafeName]);
|
||||||
|
|
||||||
let pre = appendElement(aPOuter, "pre", "entries");
|
let pre = appendElement(aPOuter, "pre", "entries");
|
||||||
appendTreeElements2(pre, /* prePath = */"", aT, [], "", rootStringLength);
|
appendTreeElements2(pre, /* prePath = */"", aT, [], "", rootStringLength);
|
||||||
|
@ -1444,7 +1433,7 @@ OtherReport.compare = function(a, b) {
|
||||||
*/
|
*/
|
||||||
function appendOtherElements(aP, aReportsByProcess)
|
function appendOtherElements(aP, aReportsByProcess)
|
||||||
{
|
{
|
||||||
appendSectionHeader(aP, kTreeNames['other']);
|
appendSectionHeader(aP, kSectionNames['other']);
|
||||||
|
|
||||||
let pre = appendElement(aP, "pre", "entries");
|
let pre = appendElement(aP, "pre", "entries");
|
||||||
|
|
||||||
|
@ -1573,14 +1562,14 @@ function getCompartmentsByProcess(aMgr)
|
||||||
// (Note that some such reports can reach here as single reports if they were
|
// (Note that some such reports can reach here as single reports if they were
|
||||||
// in the child process.)
|
// in the child process.)
|
||||||
|
|
||||||
function ignoreSingle(aPath)
|
function ignoreSingle(aUnsafePath)
|
||||||
{
|
{
|
||||||
return !aPath.startsWith("compartments/");
|
return !aUnsafePath.startsWith("compartments/");
|
||||||
}
|
}
|
||||||
|
|
||||||
function ignoreMulti(aName)
|
function ignoreMulti(aMRName)
|
||||||
{
|
{
|
||||||
return aName !== "compartments";
|
return aMRName !== "compartments";
|
||||||
}
|
}
|
||||||
|
|
||||||
let compartmentsByProcess = {};
|
let compartmentsByProcess = {};
|
||||||
|
@ -1648,9 +1637,9 @@ GhostWindow.prototype = {
|
||||||
|
|
||||||
function getGhostWindowsByProcess(aMgr)
|
function getGhostWindowsByProcess(aMgr)
|
||||||
{
|
{
|
||||||
function ignoreSingle(aPath)
|
function ignoreSingle(aUnsafePath)
|
||||||
{
|
{
|
||||||
return !aPath.startsWith('ghost-windows/')
|
return !aUnsafePath.startsWith('ghost-windows/')
|
||||||
}
|
}
|
||||||
|
|
||||||
function ignoreMulti(aName)
|
function ignoreMulti(aName)
|
||||||
|
|
|
@ -121,13 +121,13 @@
|
||||||
function f(aP, aA) {
|
function f(aP, aA) {
|
||||||
aCbObj.callback("", aP, NONHEAP, BYTES, aA * 4 * KB, "Desc.", aClosure);
|
aCbObj.callback("", aP, NONHEAP, BYTES, aA * 4 * KB, "Desc.", aClosure);
|
||||||
}
|
}
|
||||||
f("smaps/vsize/a", 24);
|
f("vsize/a", 24);
|
||||||
f("smaps/swap/a", 1);
|
f("swap/a", 1);
|
||||||
f("smaps/swap/a", 2);
|
f("swap/a", 2);
|
||||||
f("smaps/vsize/a", 19);
|
f("vsize/a", 19);
|
||||||
f("smaps/swap/b/c", 10);
|
f("swap/b/c", 10);
|
||||||
f("smaps/resident/a", 42);
|
f("resident/a", 42);
|
||||||
f("smaps/pss/a", 43);
|
f("pss/a", 43);
|
||||||
},
|
},
|
||||||
explicitNonHeap: 0
|
explicitNonHeap: 0
|
||||||
},
|
},
|
||||||
|
@ -181,8 +181,8 @@
|
||||||
// The fact that we skip the "smaps" multi-reporter in the main
|
// The fact that we skip the "smaps" multi-reporter in the main
|
||||||
// process won't cause these to be skipped; the fall-back skipping will
|
// process won't cause these to be skipped; the fall-back skipping will
|
||||||
// be hit instead.
|
// be hit instead.
|
||||||
f("2nd", "smaps/vsize/e", NONHEAP, 24*4*KB),
|
f("2nd", "vsize/e", NONHEAP, 24*4*KB),
|
||||||
f("2nd", "smaps/vsize/f", NONHEAP, 24*4*KB),
|
f("2nd", "vsize/f", NONHEAP, 24*4*KB),
|
||||||
|
|
||||||
// Check that we can handle "heap-allocated" not being present.
|
// Check that we can handle "heap-allocated" not being present.
|
||||||
f("3rd", "explicit/a/b", HEAP, 333 * MB),
|
f("3rd", "explicit/a/b", HEAP, 333 * MB),
|
||||||
|
|
|
@ -124,8 +124,7 @@ void GetBasename(const nsCString &aPath, nsACString &aOut)
|
||||||
}
|
}
|
||||||
|
|
||||||
// MapsReporter::CollectReports uses this stuct to keep track of whether it's
|
// MapsReporter::CollectReports uses this stuct to keep track of whether it's
|
||||||
// seen a mapping under 'smaps/resident', 'smaps/pss', 'smaps/vsize', and
|
// seen a mapping under 'resident', 'pss', 'vsize', and 'swap'.
|
||||||
// 'smaps/swap'.
|
|
||||||
struct CategoriesSeen {
|
struct CategoriesSeen {
|
||||||
CategoriesSeen() :
|
CategoriesSeen() :
|
||||||
mSeenResident(false),
|
mSeenResident(false),
|
||||||
|
@ -229,17 +228,18 @@ MapsReporter::CollectReports(nsIMemoryMultiReporterCallback *aCb,
|
||||||
|
|
||||||
fclose(f);
|
fclose(f);
|
||||||
|
|
||||||
// For sure we should have created some node under 'smaps/resident' and
|
// For sure we should have created some node under 'resident' and
|
||||||
// 'smaps/vsize'; otherwise we're probably not reading smaps correctly. If we
|
// 'vsize'; otherwise we're probably not reading smaps correctly. If we
|
||||||
// didn't create a node under 'smaps/swap', create one here so about:memory
|
// didn't create a node under 'swap', create one here so about:memory
|
||||||
// knows to create an empty 'smaps/swap' tree. See also bug 682735.
|
// knows to create an empty 'swap' tree; it needs a 'total' child because
|
||||||
|
// about:memory expects at least one report whose path begins with 'swap/'.
|
||||||
|
|
||||||
NS_ASSERTION(categoriesSeen.mSeenVsize, "Didn't create a vsize node?");
|
NS_ASSERTION(categoriesSeen.mSeenVsize, "Didn't create a vsize node?");
|
||||||
NS_ASSERTION(categoriesSeen.mSeenVsize, "Didn't create a resident node?");
|
NS_ASSERTION(categoriesSeen.mSeenVsize, "Didn't create a resident node?");
|
||||||
if (!categoriesSeen.mSeenSwap) {
|
if (!categoriesSeen.mSeenSwap) {
|
||||||
nsresult rv;
|
nsresult rv;
|
||||||
rv = aCb->Callback(NS_LITERAL_CSTRING(""),
|
rv = aCb->Callback(NS_LITERAL_CSTRING(""),
|
||||||
NS_LITERAL_CSTRING("smaps/swap/total"),
|
NS_LITERAL_CSTRING("swap/total"),
|
||||||
nsIMemoryReporter::KIND_NONHEAP,
|
nsIMemoryReporter::KIND_NONHEAP,
|
||||||
nsIMemoryReporter::UNITS_BYTES,
|
nsIMemoryReporter::UNITS_BYTES,
|
||||||
0,
|
0,
|
||||||
|
@ -523,7 +523,6 @@ MapsReporter::ParseMapBody(
|
||||||
}
|
}
|
||||||
|
|
||||||
nsCAutoString path;
|
nsCAutoString path;
|
||||||
path.Append("smaps/");
|
|
||||||
path.Append(category);
|
path.Append(category);
|
||||||
path.Append("/");
|
path.Append("/");
|
||||||
path.Append(aName);
|
path.Append(aName);
|
||||||
|
|
Загрузка…
Ссылка в новой задаче