зеркало из https://github.com/mozilla/gecko-dev.git
Bug 674290 - Expose contents of /proc/self/maps and smaps in about:memory. r=njn
--HG-- extra : rebase_source : 3bbe2f926ba3b0c46a122d51b027a5a6283ae2b0
This commit is contained in:
Родитель
73e9ae8488
Коммит
81a4fe113d
|
@ -69,3 +69,15 @@
|
|||
-moz-user-select: none; /* no need to include this when cutting+pasting */
|
||||
}
|
||||
|
||||
h2.tree {
|
||||
cursor: pointer;
|
||||
background: #ddd;
|
||||
padding-left: .1em;
|
||||
}
|
||||
|
||||
pre.tree {
|
||||
}
|
||||
|
||||
pre.collapsed {
|
||||
display: none;
|
||||
}
|
||||
|
|
|
@ -58,6 +58,55 @@ const UNITS_PERCENTAGE = Ci.nsIMemoryReporter.UNITS_PERCENTAGE;
|
|||
|
||||
const kUnknown = -1; // used for _amount if a memory reporter failed
|
||||
|
||||
const kTreeDescriptions = {
|
||||
'explicit' :
|
||||
"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.",
|
||||
|
||||
'resident':
|
||||
"This tree shows how much space in physical memory each of the " +
|
||||
"process's mappings is currently using (the mapping's 'resident set size', " +
|
||||
"or 'RSS'). This is a good measure of the 'cost' of the mapping, although " +
|
||||
"it does not take into account the fact that shared libraries may be mapped " +
|
||||
"by multiple processes but appear only once in physical memory. " +
|
||||
"Note that the 'resident' value here might not equal the value for " +
|
||||
"'resident' under 'Other Measurements' because the two measurements are not " +
|
||||
"taken at exactly the same time.",
|
||||
|
||||
'vsize':
|
||||
"This tree shows how much virtual addres space each of the process's " +
|
||||
"mappings takes up (the mapping's 'vsize'). A mapping may have a large " +
|
||||
"vsize but use only a small amount of physical memory; the resident set size " +
|
||||
"of a mapping is a better measure of the mapping's 'cost'. Note that the " +
|
||||
"'vsize' value here might not equal the value for 'vsize' under 'Other " +
|
||||
"Measurements' because the two measurements are not taken at exactly the " +
|
||||
"same time.",
|
||||
|
||||
'swap':
|
||||
"This tree shows how much space in the swap file each of the process's " +
|
||||
"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."
|
||||
};
|
||||
|
||||
const kTreeNames = {
|
||||
'explicit': 'Explicit Allocations',
|
||||
'resident': 'Resident Set Size (RSS) Breakdown',
|
||||
'vsize': 'Virtual Size Breakdown',
|
||||
'swap': 'Swap Usage Breakdown',
|
||||
'other': 'Other Measurements'
|
||||
};
|
||||
|
||||
const kMapTreePaths = ['map/resident', 'map/vsize', 'map/swap'];
|
||||
|
||||
function onLoad()
|
||||
{
|
||||
var os = Cc["@mozilla.org/observer-service;1"].
|
||||
|
@ -135,6 +184,17 @@ function sendHeapMinNotifications()
|
|||
sendHeapMinNotificationsInner();
|
||||
}
|
||||
|
||||
function toggleTreeVisibility(aEvent)
|
||||
{
|
||||
var headerElem = aEvent.target;
|
||||
|
||||
// Replace "header-" with "pre-" in the header element's id to get the id of
|
||||
// the corresponding pre element.
|
||||
var treeElem = $(headerElem.id.replace(/^header-/, 'pre-'));
|
||||
|
||||
treeElem.classList.toggle('collapsed');
|
||||
}
|
||||
|
||||
function Reporter(aPath, aKind, aUnits, aAmount, aDescription)
|
||||
{
|
||||
this._path = aPath;
|
||||
|
@ -261,7 +321,8 @@ function update()
|
|||
|
||||
text += "<div>" +
|
||||
"<span class='legend'>Hover the pointer over the name of a memory " +
|
||||
"reporter to see a detailed description of what it measures.</span>"
|
||||
"reporter to see a detailed description of what it measures. Click a " +
|
||||
"heading to expand or collapse its tree.</span>" +
|
||||
"</div>";
|
||||
|
||||
var div = document.createElement("div");
|
||||
|
@ -312,23 +373,41 @@ TreeNode.compare = function(a, b) {
|
|||
*
|
||||
* @param aReporters
|
||||
* The table of Reporters, indexed by path.
|
||||
* @param aTreeName
|
||||
* The name of the tree being built.
|
||||
* @return The built tree.
|
||||
*/
|
||||
function buildTree(aReporters)
|
||||
function buildTree(aReporters, aTreeName)
|
||||
{
|
||||
const treeName = "explicit";
|
||||
|
||||
// We want to process all reporters that begin with |treeName|. First we
|
||||
// We want to process all reporters that begin with |aTreeName|. First we
|
||||
// build the tree but only fill the properties that we can with a top-down
|
||||
// traversal.
|
||||
|
||||
// Is there any reporter which matches aTreeName? If not, we'll create a
|
||||
// dummy one.
|
||||
var foundReporter = false;
|
||||
for (var path in aReporters) {
|
||||
if (aReporters[path].treeNameMatches(aTreeName)) {
|
||||
foundReporter = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!foundReporter) {
|
||||
// We didn't find any reporters for this tree, so create an empty one. Its
|
||||
// description will be set later.
|
||||
aReporters[aTreeName] =
|
||||
new Reporter(aTreeName, KIND_NONHEAP, UNITS_BYTES, 0, '');
|
||||
}
|
||||
|
||||
var t = new TreeNode("falseRoot");
|
||||
for (var path in aReporters) {
|
||||
// Add any missing nodes in the tree implied by the path.
|
||||
var r = aReporters[path];
|
||||
if (r.treeNameMatches(treeName)) {
|
||||
if (r.treeNameMatches(aTreeName)) {
|
||||
assert(r._kind === KIND_HEAP || r._kind === KIND_NONHEAP,
|
||||
"reporters in the tree must have KIND_HEAP or KIND_NONHEAP");
|
||||
assert(r._units === UNITS_BYTES);
|
||||
assert(r._units === UNITS_BYTES, "r._units === UNITS_BYTES");
|
||||
var names = r._path.split('/');
|
||||
var u = t;
|
||||
for (var i = 0; i < names.length; i++) {
|
||||
|
@ -349,8 +428,9 @@ function buildTree(aReporters)
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Using falseRoot makes the above code simpler. Now discard it, leaving
|
||||
// treeName at the root.
|
||||
// aTreeName at the root.
|
||||
t = t._kids[0];
|
||||
|
||||
// Next, fill in the remaining properties bottom-up.
|
||||
|
@ -360,7 +440,7 @@ function buildTree(aReporters)
|
|||
var path = aPrepath ? aPrepath + '/' + aT._name : aT._name;
|
||||
if (aT._kids.length === 0) {
|
||||
// Leaf node. Must have a reporter.
|
||||
assert(aT._kind !== undefined);
|
||||
assert(aT._kind !== undefined, "aT._kind !== undefined");
|
||||
aT._description = getDescription(aReporters, path);
|
||||
var amount = getBytes(aReporters, path);
|
||||
if (amount !== kUnknown) {
|
||||
|
@ -400,11 +480,32 @@ function buildTree(aReporters)
|
|||
aT._description = "The sum of all entries below '" + aT._name + "'.";
|
||||
}
|
||||
}
|
||||
assert(aT._amount !== kUnknown);
|
||||
assert(aT._amount !== kUnknown, "aT._amount !== kUnknown");
|
||||
return aT._amount;
|
||||
}
|
||||
|
||||
fillInTree(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'.)
|
||||
var slashCount = 0;
|
||||
for (var 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 description on the root node.
|
||||
t._description = kTreeDescriptions[t._name];
|
||||
|
||||
return t;
|
||||
}
|
||||
|
||||
/**
|
||||
* Do some work which only makes sense for the 'explicit' tree.
|
||||
*/
|
||||
function fixUpExplicitTree(aT, aReporters) {
|
||||
// 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.
|
||||
|
@ -429,7 +530,7 @@ function buildTree(aReporters)
|
|||
var unknownHeapUsedBytes = 0;
|
||||
var hasProblem = true;
|
||||
if (heapUsedBytes !== kUnknown) {
|
||||
unknownHeapUsedBytes = heapUsedBytes - getKnownHeapUsedBytes(t);
|
||||
unknownHeapUsedBytes = heapUsedBytes - getKnownHeapUsedBytes(aT);
|
||||
hasProblem = false;
|
||||
}
|
||||
var heapUnclassified = new TreeNode("heap-unclassified");
|
||||
|
@ -446,10 +547,8 @@ function buildTree(aReporters)
|
|||
heapUnclassified._hasProblem = true;
|
||||
}
|
||||
|
||||
t._kids.push(heapUnclassified);
|
||||
t._amount += unknownHeapUsedBytes;
|
||||
|
||||
return t;
|
||||
aT._kids.push(heapUnclassified);
|
||||
aT._amount += unknownHeapUsedBytes;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -515,16 +614,26 @@ function filterTree(aTotalBytes, aT)
|
|||
*/
|
||||
function genProcessText(aProcess, aReporters)
|
||||
{
|
||||
var tree = buildTree(aReporters);
|
||||
filterTree(tree._amount, tree);
|
||||
var explicitTree = buildTree(aReporters, 'explicit');
|
||||
fixUpExplicitTree(explicitTree, aReporters);
|
||||
filterTree(explicitTree._amount, explicitTree);
|
||||
var explicitText = genTreeText(explicitTree, aProcess);
|
||||
|
||||
// Nb: the newlines give nice spacing if we cut+paste into a text buffer.
|
||||
var text = "";
|
||||
text += "<h1>" + aProcess + " Process</h1>\n\n";
|
||||
text += genTreeText(tree);
|
||||
text += genOtherText(aReporters);
|
||||
text += "<hr></hr>";
|
||||
return text;
|
||||
var mapTreeText = '';
|
||||
kMapTreePaths.forEach(function(t) {
|
||||
var tree = buildTree(aReporters, t);
|
||||
filterTree(tree._amount, tree);
|
||||
mapTreeText += genTreeText(tree, aProcess);
|
||||
});
|
||||
|
||||
// We have to call genOtherText after we process all the trees, because it
|
||||
// looks at all the reporters which aren't part of a tree.
|
||||
var otherText = genOtherText(aReporters, aProcess);
|
||||
|
||||
// The newlines give nice spacing if we cut+paste into a text buffer.
|
||||
return "<h1>" + aProcess + " Process</h1>\n\n" +
|
||||
explicitText + mapTreeText + otherText +
|
||||
"<hr></hr>";
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -735,12 +844,15 @@ function genMrNameText(aKind, aDesc, aName, aHasProblem, aNMerged)
|
|||
*
|
||||
* @param aT
|
||||
* The tree.
|
||||
* @param aProcess
|
||||
* The process the tree corresponds to.
|
||||
* @return The generated text.
|
||||
*/
|
||||
function genTreeText(aT)
|
||||
function genTreeText(aT, aProcess)
|
||||
{
|
||||
var treeBytes = aT._amount;
|
||||
var rootStringLength = aT.toString().length;
|
||||
var isExplicitTree = aT._name == 'explicit';
|
||||
|
||||
/**
|
||||
* Generates the text for a particular tree, without a heading.
|
||||
|
@ -808,8 +920,11 @@ function genTreeText(aT)
|
|||
}
|
||||
perc = "<span class='mrPerc'>(" + perc + "%)</span> ";
|
||||
|
||||
// We don't want to show '(nonheap)' on a tree like 'map/vsize', since the
|
||||
// whole tree is non-heap.
|
||||
var kind = isExplicitTree ? aT._kind : undefined;
|
||||
var text = indent + genMrValueText(tString) + " " + perc +
|
||||
genMrNameText(aT._kind, aT._description, aT._name,
|
||||
genMrNameText(kind, aT._description, aT._name,
|
||||
aT._hasProblem, aT._nMerged);
|
||||
|
||||
for (var i = 0; i < aT._kids.length; i++) {
|
||||
|
@ -822,22 +937,10 @@ function genTreeText(aT)
|
|||
}
|
||||
|
||||
var text = genTreeText2(aT, [], rootStringLength);
|
||||
// Nb: the newlines give nice spacing if we cut+paste into a text buffer.
|
||||
const desc =
|
||||
"This tree covers explicit memory allocations by the application, " +
|
||||
"both at the operating system level (via calls to functions such as " +
|
||||
"VirtualAlloc, vm_allocate, and mmap), and at the heap allocation level " +
|
||||
"(via functions such as malloc, calloc, realloc, memalign, operator " +
|
||||
"new, and operator new[]). It excludes memory that is mapped implicitly " +
|
||||
"such as code and data segments, and thread stacks. It also excludes " +
|
||||
"heap memory that has been freed by the application but is still being " +
|
||||
"held onto by the heap allocator. It is not guaranteed to cover every " +
|
||||
"explicit allocation, but it does cover most (including the entire " +
|
||||
"heap), and therefore it is the single best number to focus on when " +
|
||||
"trying to reduce memory usage.";
|
||||
|
||||
return "<h2 class='hasDesc' title='" + escapeQuotes(desc) +
|
||||
"'>Explicit Allocations</h2>\n" + "<pre>" + text + "</pre>\n";
|
||||
|
||||
// The explicit tree is not collapsed, but all other trees are, so pass
|
||||
// !isExplicitTree for genSectionMarkup's aCollapsed parameter.
|
||||
return genSectionMarkup(aProcess, aT._name, text, !isExplicitTree);
|
||||
}
|
||||
|
||||
function OtherReporter(aPath, aUnits, aAmount, aDescription,
|
||||
|
@ -880,9 +983,11 @@ OtherReporter.compare = function(a, b) {
|
|||
*
|
||||
* @param aReportersByProcess
|
||||
* Table of Reporters for this process, indexed by _path.
|
||||
* @param aProcess
|
||||
* The process these reporters correspond to.
|
||||
* @return The generated text.
|
||||
*/
|
||||
function genOtherText(aReportersByProcess)
|
||||
function genOtherText(aReportersByProcess, aProcess)
|
||||
{
|
||||
// Generate an array of Reporter-like elements, stripping out all the
|
||||
// Reporters that have already been handled. Also find the width of the
|
||||
|
@ -918,8 +1023,22 @@ function genOtherText(aReportersByProcess)
|
|||
// Nb: the newlines give nice spacing if we cut+paste into a text buffer.
|
||||
const desc = "This list contains other memory measurements that cross-cut " +
|
||||
"the requested memory measurements above."
|
||||
return "<h2 class='hasDesc' title='" + desc + "'>Other Measurements</h2>\n" +
|
||||
"<pre>" + text + "</pre>\n";
|
||||
|
||||
return genSectionMarkup(aProcess, 'other', text, false);
|
||||
}
|
||||
|
||||
function genSectionMarkup(aProcess, aName, aText, aCollapsed)
|
||||
{
|
||||
var headerId = 'header-' + aProcess + '-' + aName;
|
||||
var preId = 'pre-' + aProcess + '-' + aName;
|
||||
var elemClass = (aCollapsed ? 'collapsed' : '') + ' tree';
|
||||
|
||||
// Ugh.
|
||||
return '<h2 id="' + headerId + '" class="' + elemClass + '" ' +
|
||||
'onclick="toggleTreeVisibility(event)">' +
|
||||
kTreeNames[aName] +
|
||||
'</h2>\n' +
|
||||
'<pre id="' + preId + '" class="' + elemClass + '">' + aText + '</pre>\n';
|
||||
}
|
||||
|
||||
function assert(aCond, aMsg)
|
||||
|
|
|
@ -105,6 +105,17 @@
|
|||
f("perc2", OTHER, PERCENTAGE, 10000);
|
||||
f("perc1", OTHER, PERCENTAGE, 4567);
|
||||
}
|
||||
},
|
||||
{ collectReports: function(cbObj, closure) {
|
||||
// The amounts are given in pages, so multiply here by 4kb.
|
||||
function f(p, a) { cbObj.callback("", p, NONHEAP, BYTES, a * 4 * KB, "(desc)", closure); }
|
||||
f("map/vsize/a", 24);
|
||||
f("map/swap/a", 1);
|
||||
f("map/swap/a", 2);
|
||||
f("map/vsize/a", 19);
|
||||
f("map/swap/b/c", 10);
|
||||
f("map/resident/a", 42);
|
||||
}
|
||||
}
|
||||
];
|
||||
for (var i = 0; i < fakeReporters.length; i++) {
|
||||
|
@ -192,6 +203,20 @@ Explicit Allocations\n\
|
|||
├───11.00 MB (01.76%) -- heap-unclassified\n\
|
||||
└────0.58 MB (00.09%) -- (2 omitted)\n\
|
||||
\n\
|
||||
Resident Set Size (RSS) Breakdown\n\
|
||||
0.16 MB (100.0%) -- resident\n\
|
||||
└──0.16 MB (100.0%) -- a\n\
|
||||
\n\
|
||||
Virtual Size Breakdown\n\
|
||||
0.17 MB (100.0%) -- vsize\n\
|
||||
└──0.17 MB (100.0%) -- a [2]\n\
|
||||
\n\
|
||||
Swap Usage Breakdown\n\
|
||||
0.05 MB (100.0%) -- swap\n\
|
||||
├──0.04 MB (76.92%) -- b\n\
|
||||
│ └──0.04 MB (76.92%) -- c\n\
|
||||
└──0.01 MB (23.08%) -- a [2]\n\
|
||||
\n\
|
||||
Other Measurements\n\
|
||||
500.00 MB -- heap-allocated\n\
|
||||
100.00 MB -- heap-unallocated\n\
|
||||
|
@ -213,6 +238,15 @@ Explicit Allocations\n\
|
|||
├────200.00 MB (20.00%) -- compartment(this-will-be-truncated-in-non-verbose-mo...)\n\
|
||||
└────101.00 MB (10.10%) -- heap-unclassified\n\
|
||||
\n\
|
||||
Resident Set Size (RSS) Breakdown\n\
|
||||
0.00 MB (100.0%) -- resident\n\
|
||||
\n\
|
||||
Virtual Size Breakdown\n\
|
||||
0.00 MB (100.0%) -- vsize\n\
|
||||
\n\
|
||||
Swap Usage Breakdown\n\
|
||||
0.00 MB (100.0%) -- swap\n\
|
||||
\n\
|
||||
Other Measurements\n\
|
||||
666.00 MB -- danger<script>window.alert(1)</script>\n\
|
||||
1,000.00 MB -- heap-allocated\n\
|
||||
|
@ -229,6 +263,15 @@ Explicit Allocations\n\
|
|||
│ └────0.00 MB (00.00%) -- (1 omitted)\n\
|
||||
└────0.00 MB (00.00%) -- (2 omitted)\n\
|
||||
\n\
|
||||
Resident Set Size (RSS) Breakdown\n\
|
||||
0.00 MB (100.0%) -- resident\n\
|
||||
\n\
|
||||
Virtual Size Breakdown\n\
|
||||
0.00 MB (100.0%) -- vsize\n\
|
||||
\n\
|
||||
Swap Usage Breakdown\n\
|
||||
0.00 MB (100.0%) -- swap\n\
|
||||
\n\
|
||||
Other Measurements\n\
|
||||
0.00 MB -- heap-allocated [*]\n\
|
||||
0.00 MB -- other1 [*]\n\
|
||||
|
@ -264,6 +307,20 @@ Explicit Allocations\n\
|
|||
├──────510,976 B (00.08%) -- d\n\
|
||||
└──────102,400 B (00.02%) -- e\n\
|
||||
\n\
|
||||
Resident Set Size (RSS) Breakdown\n\
|
||||
172,032 B (100.0%) -- resident\n\
|
||||
└──172,032 B (100.0%) -- a\n\
|
||||
\n\
|
||||
Virtual Size Breakdown\n\
|
||||
176,128 B (100.0%) -- vsize\n\
|
||||
└──176,128 B (100.0%) -- a [2]\n\
|
||||
\n\
|
||||
Swap Usage Breakdown\n\
|
||||
53,248 B (100.0%) -- swap\n\
|
||||
├──40,960 B (76.92%) -- b\n\
|
||||
│ └──40,960 B (76.92%) -- c\n\
|
||||
└──12,288 B (23.08%) -- a [2]\n\
|
||||
\n\
|
||||
Other Measurements\n\
|
||||
524,288,000 B -- heap-allocated\n\
|
||||
104,857,600 B -- heap-unallocated\n\
|
||||
|
@ -285,6 +342,15 @@ Explicit Allocations\n\
|
|||
├────209,715,200 B (20.00%) -- compartment(this-will-be-truncated-in-non-verbose-mode-abcdefghijklmnopqrstuvwxyz)\n\
|
||||
└────105,906,176 B (10.10%) -- heap-unclassified\n\
|
||||
\n\
|
||||
Resident Set Size (RSS) Breakdown\n\
|
||||
0 B (100.0%) -- resident\n\
|
||||
\n\
|
||||
Virtual Size Breakdown\n\
|
||||
0 B (100.0%) -- vsize\n\
|
||||
\n\
|
||||
Swap Usage Breakdown\n\
|
||||
0 B (100.0%) -- swap\n\
|
||||
\n\
|
||||
Other Measurements\n\
|
||||
698,351,616 B -- danger<script>window.alert(1)</script>\n\
|
||||
1,048,576,000 B -- heap-allocated\n\
|
||||
|
@ -302,6 +368,15 @@ Explicit Allocations\n\
|
|||
├────────────0 B (00.00%) -- b [*]\n\
|
||||
└────────────0 B (00.00%) -- heap-unclassified [*]\n\
|
||||
\n\
|
||||
Resident Set Size (RSS) Breakdown\n\
|
||||
0 B (100.0%) -- resident\n\
|
||||
\n\
|
||||
Virtual Size Breakdown\n\
|
||||
0 B (100.0%) -- vsize\n\
|
||||
\n\
|
||||
Swap Usage Breakdown\n\
|
||||
0 B (100.0%) -- swap\n\
|
||||
\n\
|
||||
Other Measurements\n\
|
||||
0 B -- heap-allocated [*]\n\
|
||||
0 B -- other1 [*]\n\
|
||||
|
@ -328,22 +403,44 @@ Other Measurements\n\
|
|||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
function check(actual, expected) {
|
||||
var a = actual.QueryInterface(Ci.nsISupportsString).data;
|
||||
if (a != expected) {
|
||||
dump("*******ACTUAL*******\n");
|
||||
dump(a);
|
||||
dump("******EXPECTED******\n");
|
||||
dump(expected);
|
||||
dump("********************\n");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Cut+paste the entire page and check that the cut text matches what we
|
||||
// expect. This tests the output in general and also that the cutting and
|
||||
// pasting works as expected.
|
||||
function test(aFrame, aExpectedText, aNext) {
|
||||
document.querySelector("#" + aFrame).focus();
|
||||
SimpleTest.waitForClipboard(aExpectedText,
|
||||
function() {
|
||||
synthesizeKey("A", {accelKey: true});
|
||||
synthesizeKey("C", {accelKey: true});
|
||||
},
|
||||
aNext,
|
||||
function() {
|
||||
ok(false, "pasted text doesn't match for " + aFrame);
|
||||
finish();
|
||||
}
|
||||
);
|
||||
// Click all h2.collapsed elements so they expand.
|
||||
var win = document.querySelector("#" + aFrame).contentWindow;
|
||||
var nodes = win.document.querySelectorAll("pre.collapsed");
|
||||
for (var i = 0; i < nodes.length; i++) {
|
||||
nodes[i].classList.toggle('collapsed');
|
||||
}
|
||||
|
||||
SimpleTest.executeSoon(function() {
|
||||
document.querySelector("#" + aFrame).focus();
|
||||
SimpleTest.waitForClipboard(function(actual) { return check(actual, aExpectedText) },
|
||||
function() {
|
||||
synthesizeKey("A", {accelKey: true});
|
||||
synthesizeKey("C", {accelKey: true});
|
||||
},
|
||||
aNext,
|
||||
function() {
|
||||
ok(false, "pasted text doesn't match for " + aFrame);
|
||||
finish();
|
||||
}
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
addLoadEvent(function() {
|
||||
|
|
|
@ -72,6 +72,10 @@ CPPSRCS = \
|
|||
FunctionTimer.cpp \
|
||||
$(NULL)
|
||||
|
||||
ifeq ($(OS_ARCH),Linux)
|
||||
CPPSRCS += MapsMemoryReporter.cpp
|
||||
endif
|
||||
|
||||
ifeq (cocoa,$(MOZ_WIDGET_TOOLKIT))
|
||||
CPPSRCS += nsMacUtilsImpl.cpp
|
||||
endif
|
||||
|
@ -94,6 +98,7 @@ EXPORTS_NAMESPACES = mozilla
|
|||
|
||||
EXPORTS_mozilla = \
|
||||
FunctionTimer.h \
|
||||
MapsMemoryReporter.h \
|
||||
$(NULL)
|
||||
|
||||
ifeq (windows,$(MOZ_WIDGET_TOOLKIT))
|
||||
|
|
|
@ -0,0 +1,456 @@
|
|||
/* -*- Mode: C++; tab-width: 50; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim:set ts=2 sw=2 sts=2 ci et: */
|
||||
/* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla Public License Version
|
||||
* 1.1 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
* http://www.mozilla.org/MPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
* for the specific language governing rights and limitations under the
|
||||
* License.
|
||||
*
|
||||
* The Original Code is Mozilla.org code.
|
||||
*
|
||||
* The Initial Developer of the Original Code is the Mozilla Foundation.
|
||||
*
|
||||
* Portions created by the Initial Developer are Copyright (C) 2011
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
* Justin Lebar <justin.lebar@gmail.com>
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
* of those above. If you wish to allow use of your version of this file only
|
||||
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
* use your version of this file under the terms of the MPL, indicate your
|
||||
* decision by deleting the provisions above and replace them with the notice
|
||||
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
* the provisions above, a recipient may use your version of this file under
|
||||
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
#include "mozilla/MapsMemoryReporter.h"
|
||||
#include "nsIMemoryReporter.h"
|
||||
#include "nsString.h"
|
||||
#include "nsCOMPtr.h"
|
||||
#include "nsHashSets.h"
|
||||
#include <stdio.h>
|
||||
|
||||
namespace mozilla {
|
||||
namespace MapsMemoryReporter {
|
||||
|
||||
#if !defined(XP_LINUX)
|
||||
#error "This doesn't have a prayer of working if we're not on Linux."
|
||||
#endif
|
||||
|
||||
// mozillaLibraries is a list of all the shared libraries we build. This list
|
||||
// is used for determining whether a library is a "Mozilla library" or a
|
||||
// "third-party library". But even if this list is missing items, about:memory
|
||||
// will identify a library in the same directory as libxul.so as a "Mozilla
|
||||
// library".
|
||||
const char* mozillaLibraries[] =
|
||||
{
|
||||
"libfreebl3.so",
|
||||
"libmozalloc.so",
|
||||
"libmozsqlite3.so",
|
||||
"libnspr4.so",
|
||||
"libnss3.so",
|
||||
"libnssckbi.so",
|
||||
"libnssdbm3.so",
|
||||
"libnssutil3.so",
|
||||
"libplc4.so",
|
||||
"libplds4.so",
|
||||
"libsmime3.so",
|
||||
"libsoftokn3.so",
|
||||
"libssl3.so",
|
||||
"libxpcom.so",
|
||||
"libxul.so"
|
||||
};
|
||||
|
||||
namespace {
|
||||
|
||||
bool EndsWithLiteral(const nsCString &aHaystack, const char *aNeedle)
|
||||
{
|
||||
PRInt32 idx = aHaystack.RFind(aNeedle);
|
||||
if (idx == -1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return idx + strlen(aNeedle) == aHaystack.Length();
|
||||
}
|
||||
|
||||
void GetDirname(const nsCString &aPath, nsACString &aOut)
|
||||
{
|
||||
PRInt32 idx = aPath.RFind("/");
|
||||
if (idx == -1) {
|
||||
aOut.Truncate();
|
||||
}
|
||||
else {
|
||||
aOut.Assign(Substring(aPath, 0, idx));
|
||||
}
|
||||
}
|
||||
|
||||
void GetBasename(const nsCString &aPath, nsACString &aOut)
|
||||
{
|
||||
PRInt32 idx = aPath.RFind("/");
|
||||
if (idx == -1) {
|
||||
aOut.Assign(aPath);
|
||||
}
|
||||
else {
|
||||
aOut.Assign(Substring(aPath, idx + 1));
|
||||
}
|
||||
}
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
class MapsReporter : public nsIMemoryMultiReporter
|
||||
{
|
||||
public:
|
||||
MapsReporter();
|
||||
|
||||
NS_DECL_ISUPPORTS
|
||||
|
||||
NS_IMETHOD
|
||||
CollectReports(nsIMemoryMultiReporterCallback *aCallback,
|
||||
nsISupports *aClosure);
|
||||
|
||||
private:
|
||||
// Search through /proc/self/maps for libxul.so, and set mLibxulDir to the
|
||||
// the directory containing libxul.
|
||||
nsresult FindLibxul();
|
||||
|
||||
nsresult
|
||||
ParseMapping(FILE *aFile,
|
||||
nsIMemoryMultiReporterCallback *aCallback,
|
||||
nsISupports *aClosure);
|
||||
|
||||
void
|
||||
GetReporterNameAndDescription(const char *aPath,
|
||||
const char *aPermissions,
|
||||
nsACString &aName,
|
||||
nsACString &aDesc);
|
||||
|
||||
nsresult
|
||||
ParseMapBody(FILE *aFile,
|
||||
const nsACString &aName,
|
||||
const nsACString &aDescription,
|
||||
nsIMemoryMultiReporterCallback *aCallback,
|
||||
nsISupports *aClosure);
|
||||
|
||||
nsCString mLibxulDir;
|
||||
nsCStringHashSet mMozillaLibraries;
|
||||
};
|
||||
|
||||
NS_IMPL_THREADSAFE_ISUPPORTS1(MapsReporter, nsIMemoryMultiReporter)
|
||||
|
||||
MapsReporter::MapsReporter()
|
||||
{
|
||||
const PRUint32 len = NS_ARRAY_LENGTH(mozillaLibraries);
|
||||
mMozillaLibraries.Init(len);
|
||||
for (PRUint32 i = 0; i < len; i++) {
|
||||
nsCAutoString str;
|
||||
str.Assign(mozillaLibraries[i]);
|
||||
mMozillaLibraries.Put(str);
|
||||
}
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
MapsReporter::CollectReports(nsIMemoryMultiReporterCallback *aCallback,
|
||||
nsISupports *aClosure)
|
||||
{
|
||||
FILE *f = fopen("/proc/self/smaps", "r");
|
||||
if (!f)
|
||||
return NS_ERROR_FAILURE;
|
||||
|
||||
while (true) {
|
||||
nsresult rv = ParseMapping(f, aCallback, aClosure);
|
||||
if (NS_FAILED(rv))
|
||||
break;
|
||||
}
|
||||
|
||||
fclose(f);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
MapsReporter::FindLibxul()
|
||||
{
|
||||
mLibxulDir.Truncate();
|
||||
|
||||
// Note that we're scanning /proc/self/*maps*, not smaps, here.
|
||||
FILE *f = fopen("/proc/self/maps", "r");
|
||||
if (!f)
|
||||
return NS_ERROR_FAILURE;
|
||||
|
||||
while (true) {
|
||||
// Skip any number of non-slash characters, then capture starting with the
|
||||
// slash to the newline. This is the path part of /proc/self/maps.
|
||||
char path[1025];
|
||||
int numRead = fscanf(f, "%*[^/]%1024[^\n]", path);
|
||||
if (numRead != 1) {
|
||||
break;
|
||||
}
|
||||
|
||||
nsCAutoString pathStr;
|
||||
pathStr.Append(path);
|
||||
|
||||
nsCAutoString basename;
|
||||
GetBasename(pathStr, basename);
|
||||
|
||||
if (basename.EqualsLiteral("libxul.so")) {
|
||||
GetDirname(pathStr, mLibxulDir);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
fclose(f);
|
||||
return mLibxulDir.IsEmpty() ? NS_ERROR_FAILURE : NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
MapsReporter::ParseMapping(
|
||||
FILE *aFile,
|
||||
nsIMemoryMultiReporterCallback *aCallback,
|
||||
nsISupports *aClosure)
|
||||
{
|
||||
// We need to use native types in order to get good warnings from fscanf, so
|
||||
// let's make sure that the native types have the sizes we expect.
|
||||
PR_STATIC_ASSERT(sizeof(long long) == sizeof(PRInt64));
|
||||
PR_STATIC_ASSERT(sizeof(int) == sizeof(PRInt32));
|
||||
|
||||
if (mLibxulDir.IsEmpty()) {
|
||||
NS_ENSURE_SUCCESS(FindLibxul(), NS_ERROR_FAILURE);
|
||||
}
|
||||
|
||||
// The first line of an entry in /proc/self/smaps looks just like an entry
|
||||
// in /proc/maps:
|
||||
//
|
||||
// address perms offset dev inode pathname
|
||||
// 02366000-025d8000 rw-p 00000000 00:00 0 [heap]
|
||||
|
||||
const int argCount = 8;
|
||||
|
||||
unsigned long long addrStart, addrEnd;
|
||||
char perms[5];
|
||||
unsigned long long offset;
|
||||
unsigned int devMajor, devMinor, inode;
|
||||
char path[1025];
|
||||
|
||||
// A path might not be present on this line; set it to the empty string.
|
||||
path[0] = '\0';
|
||||
|
||||
// This is a bit tricky. Whitespace in a scanf pattern matches *any*
|
||||
// whitespace, including newlines. We want this pattern to match a line
|
||||
// with or without a path, but we don't want to look to a new line for the
|
||||
// path. Thus we have %u%1024[^\n] at the end of the pattern. This will
|
||||
// capture into the path some leading whitespace, which we'll later trim off.
|
||||
int numRead = fscanf(aFile, "%llx-%llx %4s %llx %u:%u %u%1024[^\n]",
|
||||
&addrStart, &addrEnd, perms, &offset, &devMajor,
|
||||
&devMinor, &inode, path);
|
||||
|
||||
// Eat up any whitespace at the end of this line, including the newline.
|
||||
fscanf(aFile, " ");
|
||||
|
||||
// We might or might not have a path, but the rest of the arguments should be
|
||||
// there.
|
||||
if (numRead != argCount && numRead != argCount - 1)
|
||||
return NS_ERROR_FAILURE;
|
||||
|
||||
nsCAutoString name, description;
|
||||
GetReporterNameAndDescription(path, perms, name, description);
|
||||
|
||||
while (true) {
|
||||
nsresult rv = ParseMapBody(aFile, name, description, aCallback, aClosure);
|
||||
if (NS_FAILED(rv))
|
||||
break;
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void
|
||||
MapsReporter::GetReporterNameAndDescription(
|
||||
const char *aPath,
|
||||
const char *aPerms,
|
||||
nsACString &aName,
|
||||
nsACString &aDesc)
|
||||
{
|
||||
aName.Truncate();
|
||||
aDesc.Truncate();
|
||||
|
||||
// If aPath points to a file, we have its absolute path, plus some
|
||||
// whitespace. Truncate this to its basename, and put the absolute path in
|
||||
// the description.
|
||||
nsCAutoString absPath;
|
||||
absPath.Append(aPath);
|
||||
absPath.StripChars(" ");
|
||||
|
||||
nsCAutoString basename;
|
||||
GetBasename(absPath, basename);
|
||||
|
||||
if (basename.EqualsLiteral("[heap]")) {
|
||||
aName.Append("anonymous/anonymous, within brk()");
|
||||
aDesc.Append("Memory in anonymous mappings within the boundaries "
|
||||
"defined by brk() / sbrk(). This is likely to be just "
|
||||
"a portion of the application's heap; the remainder "
|
||||
"lives in other anonymous mappings. This node corresponds to "
|
||||
"'[heap]' in /proc/self/smaps.");
|
||||
}
|
||||
else if (basename.EqualsLiteral("[stack]")) {
|
||||
aName.Append("main thread's stack");
|
||||
aDesc.Append("The stack size of the process's main thread. This node "
|
||||
"corresponds to '[stack]' in /proc/self/smaps.");
|
||||
}
|
||||
else if (basename.EqualsLiteral("[vdso]")) {
|
||||
aName.Append("vdso");
|
||||
aDesc.Append("The virtual dynamically-linked shared object, also known as "
|
||||
"the 'vsyscall page'. This is a memory region mapped by the "
|
||||
"operating system for the purpose of allowing processes to "
|
||||
"perform some privileged actions without the overhead of a "
|
||||
"syscall.");
|
||||
}
|
||||
else if (!basename.IsEmpty()) {
|
||||
NS_ASSERTION(!mLibxulDir.IsEmpty(), "mLibxulDir should not be empty.");
|
||||
|
||||
nsCAutoString dirname;
|
||||
GetDirname(absPath, dirname);
|
||||
|
||||
// Hack: A file is a shared library if the basename contains ".so" and its
|
||||
// dirname contains "/lib", or if the basename ends with ".so".
|
||||
if (EndsWithLiteral(basename, ".so") ||
|
||||
(basename.Find(".so") != -1 && dirname.Find("/lib") != -1)) {
|
||||
aName.Append("shared-libraries/");
|
||||
if (dirname.Equals(mLibxulDir) || mMozillaLibraries.Contains(basename)) {
|
||||
aName.Append("shared-libraries-mozilla/");
|
||||
}
|
||||
else {
|
||||
aName.Append("shared-libraries-other/");
|
||||
}
|
||||
}
|
||||
else {
|
||||
aName.Append("other-files/");
|
||||
if (EndsWithLiteral(basename, ".xpi")) {
|
||||
aName.Append("extensions/");
|
||||
}
|
||||
else if (dirname.Find("/fontconfig") != -1) {
|
||||
aName.Append("fontconfig/");
|
||||
}
|
||||
}
|
||||
|
||||
aName.Append(basename);
|
||||
aDesc.Append(absPath);
|
||||
}
|
||||
else {
|
||||
aName.Append("anonymous/anonymous, outside brk()");
|
||||
aDesc.Append("Memory in anonymous mappings outside the boundaries defined "
|
||||
"by brk() / sbrk().");
|
||||
}
|
||||
|
||||
aName.Append(" [");
|
||||
aName.Append(aPerms);
|
||||
aName.Append("]");
|
||||
|
||||
// Modify the description to include an explanation of the permissions.
|
||||
aDesc.Append(" (");
|
||||
if (strstr(aPerms, "rw")) {
|
||||
aDesc.Append("read/write, ");
|
||||
}
|
||||
else if (strchr(aPerms, 'r')) {
|
||||
aDesc.Append("read-only, ");
|
||||
}
|
||||
else if (strchr(aPerms, 'w')) {
|
||||
aDesc.Append("write-only, ");
|
||||
}
|
||||
else {
|
||||
aDesc.Append("not readable, not writable, ");
|
||||
}
|
||||
|
||||
if (strchr(aPerms, 'x')) {
|
||||
aDesc.Append("executable, ");
|
||||
}
|
||||
else {
|
||||
aDesc.Append("not executable, ");
|
||||
}
|
||||
|
||||
if (strchr(aPerms, 's')) {
|
||||
aDesc.Append("shared");
|
||||
}
|
||||
else if (strchr(aPerms, 'p')) {
|
||||
aDesc.Append("private");
|
||||
}
|
||||
else {
|
||||
aDesc.Append("not shared or private??");
|
||||
}
|
||||
aDesc.Append(")");
|
||||
}
|
||||
|
||||
nsresult
|
||||
MapsReporter::ParseMapBody(
|
||||
FILE *aFile,
|
||||
const nsACString &aName,
|
||||
const nsACString &aDescription,
|
||||
nsIMemoryMultiReporterCallback *aCallback,
|
||||
nsISupports *aClosure)
|
||||
{
|
||||
PR_STATIC_ASSERT(sizeof(long long) == sizeof(PRInt64));
|
||||
|
||||
const int argCount = 2;
|
||||
|
||||
char desc[1025];
|
||||
unsigned long long size;
|
||||
if (fscanf(aFile, "%1024[a-zA-Z_]: %llu kB\n",
|
||||
desc, &size) != argCount) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
// Don't report nodes with size 0.
|
||||
if (size == 0)
|
||||
return NS_OK;
|
||||
|
||||
const char* category;
|
||||
if (strcmp(desc, "Size") == 0) {
|
||||
category = "vsize";
|
||||
}
|
||||
else if (strcmp(desc, "Rss") == 0) {
|
||||
category = "resident";
|
||||
}
|
||||
else if (strcmp(desc, "Swap") == 0) {
|
||||
category = "swap";
|
||||
}
|
||||
else {
|
||||
// Don't report this category.
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsCAutoString path;
|
||||
path.Append("map/");
|
||||
path.Append(category);
|
||||
path.Append("/");
|
||||
path.Append(aName);
|
||||
|
||||
aCallback->Callback(NS_LITERAL_CSTRING(""),
|
||||
path,
|
||||
nsIMemoryReporter::KIND_NONHEAP,
|
||||
nsIMemoryReporter::UNITS_BYTES,
|
||||
PRInt64(size) * 1024, // convert from kB to bytes
|
||||
aDescription, aClosure);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void Init()
|
||||
{
|
||||
nsCOMPtr<nsIMemoryMultiReporter> reporter = new MapsReporter();
|
||||
NS_RegisterMemoryMultiReporter(reporter);
|
||||
}
|
||||
|
||||
} // namespace MapsMemoryReporter
|
||||
} // namespace mozilla
|
|
@ -0,0 +1,58 @@
|
|||
/* -*- Mode: C++; tab-width: 50; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim:set ts=2 sw=2 sts=2 ci et: */
|
||||
/* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla Public License Version
|
||||
* 1.1 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
* http://www.mozilla.org/MPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
* for the specific language governing rights and limitations under the
|
||||
* License.
|
||||
*
|
||||
* The Original Code is Mozilla.org code.
|
||||
*
|
||||
* The Initial Developer of the Original Code is the Mozilla Foundation.
|
||||
*
|
||||
* Portions created by the Initial Developer are Copyright (C) 2011
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
* Justin Lebar <justin.lebar@gmail.com>
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
* of those above. If you wish to allow use of your version of this file only
|
||||
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
* use your version of this file under the terms of the MPL, indicate your
|
||||
* decision by deleting the provisions above and replace them with the notice
|
||||
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
* the provisions above, a recipient may use your version of this file under
|
||||
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
#ifndef mozilla_MapsMemoryReporter_h_
|
||||
#define mozilla_MapsMemoryReporter_h_
|
||||
|
||||
namespace mozilla {
|
||||
namespace MapsMemoryReporter {
|
||||
|
||||
// This only works on Linux, but to make callers' lives easier, we stub out
|
||||
// empty functions on other platforms.
|
||||
|
||||
#if defined(XP_LINUX)
|
||||
void Init();
|
||||
#else
|
||||
void Init() {}
|
||||
#endif
|
||||
|
||||
} // namespace MapsMemoryReporter
|
||||
} // namespace mozilla
|
||||
|
||||
#endif
|
|
@ -64,7 +64,7 @@ interface nsIMemoryReporter : nsISupports
|
|||
|
||||
/*
|
||||
* The path that this memory usage should be reported under. Paths are
|
||||
* '/'-delimited, eg. "a/b/c". There are two categories of paths.
|
||||
* '/'-delimited, eg. "a/b/c". There are three categories of paths.
|
||||
*
|
||||
* - Paths starting with "explicit" represent regions of memory that have
|
||||
* been explicitly allocated with an OS-level allocation (eg.
|
||||
|
@ -91,6 +91,14 @@ interface nsIMemoryReporter : nsISupports
|
|||
* So in the example above, |a| may not count any allocations counted by
|
||||
* |d|, and vice versa.
|
||||
*
|
||||
* - Paths starting with "map" represent regions of virtual memory that the
|
||||
* process has mapped. The reporter immediately beneath "map" describes
|
||||
* the type of measurement; for instance, the reporter "map/rss/[stack]"
|
||||
* might report how much of the process's stack is currently in physical
|
||||
* memory.
|
||||
*
|
||||
* Reporters in this category must have kind NONHEAP and units BYTES.
|
||||
*
|
||||
* - All other paths represent cross-cutting values and may overlap with any
|
||||
* other reporter.
|
||||
*/
|
||||
|
@ -108,11 +116,11 @@ interface nsIMemoryReporter : nsISupports
|
|||
* live on the heap. Such memory is commonly allocated by calling one of
|
||||
* the OS's memory-mapping functions (e.g. mmap, VirtualAlloc, or
|
||||
* vm_allocate). Reporters in this category must have units UNITS_BYTES
|
||||
* and must have a path starting with "explicit".
|
||||
* and must have a path starting with "explicit" or "map".
|
||||
*
|
||||
* - OTHER: reporters which don't fit into either of these categories. Such
|
||||
* reporters must have a path that does not start with "explicit" and may
|
||||
* have any units.
|
||||
* reporters must have a path that does not start with "explicit" or "map"
|
||||
* and may have any units.
|
||||
*/
|
||||
const PRInt32 KIND_NONHEAP = 0;
|
||||
const PRInt32 KIND_HEAP = 1;
|
||||
|
|
|
@ -575,7 +575,10 @@ public:
|
|||
const nsACString &aDescription,
|
||||
nsISupports *aWrappedMRs)
|
||||
{
|
||||
if (aKind == nsIMemoryReporter::KIND_NONHEAP && aAmount != PRInt64(-1)) {
|
||||
if (aKind == nsIMemoryReporter::KIND_NONHEAP &&
|
||||
PromiseFlatCString(aPath).Find("explicit") == 0 &&
|
||||
aAmount != PRInt64(-1)) {
|
||||
|
||||
MemoryReportsWrapper *wrappedMRs =
|
||||
static_cast<MemoryReportsWrapper *>(aWrappedMRs);
|
||||
MemoryReport mr(aPath, aAmount);
|
||||
|
@ -608,7 +611,7 @@ nsMemoryReporterManager::GetExplicit(PRInt64 *aExplicit)
|
|||
PRInt64 heapUsed = PRInt64(-1);
|
||||
|
||||
// Get "heap-allocated" and all the KIND_NONHEAP measurements from vanilla
|
||||
// reporters.
|
||||
// "explicit" reporters.
|
||||
nsCOMPtr<nsISimpleEnumerator> e;
|
||||
EnumerateReporters(getter_AddRefs(e));
|
||||
|
||||
|
@ -621,10 +624,14 @@ nsMemoryReporterManager::GetExplicit(PRInt64 *aExplicit)
|
|||
nsresult rv = r->GetKind(&kind);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
if (kind == nsIMemoryReporter::KIND_NONHEAP) {
|
||||
nsCString path;
|
||||
rv = r->GetPath(path);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
nsCString path;
|
||||
rv = r->GetPath(path);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
// We're only interested in NONHEAP explicit reporters and
|
||||
// the 'heap-allocated' reporter.
|
||||
if (kind == nsIMemoryReporter::KIND_NONHEAP &&
|
||||
path.Find("explicit") == 0) {
|
||||
|
||||
PRInt64 amount;
|
||||
rv = r->GetAmount(&amount);
|
||||
|
@ -636,20 +643,14 @@ nsMemoryReporterManager::GetExplicit(PRInt64 *aExplicit)
|
|||
MemoryReport mr(path, amount);
|
||||
nonheap.AppendElement(mr);
|
||||
}
|
||||
} else {
|
||||
nsCString path;
|
||||
rv = r->GetPath(path);
|
||||
} else if (path.Equals("heap-allocated")) {
|
||||
rv = r->GetAmount(&heapUsed);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
if (path.Equals("heap-allocated")) {
|
||||
rv = r->GetAmount(&heapUsed);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
// If "heap-allocated" fails, we give up, because the result
|
||||
// would be horribly inaccurate.
|
||||
if (heapUsed == PRInt64(-1)) {
|
||||
*aExplicit = PRInt64(-1);
|
||||
return NS_OK;
|
||||
}
|
||||
// If "heap-allocated" fails, we give up, because the result
|
||||
// would be horribly inaccurate.
|
||||
if (heapUsed == PRInt64(-1)) {
|
||||
*aExplicit = PRInt64(-1);
|
||||
return NS_OK;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -659,6 +660,8 @@ nsMemoryReporterManager::GetExplicit(PRInt64 *aExplicit)
|
|||
EnumerateMultiReporters(getter_AddRefs(e2));
|
||||
nsRefPtr<MemoryReportsWrapper> wrappedMRs =
|
||||
new MemoryReportsWrapper(&nonheap);
|
||||
|
||||
// This callback adds only NONHEAP explicit reporters.
|
||||
nsRefPtr<MemoryReportCallback> cb = new MemoryReportCallback();
|
||||
|
||||
while (NS_SUCCEEDED(e2->HasMoreElements(&more)) && more) {
|
||||
|
@ -669,8 +672,8 @@ nsMemoryReporterManager::GetExplicit(PRInt64 *aExplicit)
|
|||
|
||||
// Ignore (by zeroing its amount) any reporter that is a child of another
|
||||
// reporter. Eg. if we have "explicit/a" and "explicit/a/b", zero the
|
||||
// latter. This is quadratic in the number of NONHEAP reporters, but there
|
||||
// shouldn't be many.
|
||||
// latter. This is quadratic in the number of explicit NONHEAP reporters,
|
||||
// but there shouldn't be many.
|
||||
for (PRUint32 i = 0; i < nonheap.Length(); i++) {
|
||||
const nsCString &iPath = nonheap[i].path;
|
||||
for (PRUint32 j = i + 1; j < nonheap.Length(); j++) {
|
||||
|
|
|
@ -55,6 +55,10 @@ EXPORT_LIBRARY = 1
|
|||
GRE_MODULE = 1
|
||||
MOZILLA_INTERNAL_API = 1
|
||||
|
||||
ifeq ($(OS_ARCH),Linux)
|
||||
DEFINES += -DXP_LINUX
|
||||
endif
|
||||
|
||||
CPPSRCS = \
|
||||
$(XPCOM_GLUE_SRC_LCPPSRCS) \
|
||||
$(XPCOM_GLUENS_SRC_LCPPSRCS) \
|
||||
|
|
|
@ -150,6 +150,7 @@ extern nsresult nsStringInputStreamConstructor(nsISupports *, REFNSIID, void **)
|
|||
#include "base/message_loop.h"
|
||||
|
||||
#include "mozilla/ipc/BrowserProcessSubThread.h"
|
||||
#include "mozilla/MapsMemoryReporter.h"
|
||||
|
||||
using base::AtExitManager;
|
||||
using mozilla::ipc::BrowserProcessSubThread;
|
||||
|
@ -527,6 +528,8 @@ NS_InitXPCOM2(nsIServiceManager* *result,
|
|||
ScheduleMediaCacheRemover();
|
||||
#endif
|
||||
|
||||
mozilla::MapsMemoryReporter::Init();
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче