diff options
Diffstat (limited to 'toolkit/components/aboutmemory/tests')
20 files changed, 2862 insertions, 0 deletions
diff --git a/toolkit/components/aboutmemory/tests/.eslintrc.js b/toolkit/components/aboutmemory/tests/.eslintrc.js new file mode 100644 index 000000000..2c669d844 --- /dev/null +++ b/toolkit/components/aboutmemory/tests/.eslintrc.js @@ -0,0 +1,7 @@ +"use strict"; + +module.exports = { + "extends": [ + "../../../../testing/mochitest/chrome.eslintrc.js" + ] +}; diff --git a/toolkit/components/aboutmemory/tests/chrome.ini b/toolkit/components/aboutmemory/tests/chrome.ini new file mode 100644 index 000000000..c25bc30a0 --- /dev/null +++ b/toolkit/components/aboutmemory/tests/chrome.ini @@ -0,0 +1,28 @@ +[DEFAULT] +skip-if = os == 'android' +support-files = + crash-dump-diff1.json + crash-dump-diff2.json + crash-dump-good.json + memory-reports-bad.json + memory-reports-diff1.json + memory-reports-diff2.json + memory-reports-good.json + remote.xul + +[test_aboutmemory.xul] +subsuite = clipboard +[test_aboutmemory2.xul] +subsuite = clipboard +[test_aboutmemory3.xul] +subsuite = clipboard +[test_aboutmemory4.xul] +subsuite = clipboard +[test_aboutmemory5.xul] +subsuite = clipboard +skip-if = asan # Bug 1116230 +[test_aboutmemory6.xul] +[test_memoryReporters.xul] +[test_memoryReporters2.xul] +[test_sqliteMultiReporter.xul] +[test_dumpGCAndCCLogsToFile.xul] diff --git a/toolkit/components/aboutmemory/tests/crash-dump-diff1.json b/toolkit/components/aboutmemory/tests/crash-dump-diff1.json new file mode 100644 index 000000000..d41bbcc61 --- /dev/null +++ b/toolkit/components/aboutmemory/tests/crash-dump-diff1.json @@ -0,0 +1,11 @@ +{ + "foo": 1, + "blah": 2, + "memory_report": { + "version": 1, + "hasMozMallocUsableSize": true, + "reports": [ + {"process": "Main Process (pid NNN)", "path": "heap-allocated", "kind": 2, "units": 0, "amount": 262144000, "description": "Heap allocated."} + ] + } +} diff --git a/toolkit/components/aboutmemory/tests/crash-dump-diff2.json b/toolkit/components/aboutmemory/tests/crash-dump-diff2.json new file mode 100644 index 000000000..8f9451f62 --- /dev/null +++ b/toolkit/components/aboutmemory/tests/crash-dump-diff2.json @@ -0,0 +1,11 @@ +{ + "foo": 3, + "blah": 4, + "memory_report": { + "version": 1, + "hasMozMallocUsableSize": true, + "reports": [ + {"process": "Main Process (pid NNN)", "path": "heap-allocated", "kind": 2, "units": 0, "amount": 262144001, "description": "Heap allocated."} + ] + } +} diff --git a/toolkit/components/aboutmemory/tests/crash-dump-good.json b/toolkit/components/aboutmemory/tests/crash-dump-good.json new file mode 100644 index 000000000..6bee54d59 --- /dev/null +++ b/toolkit/components/aboutmemory/tests/crash-dump-good.json @@ -0,0 +1,14 @@ +{ + "foo": 1, + "blah": 2, + "memory_report": { + "version": 1, + "hasMozMallocUsableSize": true, + "reports": [ + {"process": "Main Process (pid NNN)", "path": "heap-allocated", "kind": 2, "units": 0, "amount": 262144000, "description": "Heap allocated."}, + {"process": "Main Process (pid NNN)", "path": "other/b", "kind": 2, "units": 0, "amount": 104857, "description": "Other b."}, + {"process": "Main Process (pid NNN)", "path": "other/a", "kind": 2, "units": 0, "amount": 209715, "description": "Other a."}, + {"process": "Main Process (pid NNN)", "path": "explicit/a/b", "kind": 1, "units": 0, "amount": 52428800, "description": "A b."} + ] + } +} diff --git a/toolkit/components/aboutmemory/tests/memory-reports-bad.json b/toolkit/components/aboutmemory/tests/memory-reports-bad.json new file mode 100644 index 000000000..61a2092b1 --- /dev/null +++ b/toolkit/components/aboutmemory/tests/memory-reports-bad.json @@ -0,0 +1,3 @@ +{ + "version": 1 +} diff --git a/toolkit/components/aboutmemory/tests/memory-reports-diff1.json b/toolkit/components/aboutmemory/tests/memory-reports-diff1.json new file mode 100644 index 000000000..0bfe0b26b --- /dev/null +++ b/toolkit/components/aboutmemory/tests/memory-reports-diff1.json @@ -0,0 +1,45 @@ +{ + "version": 1, + "hasMozMallocUsableSize": true, + "reports": [ + {"process": "P", "path": "explicit/xpcom/category-manager", "kind": 1, "units": 0, "amount": 56848, "description": "Desc."}, + {"process": "P", "path": "explicit/storage/prefixset/goog-phish-shavar", "kind": 1, "units": 0, "amount": 680000, "description": "Desc."}, + + {"process": "P", "path": "explicit/spell-check", "kind": 1, "units": 0, "amount": 4, "description": "Desc."}, + {"process": "P", "path": "explicit/spell-check", "kind": 1, "units": 0, "amount": 5, "description": "Desc."}, + + {"process": "P", "path": "page-faults-soft", "kind": 2, "units": 2, "amount": 61013, "description": "Desc."}, + + {"process": "P", "path": "foobar", "kind": 2, "units": 0, "amount": 100, "description": "Desc."}, + {"process": "P", "path": "zero1", "kind": 2, "units": 0, "amount": 0, "description": "Desc."}, + + {"process": "P", "path": "a/b", "kind": 2, "units": 0, "amount": 1000000, "description": "Desc."}, + {"process": "P", "path": "a/c/d", "kind": 2, "units": 0, "amount": 2000000, "description": "Desc."}, + {"process": "P", "path": "a/c/e", "kind": 2, "units": 0, "amount": 2000000, "description": "Desc."}, + {"process": "P", "path": "a/c/f", "kind": 2, "units": 0, "amount": 3000000, "description": "Desc."}, + {"process": "P", "path": "a/c/g", "kind": 2, "units": 0, "amount": 3000000, "description": "Desc."}, + {"process": "P", "path": "a/h", "kind": 2, "units": 0, "amount": 1000, "description": "Desc."}, + + {"process": "P2 (pid 22)", "path": "p1 (pid 123)", "kind": 2, "units": 0, "amount": 33, "description": "Desc."}, + {"process": "P2 (pid 22)", "path": "p2 (blah, pid=123)", "kind": 2, "units": 0, "amount": 33, "description": "Desc."}, + {"process": "P2 (pid 22)", "path": "p3/zone(0x1234)/p3", "kind": 2, "units": 0, "amount": 33, "description": "Desc."}, + {"process": "P2 (pid 22)", "path": "p4/js-zone(0x1234)/p4", "kind": 2, "units": 0, "amount": 33, "description": "Desc."}, + {"process": "P2 (pid 22)", "path": "p5/worker(foo.com, 0x1234)/p5", "kind": 2, "units": 0, "amount": 33, "description": "Desc."}, + {"process": "P2 (pid 22)", "path": "explicit/window-objects/top(bar.com, id=123)/...", "kind": 0, "units": 0, "amount": 33, "description": "Desc."}, + {"process": "P2 (pid 22)", "path": "p6/z-moz-nullprincipal:{85e250f3-57ae-46c4-a11e-4176dd39d9c5}/p6", "kind": 2, "units": 0, "amount": 33, "description": "Desc."}, + {"process": "P2 (pid 22)", "path": "p7/js-main-runtime-compartments/system/jar:file:\\\\\\temp_xyz\\firefox\\omni.ja!/p7", "kind": 2, "units": 0, "amount": 33, "description": "Desc."}, + + {"process": "P3", "path": "p3", "kind": 2, "units": 0, "amount": 55, "description": "Desc."}, + + {"process": "P5", "path": "p5", "kind": 2, "units": 0, "amount": 0, "description": "Desc."}, + + {"process": "P7", "path": "p7", "kind": 2, "units": 0, "amount": 5, "description": "Desc."}, + + {"process": "P8", "path": "p8/a/b/c/d", "kind": 2, "units": 0, "amount": 3, "description": "Desc."}, + {"process": "P8", "path": "p8/a/b/c/e", "kind": 2, "units": 0, "amount": 4, "description": "Desc."}, + {"process": "P8", "path": "p8/a/b/f", "kind": 2, "units": 0, "amount": 5, "description": "Desc."}, + {"process": "P8", "path": "p8/a/g/h", "kind": 2, "units": 0, "amount": 6, "description": "Desc."}, + {"process": "P8", "path": "p8/a/g/i", "kind": 2, "units": 0, "amount": 7, "description": "Desc."} + ] +} + diff --git a/toolkit/components/aboutmemory/tests/memory-reports-diff2.json b/toolkit/components/aboutmemory/tests/memory-reports-diff2.json new file mode 100644 index 000000000..e2ef4caa7 --- /dev/null +++ b/toolkit/components/aboutmemory/tests/memory-reports-diff2.json @@ -0,0 +1,44 @@ +{ + "version": 1, + "hasMozMallocUsableSize": true, + "reports": [ + {"process": "P", "path": "explicit/xpcom/category-manager", "kind": 1, "units": 0, "amount": 56849, "description": "Desc."}, + {"process": "P", "path": "explicit/storage/prefixset/goog-phish-shavar", "kind": 1, "units": 0, "amount": 670000, "description": "Desc."}, + + {"process": "P", "path": "explicit/spell-check", "kind": 1, "units": 0, "amount": 3, "description": "Desc."}, + + {"process": "P", "path": "page-faults-soft", "kind": 2, "units": 2, "amount": 61013, "description": "Desc."}, + + {"process": "P", "path": "canvas-2d-pixel-bytes", "kind": 2, "units": 0, "amount": 1000, "description": "Desc."}, + {"process": "P", "path": "canvas-2d-pixel-bytes", "kind": 2, "units": 0, "amount": 2000, "description": "Desc."}, + + {"process": "P", "path": "foobaz", "kind": 2, "units": 0, "amount": 0, "description": "Desc."}, + + {"process": "P", "path": "a/b", "kind": 2, "units": 0, "amount": 2000000, "description": "Desc."}, + {"process": "P", "path": "a/c/d", "kind": 2, "units": 0, "amount": 2998000, "description": "Desc."}, + {"process": "P", "path": "a/c/e", "kind": 2, "units": 0, "amount": 1001000, "description": "Desc."}, + {"process": "P", "path": "a/c/f", "kind": 2, "units": 0, "amount": 3001000, "description": "Desc."}, + {"process": "P", "path": "a/c/g", "kind": 2, "units": 0, "amount": 3001000, "description": "Desc."}, + {"process": "P", "path": "a/h", "kind": 2, "units": 0, "amount": 2000, "description": "Desc."}, + + {"process": "P2 (pid 22)", "path": "p1 (pid 456)", "kind": 2, "units": 0, "amount": 44, "description": "Desc."}, + {"process": "P2 (pid 22)", "path": "p2 (blah, pid=456)", "kind": 2, "units": 0, "amount": 44, "description": "Desc."}, + {"process": "P2 (pid 22)", "path": "p3/zone(0x5678)/p3", "kind": 2, "units": 0, "amount": 44, "description": "Desc."}, + {"process": "P2 (pid 22)", "path": "p4/js-zone(0x5678)/p4", "kind": 2, "units": 0, "amount": 44, "description": "Desc."}, + {"process": "P2 (pid 22)", "path": "p5/worker(foo.com, 0x5678)/p5", "kind": 2, "units": 0, "amount": 44, "description": "Desc."}, + {"process": "P2 (pid 22)", "path": "explicit/window-objects/top(bar.com, id=456)/...", "kind": 0, "units": 0, "amount": 44, "description": "Desc."}, + {"process": "P2 (pid 22)", "path": "p6/z-moz-nullprincipal:{161effaa-c1f7-4010-a08e-e7c9aea01aed}/p6", "kind": 2, "units": 0, "amount": 44, "description": "Desc."}, + {"process": "P2 (pid 22)", "path": "p7/js-main-runtime-compartments/system/jar:file:\\\\\\temp_abc\\firefox\\omni.ja!/p7", "kind": 2, "units": 0, "amount": 44, "description": "Desc."}, + + {"process": "P4", "path": "p4", "kind": 2, "units": 0, "amount": 66, "description": "Desc."}, + + {"process": "P6", "path": "p6", "kind": 2, "units": 0, "amount": 0, "description": "Desc."}, + + {"process": "P7", "path": "p7/b", "kind": 2, "units": 0, "amount": 3, "description": "Desc."}, + {"process": "P7", "path": "p7/c", "kind": 2, "units": 0, "amount": 4, "description": "Desc."}, + + {"process": "P8", "path": "p8/a/b", "kind": 2, "units": 0, "amount": 1, "description": "Desc."}, + {"process": "P8", "path": "p8/a/g", "kind": 2, "units": 0, "amount": 2, "description": "Desc."} + ] +} + diff --git a/toolkit/components/aboutmemory/tests/memory-reports-good.json b/toolkit/components/aboutmemory/tests/memory-reports-good.json new file mode 100644 index 000000000..013b4b125 --- /dev/null +++ b/toolkit/components/aboutmemory/tests/memory-reports-good.json @@ -0,0 +1,29 @@ +{ + "version": 1, + "hasMozMallocUsableSize": true, + "reports": [ + {"process": "Main Process (pid NNN)", "path": "heap-allocated", "kind": 2, "units": 0, "amount": 262144000, "description": "Heap allocated."}, + {"process": "Main Process (pid NNN)", "path": "other/b", "kind": 2, "units": 0, "amount": 104857, "description": "Other b."}, + {"process": "Main Process (pid NNN)", "path": "other/a", "kind": 2, "units": 0, "amount": 209715, "description": "Other a."}, + {"process": "Main Process (pid NNN)", "path": "explicit/a/b", "kind": 1, "units": 0, "amount": 52428800, "description": "A b."}, + + {"process": "Main Process (pid NNN)", "path": "size/a", "kind": 1, "units": 0, "amount": 1024, "description": "non-sentence"}, + {"process": "Main Process (pid NNN)", "path": "rss/a", "kind": 1, "units": 0, "amount": 1024, "description": "non-sentence"}, + {"process": "Main Process (pid NNN)", "path": "pss/a", "kind": 1, "units": 0, "amount": 1024, "description": "non-sentence"}, + {"process": "Main Process (pid NNN)", "path": "swap/a", "kind": 1, "units": 0, "amount": 1024, "description": "non-sentence"}, + {"process": "Main Process (pid NNN)", "path": "compartments/system/a", "kind": 1, "units": 0, "amount": 1024, "description": ""}, + {"process": "Main Process (pid NNN)", "path": "ghost-windows/a", "kind": 1, "units": 0, "amount": 1024, "description": ""}, + + {"process": "Main Process (pid NNN)", "path": "redundant/should-be-ignored", "kind": 1, "units": 0, "amount": 1024, "description": ""}, + + {"process": "Heap-unclassified process", "path": "heap-allocated", "kind": 2, "units": 0, "amount": 262144000, "description": "Heap allocated."}, + {"process": "Heap-unclassified process", "path": "explicit/a/b", "kind": 1, "units": 0, "amount": 52428800, "description": "A b."}, + {"process": "Heap-unclassified process", "path": "explicit/heap-unclassified", "kind": 1, "units": 0, "amount": 209715200, "description": "Heap unclassified"}, + + {"process": "Explicit-only process", "path": "explicit/a/b", "kind": 1, "units": 0, "amount": 100000, "description": "A b."}, + + {"process": "Other-only process", "path": "a/b", "kind": 1, "units": 0, "amount": 100000, "description": "A b."}, + {"process": "Other-only process", "path": "a/c", "kind": 1, "units": 0, "amount": 100000, "description": "A c."}, + {"process": "Other-only process", "path": "heap-allocated", "kind": 1, "units": 0, "amount": 500000, "description": "D."} + ] +} diff --git a/toolkit/components/aboutmemory/tests/remote.xul b/toolkit/components/aboutmemory/tests/remote.xul new file mode 100644 index 000000000..7d6910130 --- /dev/null +++ b/toolkit/components/aboutmemory/tests/remote.xul @@ -0,0 +1,12 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> +<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" + type="text/css"?> +<window title="Remote browser" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + + <!-- test results are displayed in the html:body --> + <p>Remote browser</p> + + <browser type="content" src="about:blank" id="remote" remote="true"/> +</window> diff --git a/toolkit/components/aboutmemory/tests/test_aboutmemory.xul b/toolkit/components/aboutmemory/tests/test_aboutmemory.xul new file mode 100644 index 000000000..bfeab4c7b --- /dev/null +++ b/toolkit/components/aboutmemory/tests/test_aboutmemory.xul @@ -0,0 +1,602 @@ +<?xml version="1.0"?> +<?xml-stylesheet type="text/css" href="chrome://global/skin"?> +<?xml-stylesheet type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"?> +<window title="about:memory" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/> + <script type="text/javascript" src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script> + + <!-- This file uses fake memory reporters to test the presentation of memory + reports in about:memory. test_memoryReporters.xul uses the real + memory reporters to test whether the memory reporters are producing + sensible results. --> + + <!-- test results are displayed in the html:body --> + <body xmlns="http://www.w3.org/1999/xhtml"></body> + + <!-- test code goes here --> + <script type="application/javascript"> + <![CDATA[ + "use strict"; + + SimpleTest.expectAssertions(27); + + const Cc = Components.classes; + const Ci = Components.interfaces; + const Cr = Components.results; + let mgr = Cc["@mozilla.org/memory-reporter-manager;1"]. + getService(Ci.nsIMemoryReporterManager); + + // Hide all the real reporters; we'll restore them at the end. + mgr.blockRegistrationAndHideExistingReporters(); + + // Setup various fake-but-deterministic reporters. + const KB = 1024; + const MB = KB * KB; + const NONHEAP = Ci.nsIMemoryReporter.KIND_NONHEAP; + const HEAP = Ci.nsIMemoryReporter.KIND_HEAP; + const OTHER = Ci.nsIMemoryReporter.KIND_OTHER; + + const BYTES = Ci.nsIMemoryReporter.UNITS_BYTES; + const COUNT = Ci.nsIMemoryReporter.UNITS_COUNT; + const COUNT_CUMULATIVE = Ci.nsIMemoryReporter.UNITS_COUNT_CUMULATIVE; + const PERCENTAGE = Ci.nsIMemoryReporter.UNITS_PERCENTAGE; + + let fakeReporters = [ + { collectReports: function(aCbObj, aClosure, aAnonymize) { + function f(aP, aK, aU, aA) { + aCbObj.callback("", aP, aK, aU, aA, "Desc.", aClosure); + } + f("heap-allocated", OTHER, BYTES, 500 * MB); + f("heap-unallocated", OTHER, BYTES, 100 * MB); + f("explicit/a", HEAP, BYTES, 222 * MB); + f("explicit/b/a", HEAP, BYTES, 85 * MB); + f("explicit/b/b", HEAP, BYTES, 75 * MB); + f("explicit/b/c/a", HEAP, BYTES, 70 * MB); + f("explicit/b/c/b", HEAP, BYTES, 2 * MB); // omitted + f("explicit/g/a", HEAP, BYTES, 6 * MB); + f("explicit/g/b", HEAP, BYTES, 5 * MB); + f("explicit/g/other", HEAP, BYTES, 4 * MB); + // A degenerate tree with the same name as a non-degenerate tree should + // work ok. + f("explicit", OTHER, BYTES, 888 * MB); + f("other1/a/b", OTHER, BYTES, 111 * MB); + f("other1/c/d", OTHER, BYTES, 22 * MB); + f("other1/c/e", OTHER, BYTES, 33 * MB); + f("other4", OTHER, COUNT_CUMULATIVE, 777); + f("other4", OTHER, COUNT_CUMULATIVE, 111); + f("other3/a/b/c/d/e", OTHER, PERCENTAGE, 2000); + f("other3/a/b/c/d/f", OTHER, PERCENTAGE, 10); + f("other3/a/b/c/d/g", OTHER, PERCENTAGE, 5); + f("other3/a/b/c/d/g", OTHER, PERCENTAGE, 5); + // Check that a rounded-up-to-100.00% value is shown as "100.0%" (i.e. one + // decimal point). + f("other6/big", OTHER, COUNT, 99999); + f("other6/small", OTHER, COUNT, 1); + // Check that a 0 / 0 is handled correctly. + f("other7/zero", OTHER, BYTES, 0); + // These compartments ones shouldn't be displayed. + f("compartments/user/foo", OTHER, COUNT, 1); + f("compartments/system/foo", OTHER, COUNT, 1); + } + }, + { collectReports: function(aCbObj, aClosure, aAnonymize) { + function f(aP, aK, aU, aA) { + aCbObj.callback("", aP, aK, aU, aA, "Desc.", aClosure); + } + f("explicit/c/d", NONHEAP, BYTES, 13 * MB); + f("explicit/c/d", NONHEAP, BYTES, 10 * MB); // dup + f("explicit/c/other", NONHEAP, BYTES, 77 * MB); + f("explicit/cc", NONHEAP, BYTES, 13 * MB); + f("explicit/cc", NONHEAP, BYTES, 10 * MB); // dup + f("explicit/d", NONHEAP, BYTES, 499 * KB); // omitted + f("explicit/e", NONHEAP, BYTES, 100 * KB); // omitted + f("explicit/f/g/h/i", HEAP, BYTES, 10 * MB); + f("explicit/f/g/h/j", HEAP, BYTES, 10 * MB); + } + }, + { collectReports: function(aCbObj, aClosure, aAnonymize) { + function f(aP, aK, aU, aA) { + aCbObj.callback("", aP, aK, aU, aA, "Desc.", aClosure); + } + f("other3", OTHER, COUNT, 777); + f("other2", OTHER, BYTES, 222 * MB); + f("perc2", OTHER, PERCENTAGE, 10000); + f("perc1", OTHER, PERCENTAGE, 4567); + f("compartments/user/https:\\\\very-long-url.com\\very-long\\oh-so-long\\really-quite-long.html?a=2&b=3&c=4&d=5&e=abcdefghijklmnopqrstuvwxyz&f=123456789123456789123456789", OTHER, COUNT, 1); + } + }, + { collectReports: function(aCbObj, aClosure, aAnonymize) { + function f(aP) { + aCbObj.callback("", aP, OTHER, COUNT, 1, "Desc.", aClosure); + } + f("compartments/user/bar"); + f("compartments/system/bar"); + } + } + ]; + for (let i = 0; i < fakeReporters.length; i++) { + mgr.registerStrongReporterEvenIfBlocked(fakeReporters[i]); + } + + // The main process always comes first when we display about:memory. The + // remaining processes are sorted by their |resident| values (starting with + // the largest). Processes without a |resident| memory reporter are saved + // for the end. + let fakeReporters2 = [ + { collectReports: function(aCbObj, aClosure, aAnonymize) { + function f(aP1, aP2, aK, aU, aA) { + aCbObj.callback(aP1, aP2, aK, aU, aA, "Desc.", aClosure); + } + f("2nd", "heap-allocated", OTHER, BYTES,1000* MB); + f("2nd", "heap-unallocated",OTHER, BYTES,100 * MB); + f("2nd", "explicit/a/b/c", HEAP, BYTES,497 * MB); + f("2nd", "explicit/a/b/c", HEAP, BYTES, 1 * MB); // dup: merge + f("2nd", "explicit/a/b/c", HEAP, BYTES, 1 * MB); // dup: merge + f("2nd", "explicit/flip\\the\\backslashes", + HEAP, BYTES,200 * MB); + f("2nd", "explicit/compartment(compartment-url)", + HEAP, BYTES,200 * MB); + f("2nd", "other0", OTHER, BYTES,666 * MB); + f("2nd", "other1", OTHER, BYTES,111 * MB); + + // Check that we can handle "heap-allocated" not being present. + f("3rd", "explicit/a/b", HEAP, BYTES,333 * MB); + f("3rd", "explicit/a/c", HEAP, BYTES,444 * MB); + f("3rd", "other1", OTHER, BYTES, 1 * MB); + f("3rd", "resident", OTHER, BYTES,100 * MB); + + // Invalid values (negative, too-big) should be identified. + f("4th", "heap-allocated", OTHER, BYTES,100 * MB); + f("4th", "resident", OTHER, BYTES,200 * MB); + f("4th", "explicit/js/compartment(http:\\\\too-big.com\\)/stuff", + HEAP, BYTES,150 * MB); + f("4th", "explicit/ok", HEAP, BYTES, 5 * MB); + f("4th", "explicit/neg1", NONHEAP, BYTES, -2 * MB); + // -111 becomes "-0.00MB" in non-verbose mode, and getting the negative + // sign in there correctly is non-trivial. + f("4th", "other1", OTHER, BYTES,-111); + f("4th", "other2", OTHER, BYTES,-222 * MB); + f("4th", "other3", OTHER, COUNT, -333); + f("4th", "other4", OTHER, COUNT_CUMULATIVE, -444); + f("4th", "other5", OTHER, PERCENTAGE, -555); + f("4th", "other6", OTHER, PERCENTAGE, 66666); + + // If a negative value is within a collapsed sub-tree in non-verbose mode, + // we should get the warning at the top and the relevant sub-trees should + // be expanded, even in non-verbose mode. + f("5th", "heap-allocated", OTHER, BYTES,100 * MB); + f("5th", "explicit/big", HEAP, BYTES, 99 * MB); + f("5th", "explicit/a/pos", HEAP, BYTES, 40 * KB); + f("5th", "explicit/a/neg1", NONHEAP, BYTES,-20 * KB); + f("5th", "explicit/a/neg2", NONHEAP, BYTES,-10 * KB); + f("5th", "explicit/b/c/d/e", NONHEAP, BYTES, 20 * KB); + f("5th", "explicit/b/c/d/f", NONHEAP, BYTES,-60 * KB); + f("5th", "explicit/b/c/g/h", NONHEAP, BYTES, 10 * KB); + f("5th", "explicit/b/c/i/j", NONHEAP, BYTES, 5 * KB); + } + } + ]; + for (let i = 0; i < fakeReporters2.length; i++) { + mgr.registerStrongReporterEvenIfBlocked(fakeReporters2[i]); + } + fakeReporters = fakeReporters.concat(fakeReporters2); + ]]> + </script> + + <iframe id="amFrame" height="300" src="about:memory"></iframe> + <!-- vary the capitalization to make sure that works --> + <iframe id="amvFrame" height="300" src="About:Memory"></iframe> + + <script type="application/javascript"> + <![CDATA[ + let amExpectedText = +"\ +Main Process\n\ +Explicit Allocations\n\ +\n\ +623.58 MB (100.0%) -- explicit\n\ +├──232.00 MB (37.20%) -- b\n\ +│ ├───85.00 MB (13.63%) ── a\n\ +│ ├───75.00 MB (12.03%) ── b\n\ +│ └───72.00 MB (11.55%) -- c\n\ +│ ├──70.00 MB (11.23%) ── a\n\ +│ └───2.00 MB (00.32%) ── b\n\ +├──222.00 MB (35.60%) ── a\n\ +├──100.00 MB (16.04%) -- c\n\ +│ ├───77.00 MB (12.35%) ── other\n\ +│ └───23.00 MB (03.69%) ── d [2]\n\ +├───23.00 MB (03.69%) ── cc [2]\n\ +├───20.00 MB (03.21%) -- f/g/h\n\ +│ ├──10.00 MB (01.60%) ── i\n\ +│ └──10.00 MB (01.60%) ── j\n\ +├───15.00 MB (02.41%) ++ g\n\ +├───11.00 MB (01.76%) ── heap-unclassified\n\ +└────0.58 MB (00.09%) ++ (2 tiny)\n\ +\n\ +Other Measurements\n\ +\n\ +5 (100.0%) -- compartments\n\ +├──3 (60.00%) -- user\n\ +│ ├──1 (20.00%) ── bar\n\ +│ ├──1 (20.00%) ── foo\n\ +│ └──1 (20.00%) ── https://very-long-url.com/very-long/oh-so-long/really-quite-long.html?a=2&b=3&c=4&d=5&e=abcdefghijklmnopqrstuvwxyz&f=123456789123456789123456789\n\ +└──2 (40.00%) -- system\n\ + ├──1 (20.00%) ── bar\n\ + └──1 (20.00%) ── foo\n\ +\n\ +166.00 MB (100.0%) -- other1\n\ +├──111.00 MB (66.87%) ── a/b\n\ +└───55.00 MB (33.13%) -- c\n\ + ├──33.00 MB (19.88%) ── e\n\ + └──22.00 MB (13.25%) ── d\n\ +\n\ +20.20% (100.0%) -- other3\n\ +└──20.20% (100.0%) -- a/b/c/d\n\ + ├──20.00% (99.01%) ── e\n\ + └───0.20% (00.99%) ++ (2 tiny)\n\ +\n\ +100,000 (100.0%) -- other6\n\ +├───99,999 (100.0%) ── big\n\ +└────────1 (00.00%) ── small\n\ +\n\ +0.00 MB (100.0%) -- other7\n\ +└──0.00 MB (100.0%) ── zero\n\ +\n\ +888.00 MB ── explicit\n\ +500.00 MB ── heap-allocated\n\ +100.00 MB ── heap-unallocated\n\ +222.00 MB ── other2\n\ + 777 ── other3\n\ + 888 ── other4 [2]\n\ + 45.67% ── perc1\n\ + 100.00% ── perc2\n\ +\n\ +End of Main Process\n\ +4th\n\ +\n\ +WARNING: the following values are negative or unreasonably large.\n\ +\n\ + explicit/js/compartment(http://too-big.com/)/stuff\n\ + explicit/(2 tiny)\n\ + explicit/(2 tiny)/neg1\n\ + explicit/(2 tiny)/heap-unclassified\n\ + other1\n\ + other2\n\ + other3\n\ + other4\n\ + other5 \n\ +\n\ +This indicates a defect in one or more memory reporters. The invalid values are highlighted.\n\ +Explicit Allocations\n\ +\n\ +98.00 MB (100.0%) -- explicit\n\ +├──150.00 MB (153.06%) ── js/compartment(http://too-big.com/)/stuff [?!]\n\ +├───5.00 MB (05.10%) ── ok\n\ +└──-57.00 MB (-58.16%) -- (2 tiny) [?!]\n\ + ├───-2.00 MB (-2.04%) ── neg1 [?!]\n\ + └──-55.00 MB (-56.12%) ── heap-unclassified [?!]\n\ +\n\ +Other Measurements\n\ +\n\ + 100.00 MB ── heap-allocated\n\ + -0.00 MB ── other1 [?!]\n\ +-222.00 MB ── other2 [?!]\n\ + -333 ── other3 [?!]\n\ + -444 ── other4 [?!]\n\ + -5.55% ── other5 [?!]\n\ + 666.66% ── other6\n\ + 200.00 MB ── resident\n\ +\n\ +End of 4th\n\ +3rd\n\ +\n\ +WARNING: the 'heap-allocated' memory reporter does not work for this platform and/or configuration. This means that 'heap-unclassified' is not shown and the 'explicit' tree shows less memory than it should.\n\ +Explicit Allocations\n\ +\n\ +777.00 MB (100.0%) -- explicit\n\ +└──777.00 MB (100.0%) -- a\n\ + ├──444.00 MB (57.14%) ── c\n\ + └──333.00 MB (42.86%) ── b\n\ +\n\ +Other Measurements\n\ +\n\ + 1.00 MB ── other1\n\ +100.00 MB ── resident\n\ +\n\ +End of 3rd\n\ +2nd\n\ +Explicit Allocations\n\ +\n\ +1,000.00 MB (100.0%) -- explicit\n\ +├────499.00 MB (49.90%) ── a/b/c [3]\n\ +├────200.00 MB (20.00%) ── compartment(compartment-url)\n\ +├────200.00 MB (20.00%) ── flip/the/backslashes\n\ +└────101.00 MB (10.10%) ── heap-unclassified\n\ +\n\ +Other Measurements\n\ +\n\ +1,000.00 MB ── heap-allocated\n\ + 100.00 MB ── heap-unallocated\n\ + 666.00 MB ── other0\n\ + 111.00 MB ── other1\n\ +\n\ +End of 2nd\n\ +5th\n\ +\n\ +WARNING: the following values are negative or unreasonably large.\n\ +\n\ + explicit/(3 tiny)/a/neg2\n\ + explicit/(3 tiny)/a/neg1\n\ + explicit/(3 tiny)/b/c\n\ + explicit/(3 tiny)/b/c/d\n\ + explicit/(3 tiny)/b/c/d/f \n\ +\n\ +This indicates a defect in one or more memory reporters. The invalid values are highlighted.\n\ +Explicit Allocations\n\ +\n\ +99.95 MB (100.0%) -- explicit\n\ +├──99.00 MB (99.05%) ── big\n\ +└───0.95 MB (00.95%) -- (3 tiny)\n\ + ├──0.96 MB (00.96%) ── heap-unclassified\n\ + ├──0.01 MB (00.01%) -- a\n\ + │ ├──0.04 MB (00.04%) ── pos\n\ + │ ├──-0.01 MB (-0.01%) ── neg2 [?!]\n\ + │ └──-0.02 MB (-0.02%) ── neg1 [?!]\n\ + └──-0.02 MB (-0.02%) -- b/c [?!]\n\ + ├───0.01 MB (00.01%) ── g/h\n\ + ├───0.00 MB (00.00%) ── i/j\n\ + └──-0.04 MB (-0.04%) -- d [?!]\n\ + ├───0.02 MB (00.02%) ── e\n\ + └──-0.06 MB (-0.06%) ── f [?!]\n\ +\n\ +Other Measurements\n\ +\n\ +100.00 MB ── heap-allocated\n\ +\n\ +End of 5th\n\ +"; + + let amvExpectedText = +"\ +Main Process\n\ +Explicit Allocations\n\ +\n\ +653,876,224 B (100.0%) -- explicit\n\ +├──243,269,632 B (37.20%) -- b\n\ +│ ├───89,128,960 B (13.63%) ── a\n\ +│ ├───78,643,200 B (12.03%) ── b\n\ +│ └───75,497,472 B (11.55%) -- c\n\ +│ ├──73,400,320 B (11.23%) ── a\n\ +│ └───2,097,152 B (00.32%) ── b\n\ +├──232,783,872 B (35.60%) ── a\n\ +├──104,857,600 B (16.04%) -- c\n\ +│ ├───80,740,352 B (12.35%) ── other\n\ +│ └───24,117,248 B (03.69%) ── d [2]\n\ +├───24,117,248 B (03.69%) ── cc [2]\n\ +├───20,971,520 B (03.21%) -- f/g/h\n\ +│ ├──10,485,760 B (01.60%) ── i\n\ +│ └──10,485,760 B (01.60%) ── j\n\ +├───15,728,640 B (02.41%) -- g\n\ +│ ├───6,291,456 B (00.96%) ── a\n\ +│ ├───5,242,880 B (00.80%) ── b\n\ +│ └───4,194,304 B (00.64%) ── other\n\ +├───11,534,336 B (01.76%) ── heap-unclassified\n\ +├──────510,976 B (00.08%) ── d\n\ +└──────102,400 B (00.02%) ── e\n\ +\n\ +Other Measurements\n\ +\n\ +5 (100.0%) -- compartments\n\ +├──3 (60.00%) -- user\n\ +│ ├──1 (20.00%) ── bar\n\ +│ ├──1 (20.00%) ── foo\n\ +│ └──1 (20.00%) ── https://very-long-url.com/very-long/oh-so-long/really-quite-long.html?a=2&b=3&c=4&d=5&e=abcdefghijklmnopqrstuvwxyz&f=123456789123456789123456789\n\ +└──2 (40.00%) -- system\n\ + ├──1 (20.00%) ── bar\n\ + └──1 (20.00%) ── foo\n\ +\n\ +174,063,616 B (100.0%) -- other1\n\ +├──116,391,936 B (66.87%) ── a/b\n\ +└───57,671,680 B (33.13%) -- c\n\ + ├──34,603,008 B (19.88%) ── e\n\ + └──23,068,672 B (13.25%) ── d\n\ +\n\ +20.20% (100.0%) -- other3\n\ +└──20.20% (100.0%) -- a/b/c/d\n\ + ├──20.00% (99.01%) ── e\n\ + ├───0.10% (00.50%) ── f\n\ + └───0.10% (00.50%) ── g [2]\n\ +\n\ +100,000 (100.0%) -- other6\n\ +├───99,999 (100.0%) ── big\n\ +└────────1 (00.00%) ── small\n\ +\n\ +0 B (100.0%) -- other7\n\ +└──0 B (100.0%) ── zero\n\ +\n\ +931,135,488 B ── explicit\n\ +524,288,000 B ── heap-allocated\n\ +104,857,600 B ── heap-unallocated\n\ +232,783,872 B ── other2\n\ + 777 ── other3\n\ + 888 ── other4 [2]\n\ + 45.67% ── perc1\n\ + 100.00% ── perc2\n\ +\n\ +End of Main Process\n\ +4th\n\ +\n\ +WARNING: the following values are negative or unreasonably large.\n\ +\n\ + explicit/js/compartment(http://too-big.com/)/stuff\n\ + explicit/neg1\n\ + explicit/heap-unclassified\n\ + other1\n\ + other2\n\ + other3\n\ + other4\n\ + other5 \n\ +\n\ +This indicates a defect in one or more memory reporters. The invalid values are highlighted.\n\ +Explicit Allocations\n\ +\n\ +102,760,448 B (100.0%) -- explicit\n\ +├──157,286,400 B (153.06%) ── js/compartment(http://too-big.com/)/stuff [?!]\n\ +├────5,242,880 B (05.10%) ── ok\n\ +├───-2,097,152 B (-2.04%) ── neg1 [?!]\n\ +└──-57,671,680 B (-56.12%) ── heap-unclassified [?!]\n\ +\n\ +Other Measurements\n\ +\n\ + 104,857,600 B ── heap-allocated\n\ + -111 B ── other1 [?!]\n\ +-232,783,872 B ── other2 [?!]\n\ + -333 ── other3 [?!]\n\ + -444 ── other4 [?!]\n\ + -5.55% ── other5 [?!]\n\ + 666.66% ── other6\n\ + 209,715,200 B ── resident\n\ +\n\ +End of 4th\n\ +3rd\n\ +\n\ +WARNING: the 'heap-allocated' memory reporter does not work for this platform and/or configuration. This means that 'heap-unclassified' is not shown and the 'explicit' tree shows less memory than it should.\n\ +Explicit Allocations\n\ +\n\ +814,743,552 B (100.0%) -- explicit\n\ +└──814,743,552 B (100.0%) -- a\n\ + ├──465,567,744 B (57.14%) ── c\n\ + └──349,175,808 B (42.86%) ── b\n\ +\n\ +Other Measurements\n\ +\n\ + 1,048,576 B ── other1\n\ +104,857,600 B ── resident\n\ +\n\ +End of 3rd\n\ +2nd\n\ +Explicit Allocations\n\ +\n\ +1,048,576,000 B (100.0%) -- explicit\n\ +├────523,239,424 B (49.90%) ── a/b/c [3]\n\ +├────209,715,200 B (20.00%) ── compartment(compartment-url)\n\ +├────209,715,200 B (20.00%) ── flip/the/backslashes\n\ +└────105,906,176 B (10.10%) ── heap-unclassified\n\ +\n\ +Other Measurements\n\ +\n\ +1,048,576,000 B ── heap-allocated\n\ + 104,857,600 B ── heap-unallocated\n\ + 698,351,616 B ── other0\n\ + 116,391,936 B ── other1\n\ +\n\ +End of 2nd\n\ +5th\n\ +\n\ +WARNING: the following values are negative or unreasonably large.\n\ +\n\ + explicit/a/neg2\n\ + explicit/a/neg1\n\ + explicit/b/c\n\ + explicit/b/c/d\n\ + explicit/b/c/d/f \n\ +\n\ +This indicates a defect in one or more memory reporters. The invalid values are highlighted.\n\ +Explicit Allocations\n\ +\n\ +104,801,280 B (100.0%) -- explicit\n\ +├──103,809,024 B (99.05%) ── big\n\ +├────1,007,616 B (00.96%) ── heap-unclassified\n\ +├───────10,240 B (00.01%) -- a\n\ +│ ├──40,960 B (00.04%) ── pos\n\ +│ ├──-10,240 B (-0.01%) ── neg2 [?!]\n\ +│ └──-20,480 B (-0.02%) ── neg1 [?!]\n\ +└──────-25,600 B (-0.02%) -- b/c [?!]\n\ + ├───10,240 B (00.01%) ── g/h\n\ + ├────5,120 B (00.00%) ── i/j\n\ + └──-40,960 B (-0.04%) -- d [?!]\n\ + ├───20,480 B (00.02%) ── e\n\ + └──-61,440 B (-0.06%) ── f [?!]\n\ +\n\ +Other Measurements\n\ +\n\ +104,857,600 B ── heap-allocated\n\ +\n\ +End of 5th\n\ +"; + + function finish() + { + mgr.unblockRegistrationAndRestoreOriginalReporters(); + SimpleTest.finish(); + } + + // 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(aFrameId, aVerbose, aExpected, aNext) { + SimpleTest.executeSoon(function() { + ok(document.title === "about:memory", "document.title is correct"); + let mostRecentActual; + let frame = document.getElementById(aFrameId); + frame.focus(); + + // Set the verbose checkbox value and click the go button. + let doc = frame.contentWindow.document; + let measureButton = doc.getElementById("measureButton"); + let verbose = doc.getElementById("verbose"); + verbose.checked = aVerbose; + measureButton.click(); + + SimpleTest.waitForClipboard( + function(aActual) { + mostRecentActual = aActual; + let rslt = aActual.trim() === aExpected.trim(); + if (!rslt) { + // Try copying again. + synthesizeKey("A", {accelKey: true}); + synthesizeKey("C", {accelKey: true}); + } + + return rslt; + }, + function() { + synthesizeKey("A", {accelKey: true}); + synthesizeKey("C", {accelKey: true}); + }, + aNext, + function() { + ok(false, "pasted text doesn't match for " + aFrameId); + dump("******EXPECTED******\n"); + dump("<<<" + aExpected + ">>>\n"); + dump("*******ACTUAL*******\n"); + dump("<<<" + mostRecentActual + ">>>\n"); + dump("********************\n"); + finish(); + } + ); + }); + } + + SimpleTest.waitForFocus(function() { + test( + "amFrame", + /* verbose = */ false, + amExpectedText, + function() { + test( + "amvFrame", + /* verbose = */ true, + amvExpectedText, + function() { + finish() + } + ) + } + ); + }); + SimpleTest.waitForExplicitFinish(); + ]]> + </script> +</window> diff --git a/toolkit/components/aboutmemory/tests/test_aboutmemory2.xul b/toolkit/components/aboutmemory/tests/test_aboutmemory2.xul new file mode 100644 index 000000000..8cf197e6d --- /dev/null +++ b/toolkit/components/aboutmemory/tests/test_aboutmemory2.xul @@ -0,0 +1,423 @@ +<?xml version="1.0"?> +<?xml-stylesheet type="text/css" href="chrome://global/skin"?> +<?xml-stylesheet type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"?> +<window title="about:memory" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/> + <script type="text/javascript" src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script> + + <!-- This file tests the collapsing and expanding of sub-trees in + about:memory. --> + + <!-- test results are displayed in the html:body --> + <body xmlns="http://www.w3.org/1999/xhtml"></body> + + <!-- test code goes here --> + <script type="application/javascript"> + <![CDATA[ + "use strict"; + + const Cc = Components.classes; + const Ci = Components.interfaces; + let mgr = Cc["@mozilla.org/memory-reporter-manager;1"]. + getService(Ci.nsIMemoryReporterManager); + + // Hide all the real reporters; we'll restore them at the end. + mgr.blockRegistrationAndHideExistingReporters(); + + // Setup various fake-but-deterministic reporters. + const KB = 1024; + const MB = KB * KB; + const HEAP = Ci.nsIMemoryReporter.KIND_HEAP; + const OTHER = Ci.nsIMemoryReporter.KIND_OTHER; + const BYTES = Ci.nsIMemoryReporter.UNITS_BYTES; + + let hiPath = "explicit/h/i"; + let hi2Path = "explicit/h/i2"; + let jkPath = "explicit/j/k"; + let jk2Path = "explicit/j/k2"; + + let fakeReporters = [ + { collectReports: function(aCbObj, aClosure, aAnonymize) { + function f(aP, aK, aA) { + aCbObj.callback("", aP, aK, BYTES, aA, "Desc.", aClosure); + } + f("heap-allocated", OTHER, 250 * MB); + f("explicit/a/b", HEAP, 50 * MB); + f("explicit/a/c/d", HEAP, 25 * MB); + f("explicit/a/c/e", HEAP, 15 * MB); + f("explicit/a/f", HEAP, 30 * MB); + f("explicit/g", HEAP, 100 * MB); + f(hiPath, HEAP, 10 * MB); + f(hi2Path, HEAP, 9 * MB); + f(jkPath, HEAP, 0.5 * MB); + f(jk2Path, HEAP, 0.3 * MB); + f("explicit/a/l/m", HEAP, 0.1 * MB); + f("explicit/a/l/n", HEAP, 0.1 * MB); + } + } + ]; + + for (let i = 0; i < fakeReporters.length; i++) { + mgr.registerStrongReporterEvenIfBlocked(fakeReporters[i]); + } + + ]]> + </script> + + <iframe id="amFrame" height="500" src="about:memory"></iframe> + + <script type="application/javascript"> + <![CDATA[ + function finish() + { + mgr.unblockRegistrationAndRestoreOriginalReporters(); + SimpleTest.finish(); + } + + // Click on the identified element, then cut+paste the entire page and + // check that the cut text matches what we expect. + function test(aId, aSwap, aExpected, aNext) { + let win = document.getElementById("amFrame").contentWindow; + if (aId) { + let node = win.document.getElementById(aId); + + // Yuk: clicking a button is easy; but for tree entries we need to + // click on a child of the span identified via |id|. + if (node.nodeName === "button") { + if (aSwap) { + // We swap hipath/hi2Path and jkPath/jk2Path just before updating, to + // test what happens when significant nodes become insignificant and + // vice versa. + hiPath = "explicit/j/k"; + hi2Path = "explicit/j/k2"; + jkPath = "explicit/h/i"; + jk2Path = "explicit/h/i2"; + } + node.click(); + } else { + node.childNodes[0].click(); + } + } + + SimpleTest.executeSoon(function() { + let mostRecentActual; + document.getElementById("amFrame").focus(); + SimpleTest.waitForClipboard( + function(aActual) { + mostRecentActual = aActual; + let rslt = aActual.trim() === aExpected.trim(); + if (!rslt) { + // Try copying again. + synthesizeKey("A", {accelKey: true}); + synthesizeKey("C", {accelKey: true}); + } + + return rslt; + }, + function() { + synthesizeKey("A", {accelKey: true}); + synthesizeKey("C", {accelKey: true}); + }, + aNext, + function() { + ok(false, "pasted text doesn't match"); + dump("******EXPECTED******\n"); + dump(aExpected); + dump("*******ACTUAL*******\n"); + dump(mostRecentActual); + dump("********************\n"); + finish(); + } + ); + }); + } + + // Returns a function that chains together one test() call per id. + function chain(aIds) { + let x = aIds.shift(); + if (x) { + return function() { test(x.id, x.swap, x.expected, chain(aIds)); } + } else { + return function() { finish(); }; + } + } + + let startExpected = +"\ +Main Process\n\ +Explicit Allocations\n\ +\n\ +250.00 MB (100.0%) -- explicit\n\ +├──120.20 MB (48.08%) -- a\n\ +│ ├───50.00 MB (20.00%) ── b\n\ +│ ├───40.00 MB (16.00%) -- c\n\ +│ │ ├──25.00 MB (10.00%) ── d\n\ +│ │ └──15.00 MB (06.00%) ── e\n\ +│ ├───30.00 MB (12.00%) ── f\n\ +│ └────0.20 MB (00.08%) ++ l\n\ +├──100.00 MB (40.00%) ── g\n\ +├───19.00 MB (07.60%) -- h\n\ +│ ├──10.00 MB (04.00%) ── i\n\ +│ └───9.00 MB (03.60%) ── i2\n\ +├───10.00 MB (04.00%) ── heap-unclassified\n\ +└────0.80 MB (00.32%) ++ j\n\ +\n\ +Other Measurements\n\ +\n\ +250.00 MB ── heap-allocated\n\ +\n\ +End of Main Process\n\ +"; + + let acCollapsedExpected = +"\ +Main Process\n\ +Explicit Allocations\n\ +\n\ +250.00 MB (100.0%) -- explicit\n\ +├──120.20 MB (48.08%) -- a\n\ +│ ├───50.00 MB (20.00%) ── b\n\ +│ ├───40.00 MB (16.00%) ++ c\n\ +│ ├───30.00 MB (12.00%) ── f\n\ +│ └────0.20 MB (00.08%) ++ l\n\ +├──100.00 MB (40.00%) ── g\n\ +├───19.00 MB (07.60%) -- h\n\ +│ ├──10.00 MB (04.00%) ── i\n\ +│ └───9.00 MB (03.60%) ── i2\n\ +├───10.00 MB (04.00%) ── heap-unclassified\n\ +└────0.80 MB (00.32%) ++ j\n\ +\n\ +Other Measurements\n\ +\n\ +250.00 MB ── heap-allocated\n\ +\n\ +End of Main Process\n\ +"; + + let alExpandedExpected = +"\ +Main Process\n\ +Explicit Allocations\n\ +\n\ +250.00 MB (100.0%) -- explicit\n\ +├──120.20 MB (48.08%) -- a\n\ +│ ├───50.00 MB (20.00%) ── b\n\ +│ ├───40.00 MB (16.00%) ++ c\n\ +│ ├───30.00 MB (12.00%) ── f\n\ +│ └────0.20 MB (00.08%) -- l\n\ +│ ├──0.10 MB (00.04%) ── m\n\ +│ └──0.10 MB (00.04%) ── n\n\ +├──100.00 MB (40.00%) ── g\n\ +├───19.00 MB (07.60%) -- h\n\ +│ ├──10.00 MB (04.00%) ── i\n\ +│ └───9.00 MB (03.60%) ── i2\n\ +├───10.00 MB (04.00%) ── heap-unclassified\n\ +└────0.80 MB (00.32%) ++ j\n\ +\n\ +Other Measurements\n\ +\n\ +250.00 MB ── heap-allocated\n\ +\n\ +End of Main Process\n\ +"; + + let aCollapsedExpected = +"\ +Main Process\n\ +Explicit Allocations\n\ +\n\ +250.00 MB (100.0%) -- explicit\n\ +├──120.20 MB (48.08%) ++ a\n\ +├──100.00 MB (40.00%) ── g\n\ +├───19.00 MB (07.60%) -- h\n\ +│ ├──10.00 MB (04.00%) ── i\n\ +│ └───9.00 MB (03.60%) ── i2\n\ +├───10.00 MB (04.00%) ── heap-unclassified\n\ +└────0.80 MB (00.32%) ++ j\n\ +\n\ +Other Measurements\n\ +\n\ +250.00 MB ── heap-allocated\n\ +\n\ +End of Main Process\n\ +"; + + let hCollapsedExpected = +"\ +Main Process\n\ +Explicit Allocations\n\ +\n\ +250.00 MB (100.0%) -- explicit\n\ +├──120.20 MB (48.08%) ++ a\n\ +├──100.00 MB (40.00%) ── g\n\ +├───19.00 MB (07.60%) ++ h\n\ +├───10.00 MB (04.00%) ── heap-unclassified\n\ +└────0.80 MB (00.32%) ++ j\n\ +\n\ +Other Measurements\n\ +\n\ +250.00 MB ── heap-allocated\n\ +\n\ +End of Main Process\n\ +"; + + let jExpandedExpected = +"\ +Main Process\n\ +Explicit Allocations\n\ +\n\ +250.00 MB (100.0%) -- explicit\n\ +├──120.20 MB (48.08%) ++ a\n\ +├──100.00 MB (40.00%) ── g\n\ +├───19.00 MB (07.60%) ++ h\n\ +├───10.00 MB (04.00%) ── heap-unclassified\n\ +└────0.80 MB (00.32%) -- j\n\ + ├──0.50 MB (00.20%) ── k\n\ + └──0.30 MB (00.12%) ── k2\n\ +\n\ +Other Measurements\n\ +\n\ +250.00 MB ── heap-allocated\n\ +\n\ +End of Main Process\n\ +"; + + // The important thing here is that two values have been swapped. + // explicit/h/i should remain collapsed, and explicit/j/k should remain + // expanded. See bug 724863. + let updatedExpected = +"\ +Main Process\n\ +Explicit Allocations\n\ +\n\ +250.00 MB (100.0%) -- explicit\n\ +├──120.20 MB (48.08%) ++ a\n\ +├──100.00 MB (40.00%) ── g\n\ +├───19.00 MB (07.60%) -- j\n\ +│ ├──10.00 MB (04.00%) ── k\n\ +│ └───9.00 MB (03.60%) ── k2\n\ +├───10.00 MB (04.00%) ── heap-unclassified\n\ +└────0.80 MB (00.32%) ++ h\n\ +\n\ +Other Measurements\n\ +\n\ +250.00 MB ── heap-allocated\n\ +\n\ +End of Main Process\n\ +"; + + let aExpandedExpected = +"\ +Main Process\n\ +Explicit Allocations\n\ +\n\ +250.00 MB (100.0%) -- explicit\n\ +├──120.20 MB (48.08%) -- a\n\ +│ ├───50.00 MB (20.00%) ── b\n\ +│ ├───40.00 MB (16.00%) ++ c\n\ +│ ├───30.00 MB (12.00%) ── f\n\ +│ └────0.20 MB (00.08%) -- l\n\ +│ ├──0.10 MB (00.04%) ── m\n\ +│ └──0.10 MB (00.04%) ── n\n\ +├──100.00 MB (40.00%) ── g\n\ +├───19.00 MB (07.60%) -- j\n\ +│ ├──10.00 MB (04.00%) ── k\n\ +│ └───9.00 MB (03.60%) ── k2\n\ +├───10.00 MB (04.00%) ── heap-unclassified\n\ +└────0.80 MB (00.32%) ++ h\n\ +\n\ +Other Measurements\n\ +\n\ +250.00 MB ── heap-allocated\n\ +\n\ +End of Main Process\n\ +"; + + let acExpandedExpected = +"\ +Main Process\n\ +Explicit Allocations\n\ +\n\ +250.00 MB (100.0%) -- explicit\n\ +├──120.20 MB (48.08%) -- a\n\ +│ ├───50.00 MB (20.00%) ── b\n\ +│ ├───40.00 MB (16.00%) -- c\n\ +│ │ ├──25.00 MB (10.00%) ── d\n\ +│ │ └──15.00 MB (06.00%) ── e\n\ +│ ├───30.00 MB (12.00%) ── f\n\ +│ └────0.20 MB (00.08%) -- l\n\ +│ ├──0.10 MB (00.04%) ── m\n\ +│ └──0.10 MB (00.04%) ── n\n\ +├──100.00 MB (40.00%) ── g\n\ +├───19.00 MB (07.60%) -- j\n\ +│ ├──10.00 MB (04.00%) ── k\n\ +│ └───9.00 MB (03.60%) ── k2\n\ +├───10.00 MB (04.00%) ── heap-unclassified\n\ +└────0.80 MB (00.32%) ++ h\n\ +\n\ +Other Measurements\n\ +\n\ +250.00 MB ── heap-allocated\n\ +\n\ +End of Main Process\n\ +"; + + let alCollapsedExpected = +"\ +Main Process\n\ +Explicit Allocations\n\ +\n\ +250.00 MB (100.0%) -- explicit\n\ +├──120.20 MB (48.08%) -- a\n\ +│ ├───50.00 MB (20.00%) ── b\n\ +│ ├───40.00 MB (16.00%) -- c\n\ +│ │ ├──25.00 MB (10.00%) ── d\n\ +│ │ └──15.00 MB (06.00%) ── e\n\ +│ ├───30.00 MB (12.00%) ── f\n\ +│ └────0.20 MB (00.08%) ++ l\n\ +├──100.00 MB (40.00%) ── g\n\ +├───19.00 MB (07.60%) -- j\n\ +│ ├──10.00 MB (04.00%) ── k\n\ +│ └───9.00 MB (03.60%) ── k2\n\ +├───10.00 MB (04.00%) ── heap-unclassified\n\ +└────0.80 MB (00.32%) ++ h\n\ +\n\ +Other Measurements\n\ +\n\ +250.00 MB ── heap-allocated\n\ +\n\ +End of Main Process\n\ +"; + + // Test the following cases: + // - explicit/a/c is significant, we collapse it, it's unchanged upon + // update, we re-expand it + // - explicit/a/l is insignificant, we expand it, it's unchanged upon + // update, we re-collapse it + // - explicit/a is significant, we collapse it (which hides its + // sub-trees), it's unchanged upon update, we re-expand it + // - explicit/h is significant, we collapse it, it becomes insignificant + // upon update (and should remain collapsed) + // - explicit/j is insignificant, we expand it, it becomes significant + // upon update (and should remain expanded) + // + let idsToClick = [ + { id: "measureButton", swap: 0, expected: startExpected }, + { id: "Main Process:explicit/a/c", swap: 0, expected: acCollapsedExpected }, + { id: "Main Process:explicit/a/l", swap: 0, expected: alExpandedExpected }, + { id: "Main Process:explicit/a", swap: 0, expected: aCollapsedExpected }, + { id: "Main Process:explicit/h", swap: 0, expected: hCollapsedExpected }, + { id: "Main Process:explicit/j", swap: 0, expected: jExpandedExpected }, + { id: "measureButton", swap: 1, expected: updatedExpected }, + { id: "Main Process:explicit/a", swap: 0, expected: aExpandedExpected }, + { id: "Main Process:explicit/a/c", swap: 0, expected: acExpandedExpected }, + { id: "Main Process:explicit/a/l", swap: 0, expected: alCollapsedExpected } + ]; + + SimpleTest.waitForFocus(chain(idsToClick)); + + SimpleTest.waitForExplicitFinish(); + ]]> + </script> +</window> diff --git a/toolkit/components/aboutmemory/tests/test_aboutmemory3.xul b/toolkit/components/aboutmemory/tests/test_aboutmemory3.xul new file mode 100644 index 000000000..c712070cc --- /dev/null +++ b/toolkit/components/aboutmemory/tests/test_aboutmemory3.xul @@ -0,0 +1,515 @@ +<?xml version="1.0"?> +<?xml-stylesheet type="text/css" href="chrome://global/skin"?> +<?xml-stylesheet type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"?> +<window title="about:memory" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/> + <script type="text/javascript" src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script> + + <!-- This file tests the saving and loading of memory reports to/from file in + about:memory. --> + + <!-- test results are displayed in the html:body --> + <body xmlns="http://www.w3.org/1999/xhtml"></body> + + <!-- test code goes here --> + <script type="application/javascript"> + <![CDATA[ + "use strict"; + + const Cc = Components.classes; + const Ci = Components.interfaces; + let mgr = Cc["@mozilla.org/memory-reporter-manager;1"]. + getService(Ci.nsIMemoryReporterManager); + + // Hide all the real reporters; we'll restore them at the end. + mgr.blockRegistrationAndHideExistingReporters(); + + // Setup a minimal number of fake reporters. + const KB = 1024; + const MB = KB * KB; + const HEAP = Ci.nsIMemoryReporter.KIND_HEAP; + const OTHER = Ci.nsIMemoryReporter.KIND_OTHER; + const BYTES = Ci.nsIMemoryReporter.UNITS_BYTES; + + let fakeReporters = [ + { collectReports: function(aCbObj, aClosure, aAnonymize) { + function f(aP, aK, aA, aD) { + aCbObj.callback("", aP, aK, BYTES, aA, aD, aClosure); + } + f("heap-allocated", OTHER, 250 * MB, "Heap allocated."); + f("explicit/a/b", HEAP, 50 * MB, "A b."); + f("other/a", OTHER, 0.2 * MB, "Other a."); + f("other/b", OTHER, 0.1 * MB, "Other b."); + } + } + ]; + + for (let i = 0; i < fakeReporters.length; i++) { + mgr.registerStrongReporterEvenIfBlocked(fakeReporters[i]); + } + + ]]> + </script> + + <iframe id="amFrame" height="400" src="about:memory"></iframe> + + <script type="application/javascript"> + <![CDATA[ + function finish() + { + mgr.unblockRegistrationAndRestoreOriginalReporters(); + SimpleTest.finish(); + } + + // Load the given file into the frame, then copy+paste the entire frame and + // check that the cut text matches what we expect. + function test(aFilename, aFilename2, aExpected, aDumpFirst, aVerbose, aNext) { + let frame = document.getElementById("amFrame"); + frame.focus(); + + let doc = frame.contentWindow.document; + let verbosity = doc.getElementById("verbose"); + verbosity.checked = aVerbose; + + function getFilePath(aFilename) { + let file = Cc["@mozilla.org/file/directory_service;1"] + .getService(Components.interfaces.nsIProperties) + .get("CurWorkD", Components.interfaces.nsIFile); + file.append("chrome"); + file.append("toolkit"); + file.append("components"); + file.append("aboutmemory"); + file.append("tests"); + file.append(aFilename); + return file.path; + } + + let filePath = getFilePath(aFilename); + + let e = document.createEvent('Event'); + e.initEvent('change', true, true); + + function check() { + // Initialize the clipboard contents. + SpecialPowers.clipboardCopyString("initial clipboard value"); + + let numFailures = 0, maxFailures = 30; + + // Because the file load is async, we don't know when it will finish and + // the output will show up. So we poll. + function copyPasteAndCheck() { + // Copy and paste frame contents, and filter out non-deterministic + // differences. + synthesizeKey("A", {accelKey: true}); + synthesizeKey("C", {accelKey: true}); + let actual = SpecialPowers.getClipboardData("text/unicode"); + actual = actual.replace(/\(pid \d+\)/g, "(pid NNN)"); + + if (actual.trim() === aExpected.trim()) { + SimpleTest.ok(true, "Clipboard has the expected contents"); + aNext(); + } else { + numFailures++; + if (numFailures === maxFailures) { + ok(false, "pasted text doesn't match"); + dump("******EXPECTED******\n"); + dump(aExpected); + dump("*******ACTUAL*******\n"); + dump(actual); + dump("********************\n"); + finish(); + } else { + setTimeout(copyPasteAndCheck, 100); + } + } + } + copyPasteAndCheck(); + } + + if (!aFilename2) { + function loadAndCheck() { + let fileInput1 = + frame.contentWindow.document.getElementById("fileInput1"); + fileInput1.value = filePath; // this works because it's a chrome test + + fileInput1.dispatchEvent(e); + check(); + } + + if (aDumpFirst) { + let dumper = Cc["@mozilla.org/memory-info-dumper;1"]. + getService(Ci.nsIMemoryInfoDumper); + dumper.dumpMemoryReportsToNamedFile(filePath, loadAndCheck, null, + /* anonymize = */ false); + } else { + loadAndCheck(); + } + + } else { + let fileInput2 = + frame.contentWindow.document.getElementById("fileInput2"); + fileInput2.value = filePath; // this works because it's a chrome test + + // Hack alert: fileInput2's onchange handler calls fileInput2.click(). + // But we don't want that to happen, because we want to bypass the file + // picker for the test. So we set |e.skipClick|, which causes + // fileInput2.click() to be skipped, and dispatch the second change event + // directly ourselves. + + e.skipClick = true; + fileInput2.dispatchEvent(e); + + let filePath2 = getFilePath(aFilename2); + fileInput2.value = filePath2; // this works because it's a chrome test + + let e2 = document.createEvent('Event'); + e2.initEvent('change', true, true); + fileInput2.dispatchEvent(e); + + check(); + } + } + + // Returns a function that chains together multiple test() calls. + function chain(aPieces) { + let x = aPieces.shift(); + if (x) { + return function() { test(x.filename, x.filename2, x.expected, x.dumpFirst, x.verbose, chain(aPieces)); } + } else { + return function() { finish(); }; + } + } + + let expectedGood = +"\ +Explicit-only process\n\ +\n\ +WARNING: the 'heap-allocated' memory reporter does not work for this platform and/or configuration. This means that 'heap-unclassified' is not shown and the 'explicit' tree shows less memory than it should.\n\ +Explicit Allocations\n\ +\n\ +100,000 B (100.0%) -- explicit\n\ +└──100,000 B (100.0%) ── a/b\n\ +\n\ +Other Measurements\n\ +\n\ +End of Explicit-only process\n\ +Heap-unclassified process\n\ +Explicit Allocations\n\ +\n\ +262,144,000 B (100.0%) -- explicit\n\ +├──209,715,200 B (80.00%) ── heap-unclassified\n\ +└───52,428,800 B (20.00%) ── a/b\n\ +\n\ +Other Measurements\n\ +\n\ +262,144,000 B ── heap-allocated\n\ +\n\ +End of Heap-unclassified process\n\ +Main Process (pid NNN)\n\ +Explicit Allocations\n\ +\n\ +262,144,000 B (100.0%) -- explicit\n\ +├──209,715,200 B (80.00%) ── heap-unclassified\n\ +└───52,428,800 B (20.00%) ── a/b\n\ +\n\ +Other Measurements\n\ +\n\ +1,024 B (100.0%) -- compartments\n\ +└──1,024 B (100.0%) ── system/a\n\ +\n\ +1,024 B (100.0%) -- ghost-windows\n\ +└──1,024 B (100.0%) ── a\n\ +\n\ +314,572 B (100.0%) -- other\n\ +├──209,715 B (66.67%) ── a\n\ +└──104,857 B (33.33%) ── b\n\ +\n\ +1,024 B (100.0%) -- pss\n\ +└──1,024 B (100.0%) ── a\n\ +\n\ +1,024 B (100.0%) -- rss\n\ +└──1,024 B (100.0%) ── a\n\ +\n\ +1,024 B (100.0%) -- size\n\ +└──1,024 B (100.0%) ── a\n\ +\n\ +1,024 B (100.0%) -- swap\n\ +└──1,024 B (100.0%) ── a\n\ +\n\ +262,144,000 B ── heap-allocated\n\ +\n\ +End of Main Process (pid NNN)\n\ +Other-only process\n\ +Other Measurements\n\ +\n\ +200,000 B (100.0%) -- a\n\ +├──100,000 B (50.00%) ── b\n\ +└──100,000 B (50.00%) ── c\n\ +\n\ +500,000 B ── heap-allocated\n\ +\n\ +End of Other-only process\n\ +"; + + let expectedGood2 = +"\ +Main Process (pid NNN)\n\ +Explicit Allocations\n\ +\n\ +262,144,000 B (100.0%) -- explicit\n\ +├──209,715,200 B (80.00%) ── heap-unclassified\n\ +└───52,428,800 B (20.00%) ── a/b\n\ +\n\ +Other Measurements\n\ +\n\ +314,572 B (100.0%) -- other\n\ +├──209,715 B (66.67%) ── a\n\ +└──104,857 B (33.33%) ── b\n\ +\n\ +262,144,000 B ── heap-allocated\n\ +\n\ +End of Main Process (pid NNN)\n\ +"; + + // This is the output for a malformed data file. + let expectedBad = +"\ +Error: Invalid memory report(s): missing 'hasMozMallocUsableSize' property\ +"; + + // This is the output for a non-verbose diff. + let expectedDiffNonVerbose = +"\ +P\n\ +\n\ +WARNING: the 'heap-allocated' memory reporter does not work for this platform and/or configuration. This means that 'heap-unclassified' is not shown and the 'explicit' tree shows less memory than it should.\n\ +Explicit Allocations\n\ +\n\ +-0.01 MB (100.0%) -- explicit\n\ +├──-0.01 MB (99.95%) ── storage/prefixset/goog-phish-shavar\n\ +└──-0.00 MB (00.05%) ++ (2 tiny)\n\ +\n\ +Other Measurements\n\ +\n\ +0.96 MB (100.0%) -- a\n\ +├──0.95 MB (99.80%) ── b\n\ +├──0.00 MB (00.10%) -- c\n\ +│ ├──-0.95 MB (-99.70%) ── e\n\ +│ ├──0.95 MB (99.60%) ── d\n\ +│ └──0.00 MB (00.20%) ++ (2 tiny)\n\ +└──0.00 MB (00.10%) ── h\n\ +\n\ + 0.00 MB ── canvas-2d-pixel-bytes [2] [+]\n\ +-0.00 MB ── foobar [-]\n\ +\n\ +End of P\n\ +P2 (pid NNN)\n\ +\n\ +WARNING: the 'heap-allocated' memory reporter does not work for this platform and/or configuration. This means that 'heap-unclassified' is not shown and the 'explicit' tree shows less memory than it should.\n\ +Explicit Allocations\n\ +\n\ +0.00 MB (100.0%) -- explicit\n\ +└──0.00 MB (100.0%) ── window-objects/top(bar.com, id=NNN)/...\n\ +\n\ +Other Measurements\n\ +\n\ +0.00 MB (100.0%) -- p3\n\ +└──0.00 MB (100.0%) ── zone(0xNNN)/p3\n\ +\n\ +0.00 MB (100.0%) -- p4\n\ +└──0.00 MB (100.0%) ── js-zone(0xNNN)/p4\n\ +\n\ +0.00 MB (100.0%) -- p5\n\ +└──0.00 MB (100.0%) ── worker(foo.com, 0xNNN)/p5\n\ +\n\ +0.00 MB (100.0%) -- p6\n\ +└──0.00 MB (100.0%) ── z-moz-nullprincipal:{NNNNNNNN-NNNN-NNNN-NNNN-NNNNNNNNNNNN}/p6\n\ +\n\ +0.00 MB (100.0%) -- p7\n\ +└──0.00 MB (100.0%) ── js-main-runtime-compartments/system/jar:file:///.../omni.ja!/p7\n\ +\n\ +0.00 MB ── p1 (pid NNN)\n\ +0.00 MB ── p2 (blah, pid=NNN)\n\ +\n\ +End of P2 (pid NNN)\n\ +P3\n\ +Other Measurements\n\ +\n\ +-0.00 MB ── p3 [-]\n\ +\n\ +End of P3\n\ +P4\n\ +Other Measurements\n\ +\n\ +0.00 MB ── p4 [+]\n\ +\n\ +End of P4\n\ +P7\n\ +Other Measurements\n\ +\n\ +0.00 MB (100.0%) -- p7\n\ +├──0.00 MB (57.14%) ── c [+]\n\ +└──0.00 MB (42.86%) ── b [+]\n\ +\n\ +-0.00 MB ── p7 [-]\n\ +\n\ +End of P7\n\ +P8\n\ +Other Measurements\n\ +\n\ +-0.00 MB (100.0%) -- p8\n\ +└──-0.00 MB (100.0%) -- a\n\ + ├──-0.00 MB (50.00%) -- b\n\ + │ ├──-0.00 MB (31.82%) -- c\n\ + │ │ ├──-0.00 MB (18.18%) ── e [-]\n\ + │ │ └──-0.00 MB (13.64%) ── d [-]\n\ + │ ├──-0.00 MB (22.73%) ── f [-]\n\ + │ └───0.00 MB (-4.55%) ── (fake child) [!]\n\ + └──-0.00 MB (50.00%) -- g\n\ + ├──-0.00 MB (31.82%) ── i [-]\n\ + ├──-0.00 MB (27.27%) ── h [-]\n\ + └───0.00 MB (-9.09%) ── (fake child) [!]\n\ +\n\ +End of P8\n\ +"; + + // This is the output for a verbose diff. + let expectedDiffVerbose = +"\ +P\n\ +\n\ +WARNING: the 'heap-allocated' memory reporter does not work for this platform and/or configuration. This means that 'heap-unclassified' is not shown and the 'explicit' tree shows less memory than it should.\n\ +Explicit Allocations\n\ +\n\ +-10,005 B (100.0%) -- explicit\n\ +├──-10,000 B (99.95%) ── storage/prefixset/goog-phish-shavar\n\ +├───────-6 B (00.06%) ── spell-check [2]\n\ +└────────1 B (-0.01%) ── xpcom/category-manager\n\ +\n\ +Other Measurements\n\ +\n\ +1,002,000 B (100.0%) -- a\n\ +├──1,000,000 B (99.80%) ── b\n\ +├──────1,000 B (00.10%) -- c\n\ +│ ├──-999,000 B (-99.70%) ── e\n\ +│ ├──998,000 B (99.60%) ── d\n\ +│ ├──1,000 B (00.10%) ── f\n\ +│ └──1,000 B (00.10%) ── g\n\ +└──────1,000 B (00.10%) ── h\n\ +\n\ +3,000 B ── canvas-2d-pixel-bytes [2] [+]\n\ + -100 B ── foobar [-]\n\ +\n\ +End of P\n\ +P2 (pid NNN)\n\ +\n\ +WARNING: the 'heap-allocated' memory reporter does not work for this platform and/or configuration. This means that 'heap-unclassified' is not shown and the 'explicit' tree shows less memory than it should.\n\ +Explicit Allocations\n\ +\n\ +11 B (100.0%) -- explicit\n\ +└──11 B (100.0%) ── window-objects/top(bar.com, id=NNN)/...\n\ +\n\ +Other Measurements\n\ +\n\ +11 B (100.0%) -- p3\n\ +└──11 B (100.0%) ── zone(0xNNN)/p3\n\ +\n\ +11 B (100.0%) -- p4\n\ +└──11 B (100.0%) ── js-zone(0xNNN)/p4\n\ +\n\ +11 B (100.0%) -- p5\n\ +└──11 B (100.0%) ── worker(foo.com, 0xNNN)/p5\n\ +\n\ +11 B (100.0%) -- p6\n\ +└──11 B (100.0%) ── z-moz-nullprincipal:{NNNNNNNN-NNNN-NNNN-NNNN-NNNNNNNNNNNN}/p6\n\ +\n\ +11 B (100.0%) -- p7\n\ +└──11 B (100.0%) ── js-main-runtime-compartments/system/jar:file:///.../omni.ja!/p7\n\ +\n\ +11 B ── p1 (pid NNN)\n\ +11 B ── p2 (blah, pid=NNN)\n\ +\n\ +End of P2 (pid NNN)\n\ +P3\n\ +Other Measurements\n\ +\n\ +-55 B ── p3 [-]\n\ +\n\ +End of P3\n\ +P4\n\ +Other Measurements\n\ +\n\ +66 B ── p4 [+]\n\ +\n\ +End of P4\n\ +P7\n\ +Other Measurements\n\ +\n\ +7 B (100.0%) -- p7\n\ +├──4 B (57.14%) ── c [+]\n\ +└──3 B (42.86%) ── b [+]\n\ +\n\ +-5 B ── p7 [-]\n\ +\n\ +End of P7\n\ +P8\n\ +Other Measurements\n\ +\n\ +-22 B (100.0%) -- p8\n\ +└──-22 B (100.0%) -- a\n\ + ├──-11 B (50.00%) -- b\n\ + │ ├───-7 B (31.82%) -- c\n\ + │ │ ├──-4 B (18.18%) ── e [-]\n\ + │ │ └──-3 B (13.64%) ── d [-]\n\ + │ ├───-5 B (22.73%) ── f [-]\n\ + │ └────1 B (-4.55%) ── (fake child) [!]\n\ + └──-11 B (50.00%) -- g\n\ + ├───-7 B (31.82%) ── i [-]\n\ + ├───-6 B (27.27%) ── h [-]\n\ + └────2 B (-9.09%) ── (fake child) [!]\n\ +\n\ +End of P8\n\ +"; + + // This is the output for the crash reports diff. + let expectedDiff2 = +"\ +Main Process (pid NNN)\n\ +Other Measurements\n\ +\n\ +1 B ── heap-allocated\n\ +\n\ +End of Main Process (pid NNN)\n\ +"; + + let frames = [ + // This loads a pre-existing memory reports file that is valid. + { filename: "memory-reports-good.json", expected: expectedGood, dumpFirst: false, verbose: true }, + + // This loads a pre-existing crash dump file that is valid. + { filename: "crash-dump-good.json", expected: expectedGood2, dumpFirst: false, verbose: true }, + + // This dumps to a file and then reads it back in. (The result is the same + // as the previous test.) + { filename: "memory-reports-dumped.json.gz", expected: expectedGood2, dumpFirst: true, verbose: true }, + + // This loads a pre-existing file that is invalid. + { filename: "memory-reports-bad.json", expected: expectedBad, dumpFirst: false, verbose: true }, + + // This diffs two pre-existing memory reports files. + { filename: "memory-reports-diff1.json", filename2: "memory-reports-diff2.json", expected: expectedDiffNonVerbose, dumpFirst: false, verbose: false }, + + // Ditto. + { filename: "memory-reports-diff1.json", filename2: "memory-reports-diff2.json", expected: expectedDiffVerbose, dumpFirst: false, verbose: true }, + + // This diffs two pre-existing crash report files. + { filename: "crash-dump-diff1.json", filename2: "crash-dump-diff2.json", expected: expectedDiff2, dumpFirst: false, verbose: true } + ]; + + SimpleTest.waitForFocus(chain(frames)); + + SimpleTest.waitForExplicitFinish(); + ]]> + </script> +</window> diff --git a/toolkit/components/aboutmemory/tests/test_aboutmemory4.xul b/toolkit/components/aboutmemory/tests/test_aboutmemory4.xul new file mode 100644 index 000000000..f2c752ac5 --- /dev/null +++ b/toolkit/components/aboutmemory/tests/test_aboutmemory4.xul @@ -0,0 +1,179 @@ +<?xml version="1.0"?> +<?xml-stylesheet type="text/css" href="chrome://global/skin"?> +<?xml-stylesheet type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"?> +<window title="about:memory" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/> + <script type="text/javascript" src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script> + + <!-- This file tests the loading of memory reports from file when specified + in about:memory's URL (via the "file=" suffix). --> + + <!-- test results are displayed in the html:body --> + <body xmlns="http://www.w3.org/1999/xhtml"></body> + + <!-- test code goes here --> + <script type="application/javascript"> + <![CDATA[ + "use strict"; + + function makePathname(aFilename) { + let file = Components.classes["@mozilla.org/file/directory_service;1"] + .getService(Components.interfaces.nsIProperties) + .get("CurWorkD", Components.interfaces.nsIFile); + file.append("chrome"); + file.append("toolkit"); + file.append("components"); + file.append("aboutmemory"); + file.append("tests"); + file.append(aFilename); + return file.path; + } + + // Load the given file into the frame, then copy+paste the entire frame and + // check that the cut text matches what we expect. + function test(aFilename, aExpected, aNext) { + let frame = document.createElementNS("http://www.w3.org/1999/xhtml", "iframe") + frame.height = 300; + frame.src = "about:memory?file=" + makePathname(aFilename); + document.documentElement.appendChild(frame); + frame.focus(); + + // Initialize the clipboard contents. + SpecialPowers.clipboardCopyString("initial clipboard value"); + + let numFailures = 0, maxFailures = 30; + + // Because the file load is async, we don't know when it will finish and + // the output will show up. So we poll. + function copyPasteAndCheck() { + // Copy and paste frame contents, and filter out non-deterministic + // differences. + synthesizeKey("A", {accelKey: true}); + synthesizeKey("C", {accelKey: true}); + let actual = SpecialPowers.getClipboardData("text/unicode"); + actual = actual.replace(/\(pid \d+\)/, "(pid NNN)"); + + if (actual.trim() === aExpected.trim()) { + SimpleTest.ok(true, "Clipboard has the expected contents"); + aNext(); + } else { + numFailures++; + if (numFailures === maxFailures) { + ok(false, "pasted text doesn't match"); + dump("******EXPECTED******\n"); + dump(aExpected); + dump("*******ACTUAL*******\n"); + dump(actual); + dump("********************\n"); + SimpleTest.finish(); + } else { + setTimeout(copyPasteAndCheck, 100); + } + } + } + copyPasteAndCheck(); + } + + // Returns a function that chains together multiple test() calls. + function chain(aFrameIds) { + let x = aFrameIds.shift(); + if (x) { + return function() { test(x.filename, x.expected, chain(aFrameIds)); } + } else { + return function() { SimpleTest.finish(); }; + } + } + + // This is pretty simple output, but that's ok; this file is about testing + // the loading of data from file. If we got this far, we're doing fine. + let expectedGood = +"\ +Explicit-only process\n\ +\n\ +WARNING: the 'heap-allocated' memory reporter does not work for this platform and/or configuration. This means that 'heap-unclassified' is not shown and the 'explicit' tree shows less memory than it should.\n\ +Explicit Allocations\n\ +\n\ +0.10 MB (100.0%) -- explicit\n\ +└──0.10 MB (100.0%) ── a/b\n\ +\n\ +Other Measurements\n\ +\n\ +End of Explicit-only process\n\ +Heap-unclassified process\n\ +Explicit Allocations\n\ +\n\ +250.00 MB (100.0%) -- explicit\n\ +├──200.00 MB (80.00%) ── heap-unclassified\n\ +└───50.00 MB (20.00%) ── a/b\n\ +\n\ +Other Measurements\n\ +\n\ +250.00 MB ── heap-allocated\n\ +\n\ +End of Heap-unclassified process\n\ +Main Process (pid NNN)\n\ +Explicit Allocations\n\ +\n\ +250.00 MB (100.0%) -- explicit\n\ +├──200.00 MB (80.00%) ── heap-unclassified\n\ +└───50.00 MB (20.00%) ── a/b\n\ +\n\ +Other Measurements\n\ +\n\ +0.00 MB (100.0%) -- compartments\n\ +└──0.00 MB (100.0%) ── system/a\n\ +\n\ +0.00 MB (100.0%) -- ghost-windows\n\ +└──0.00 MB (100.0%) ── a\n\ +\n\ +0.30 MB (100.0%) -- other\n\ +├──0.20 MB (66.67%) ── a\n\ +└──0.10 MB (33.33%) ── b\n\ +\n\ +0.00 MB (100.0%) -- pss\n\ +└──0.00 MB (100.0%) ── a\n\ +\n\ +0.00 MB (100.0%) -- rss\n\ +└──0.00 MB (100.0%) ── a\n\ +\n\ +0.00 MB (100.0%) -- size\n\ +└──0.00 MB (100.0%) ── a\n\ +\n\ +0.00 MB (100.0%) -- swap\n\ +└──0.00 MB (100.0%) ── a\n\ +\n\ +250.00 MB ── heap-allocated\n\ +\n\ +End of Main Process (pid NNN)\n\ +Other-only process\n\ +Other Measurements\n\ +\n\ +0.19 MB (100.0%) -- a\n\ +├──0.10 MB (50.00%) ── b\n\ +└──0.10 MB (50.00%) ── c\n\ +\n\ +0.48 MB ── heap-allocated\n\ +\n\ +End of Other-only process\n\ +"; + + // This is the output for a malformed data file. + let expectedBad = +"\ +Error: Invalid memory report(s): missing 'hasMozMallocUsableSize' property"; + + let frames = [ + // This loads a pre-existing file that is valid. + { filename: "memory-reports-good.json", expected: expectedGood }, + + // This loads a pre-existing file that is valid. + { filename: "memory-reports-bad.json", expected: expectedBad } + ]; + + SimpleTest.waitForFocus(chain(frames)); + + SimpleTest.waitForExplicitFinish(); + ]]> + </script> +</window> diff --git a/toolkit/components/aboutmemory/tests/test_aboutmemory5.xul b/toolkit/components/aboutmemory/tests/test_aboutmemory5.xul new file mode 100644 index 000000000..2fec803b9 --- /dev/null +++ b/toolkit/components/aboutmemory/tests/test_aboutmemory5.xul @@ -0,0 +1,167 @@ +<?xml version="1.0"?> +<?xml-stylesheet type="text/css" href="chrome://global/skin"?> +<?xml-stylesheet type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"?> +<window title="about:memory" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/> + <script type="text/javascript" src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script> + + <!-- This file tests the saving and loading of memory reports to/from file in + about:memory in the presence of child processes. It is also notable + for being an about:memory test that uses the real reporters, rather + than fake deterministic ones, and so tends to show up problems in the + real reporters (like bogus negative values). --> + + <!-- test results are displayed in the html:body --> + <body xmlns="http://www.w3.org/1999/xhtml"></body> + + <iframe id="amFrame" height="400" src="about:memory"></iframe> + + <script type="application/javascript"> + <![CDATA[ + "use strict"; + + const Cc = Components.classes; + const Ci = Components.interfaces; + let mgr = Cc["@mozilla.org/memory-reporter-manager;1"]. + getService(Ci.nsIMemoryReporterManager); + + let numRemotes = 3; + let numReady = 0; + + // Create some remote processes, and set up message-passing so that + // we know when each child is fully initialized. + let remotes = []; + + let prefs = [ + ["dom.ipc.processCount", 3], // Allow up to 3 child processes + ["memory.report_concurrency", 2], // Cover more child handling cases + ["memory.system_memory_reporter", true] // Test SystemMemoryReporter + ]; + + SpecialPowers.pushPrefEnv({"set": prefs}, function() { + for (let i = 0; i < numRemotes; i++) { + let w = remotes[i] = window.open("remote.xul", "", "chrome"); + + w.addEventListener("load", function loadHandler() { + w.removeEventListener("load", loadHandler); + let remoteBrowser = w.document.getElementById("remote"); + let mm = remoteBrowser.messageManager; + mm.addMessageListener("test:ready", function readyHandler() { + mm.removeMessageListener("test:ready", readyHandler); + numReady++; + if (numReady == numRemotes) { + // All the remote processes are ready. + SimpleTest.waitForFocus(onFocus); + } + }); + mm.loadFrameScript("data:," + encodeURI("sendAsyncMessage('test:ready');"), true); + }); + } + }); + + // Load the given file into the frame, then copy+paste the entire frame and + // check that the cut text matches what we expect. + function onFocus() { + let frame = document.getElementById("amFrame"); + frame.focus(); + + function getFilePath(aFilename) { + let file = Cc["@mozilla.org/file/directory_service;1"] + .getService(Components.interfaces.nsIProperties) + .get("CurWorkD", Components.interfaces.nsIFile); + file.append("chrome"); + file.append("toolkit"); + file.append("components"); + file.append("aboutmemory"); + file.append("tests"); + file.append(aFilename); + return file.path; + } + + let filePath = getFilePath("memory-reports-dumped.json.gz"); + + let e = document.createEvent('Event'); + e.initEvent('change', true, true); + + let dumper = Cc["@mozilla.org/memory-info-dumper;1"]. + getService(Ci.nsIMemoryInfoDumper); + dumper.dumpMemoryReportsToNamedFile(filePath, loadAndCheck, null, + /* anonymize = */ false); + + function loadAndCheck() { + // Load the file. + let fileInput1 = + frame.contentWindow.document.getElementById("fileInput1"); + fileInput1.value = filePath; // this works because it's a chrome test + fileInput1.dispatchEvent(e); + + // Initialize the clipboard contents. + SpecialPowers.clipboardCopyString("initial clipboard value"); + + let numFailures = 0, maxFailures = 30; + + copyPasteAndCheck(); + + // Because the file load is async, we don't know when it will finish and + // the output will show up. So we poll. + function copyPasteAndCheck() { + // Copy and paste frame contents, and filter out non-deterministic + // differences. + synthesizeKey("A", {accelKey: true}); + synthesizeKey("C", {accelKey: true}); + let actual = SpecialPowers.getClipboardData("text/unicode"); + + // If we have more than 1000 chars, we've probably successfully + // copy+pasted. + if (actual.length > 1000) { + + let good = true; + + if (actual.match("End of System")) { + let m1 = actual.match("anonymous") && + actual.match("shared-libraries"); + ok(m1, "system-wide reporter") + good = good && !!m1; + } + + // Note: Match "vsize" but not "vsize-max-contiguous". + let vsizes = actual.match(/vsize[^-]/g); + let endOfBrowsers = actual.match(/End of Browser/g); + if (endOfBrowsers == null) { + endOfBrowsers = actual.match(/End of Web Content/g); + } + let m2 = (vsizes.length == 4 && endOfBrowsers.length == 3); + ok(m2, "three child processes present in loaded data"); + good = good && !!m2; + + if (!good) { + dump("*******ACTUAL*******\n"); + dump(actual); + dump("********************\n"); + } + + // Close the remote processes. + for (let i = 0; i < numRemotes; i++) { + remotes[i].close(); + } + + SimpleTest.finish(); + + } else { + numFailures++; + if (numFailures === maxFailures) { + ok(false, "not enough chars in pasted output"); + SimpleTest.finish(); + } else { + setTimeout(copyPasteAndCheck, 100); + } + } + } + } + } + + SimpleTest.waitForExplicitFinish(); + ]]> + </script> +</window> diff --git a/toolkit/components/aboutmemory/tests/test_aboutmemory6.xul b/toolkit/components/aboutmemory/tests/test_aboutmemory6.xul new file mode 100644 index 000000000..365f99091 --- /dev/null +++ b/toolkit/components/aboutmemory/tests/test_aboutmemory6.xul @@ -0,0 +1,88 @@ +<?xml version="1.0"?> +<?xml-stylesheet type="text/css" href="chrome://global/skin"?> +<?xml-stylesheet type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"?> +<window title="about:memory" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/> + <script type="text/javascript" src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script> + + <!-- This file tests the saving of GC and CC logs in both concise and + verbose formats. --> + + <!-- test results are displayed in the html:body --> + <body xmlns="http://www.w3.org/1999/xhtml"></body> + + <iframe id="amFrame" height="400" src="about:memory"></iframe> + + <script type="application/javascript"> + <![CDATA[ + "use strict"; + + const Cc = Components.classes; + const Ci = Components.interfaces; + + function onFocus() { + let frame = document.getElementById("amFrame"); + frame.focus(); + + // Checks that a file exists on the local file system and removes it if it + // is present. + function checkForFileAndRemove(aFilename) { + let localFile = Cc["@mozilla.org/file/local;1"] + .createInstance(Ci.nsILocalFile); + localFile.initWithPath(aFilename); + + let exists = localFile.exists(); + if (exists) { + localFile.remove(/* recursive = */ false); + } + + return exists; + } + + // Given a save log button, triggers the action and checks if both CC & GC + // logs were written to disk. + function saveLogs(aLogButton, aCCLogType) + { + // trigger the log saving + aLogButton.click(); + + // mainDiv + // |-> section + // | -> div gc log path + // | -> div cc log path + let mainDiv = frame.contentWindow.document.getElementById("mainDiv"); + let logNodes = mainDiv.childNodes[0]; + + // we expect 2 logs listed + let numOfLogs = logNodes.childNodes.length; + ok(numOfLogs == 2, "two log entries generated") + + // grab the path portion of the text + let gcLogPath = logNodes.childNodes[0].textContent + .replace("Saved GC log to ", ""); + let ccLogPath = logNodes.childNodes[1].textContent + .replace("Saved " + aCCLogType + " CC log to ", ""); + + // check that the files actually exist + ok(checkForFileAndRemove(gcLogPath), "GC log file exists"); + ok(checkForFileAndRemove(ccLogPath), "CC log file exists"); + } + + // get the log buttons to test + let saveConcise = frame.contentWindow.document + .getElementById("saveLogsConcise"); + let saveVerbose = frame.contentWindow.document + .getElementById("saveLogsVerbose"); + + saveLogs(saveConcise, "concise"); + saveLogs(saveVerbose, "verbose"); + + SimpleTest.finish(); + } + + SimpleTest.waitForFocus(onFocus); + SimpleTest.waitForExplicitFinish(); + ]]> + </script> +</window> diff --git a/toolkit/components/aboutmemory/tests/test_dumpGCAndCCLogsToFile.xul b/toolkit/components/aboutmemory/tests/test_dumpGCAndCCLogsToFile.xul new file mode 100644 index 000000000..a39869b7d --- /dev/null +++ b/toolkit/components/aboutmemory/tests/test_dumpGCAndCCLogsToFile.xul @@ -0,0 +1,98 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> +<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" + type="text/css"?> +<window title="GC/CC logging with child processes" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + <script type="application/javascript" + src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/> + + <!-- test results are displayed in the html:body --> + <body xmlns="http://www.w3.org/1999/xhtml"> + </body> + + <!-- test code goes here --> + <script type="application/javascript"><![CDATA[ + + const Cc = Components.classes; + const Ci = Components.interfaces; + + SimpleTest.waitForExplicitFinish(); + + let numRemotes = 3; + let numReady = 0; + + // Create some remote processes, and set up message-passing so that + // we know when each child is fully initialized. + let remotes = []; + SpecialPowers.pushPrefEnv({"set": [["dom.ipc.processCount", numRemotes]]}, + function() { + for (let i = 0; i < numRemotes; i++) { + let w = remotes[i] = window.open("remote.xul", "", "chrome"); + + w.addEventListener("load", function loadHandler() { + w.removeEventListener("load", loadHandler); + let remoteBrowser = w.document.getElementById("remote"); + let mm = remoteBrowser.messageManager; + mm.addMessageListener("test:ready", function readyHandler() { + mm.removeMessageListener("test:ready", readyHandler); + numReady++; + if (numReady == numRemotes) { + // All the remote processes are ready. Run test. + runTest(); + } + }); + mm.loadFrameScript("data:," + encodeURI("sendAsyncMessage('test:ready');"), true); + }); + } + }); + + let dumper = Cc["@mozilla.org/memory-info-dumper;1"]. + getService(Ci.nsIMemoryInfoDumper); + + function runTest() + { + let numParents = 0; + let numChildren = 0; + dumper.dumpGCAndCCLogsToFile( + /* identifier: */ "test." + Date.now(), + /* allTraces: */ false, + /* childProcesses: */ true, + { + onDump: function(gcLog, ccLog, isParent) { + if (isParent) { + numParents++; + } else { + numChildren++; + } + checkAndRemoveLog(gcLog); + checkAndRemoveLog(ccLog); + }, + onFinish: function() { + is(numParents, 1, + "GC/CC logs for the parent process"); + is(numChildren, numRemotes, + "GC/CC logs for each child process"); + cleanUpAndFinish(); + } + }); + } + + function cleanUpAndFinish() { + // Close the remote processes. + for (let i = 0; i < numRemotes; i++) { + remotes[i].close(); + } + SimpleTest.finish(); + } + + function checkAndRemoveLog(logFile) { + let name = logFile.path; + ok(logFile.exists(), "log file "+name+" exists"); + ok(logFile.isFile(), "log file "+name+" is a regular file"); + ok(logFile.fileSize > 0, "log file "+name+" is not empty"); + logFile.remove(/* recursive: */ false); + } + + ]]></script> +</window> diff --git a/toolkit/components/aboutmemory/tests/test_memoryReporters.xul b/toolkit/components/aboutmemory/tests/test_memoryReporters.xul new file mode 100644 index 000000000..9d56890b3 --- /dev/null +++ b/toolkit/components/aboutmemory/tests/test_memoryReporters.xul @@ -0,0 +1,424 @@ +<?xml version="1.0"?> +<?xml-stylesheet type="text/css" href="chrome://global/skin"?> +<?xml-stylesheet type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"?> +<window title="Memory reporters" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + <script type="application/javascript" + src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/> + + <!-- This file tests (in a rough fashion) whether the memory reporters are + producing sensible results. test_aboutmemory.xul tests the + presentation of memory reports in about:memory. --> + + <!-- test results are displayed in the html:body --> + <body xmlns="http://www.w3.org/1999/xhtml"> + <!-- In bug 773533, <marquee> elements crashed the JS memory reporter --> + <marquee>Marquee</marquee> + </body> + + <!-- some URIs that should be anonymized in anonymous mode --> + <iframe id="amFrame" height="200" src="http://example.org:80"></iframe> + <iframe id="amFrame" height="200" src="https://example.com:443"></iframe> + + <!-- test code goes here --> + <script type="application/javascript"> + <![CDATA[ + + "use strict"; + + const Cc = Components.classes; + const Ci = Components.interfaces; + const Cr = Components.results; + + const NONHEAP = Ci.nsIMemoryReporter.KIND_NONHEAP; + const HEAP = Ci.nsIMemoryReporter.KIND_HEAP; + const OTHER = Ci.nsIMemoryReporter.KIND_OTHER; + + const BYTES = Ci.nsIMemoryReporter.UNITS_BYTES; + const COUNT = Ci.nsIMemoryReporter.UNITS_COUNT; + const COUNT_CUMULATIVE = Ci.nsIMemoryReporter.UNITS_COUNT_CUMULATIVE; + const PERCENTAGE = Ci.nsIMemoryReporter.UNITS_PERCENTAGE; + + // Use backslashes instead of forward slashes due to memory reporting's hacky + // handling of URLs. + const XUL_NS = + "http:\\\\www.mozilla.org\\keymaster\\gatekeeper\\there.is.only.xul"; + + SimpleTest.waitForExplicitFinish(); + + let vsizeAmounts = []; + let residentAmounts = []; + let heapAllocatedAmounts = []; + let storageSqliteAmounts = []; + + let jsGcHeapUsedGcThingsTotal = 0; + let jsGcHeapUsedGcThings = {}; + + let present = {} + + // Generate a long, random string. We'll check that this string is + // reported in at least one of the memory reporters. + let bigString = ""; + while (bigString.length < 10000) { + bigString += Math.random(); + } + let bigStringPrefix = bigString.substring(0, 100); + + // Generate many copies of two distinctive short strings, "!)(*&" and + // "@)(*&". We'll check that these strings are reported in at least + // one of the memory reporters. + let shortStrings = []; + for (let i = 0; i < 10000; i++) { + let str = (Math.random() > 0.5 ? "!" : "@") + ")(*&"; + shortStrings.push(str); + } + + let mySandbox = Components.utils.Sandbox(document.nodePrincipal, + { sandboxName: "this-is-a-sandbox-name" }); + + function handleReportNormal(aProcess, aPath, aKind, aUnits, aAmount, + aDescription) + { + // Record the values of some notable reporters. + if (aPath === "vsize") { + vsizeAmounts.push(aAmount); + } else if (aPath === "resident") { + residentAmounts.push(aAmount); + } else if (aPath.search(/^js-main-runtime-gc-heap-committed\/used\/gc-things\//) >= 0) { + jsGcHeapUsedGcThingsTotal += aAmount; + jsGcHeapUsedGcThings[aPath] = (jsGcHeapUsedGcThings[aPath] | 0) + 1; + } else if (aPath === "heap-allocated") { + heapAllocatedAmounts.push(aAmount); + } else if (aPath === "storage-sqlite") { + storageSqliteAmounts.push(aAmount); + + // Check the presence of some other notable reporters. + } else if (aPath.search(/^explicit\/js-non-window\/.*compartment\(/) >= 0) { + present.jsNonWindowCompartments = true; + } else if (aPath.search(/^explicit\/window-objects\/top\(.*\/js-compartment\(/) >= 0) { + present.windowObjectsJsCompartments = true; + } else if (aPath.search(/^explicit\/storage\/sqlite\/places.sqlite/) >= 0) { + present.places = true; + } else if (aPath.search(/^explicit\/images/) >= 0) { + present.images = true; + } else if (aPath.search(/^explicit\/xpti-working-set$/) >= 0) { + present.xptiWorkingSet = true; + } else if (aPath.search(/^explicit\/atom-tables\/main$/) >= 0) { + present.atomTablesMain = true; + } else if (/\[System Principal\].*this-is-a-sandbox-name/.test(aPath)) { + // A system compartment with a location (such as a sandbox) should + // show that location. + present.sandboxLocation = true; + } else if (aPath.includes(bigStringPrefix)) { + present.bigString = true; + } else if (aPath.includes("!)(*&")) { + present.smallString1 = true; + } else if (aPath.includes("@)(*&")) { + present.smallString2 = true; + } + + // Shouldn't get any anonymized paths. + if (aPath.includes('<anonymized')) { + present.anonymizedWhenUnnecessary = aPath; + } + } + + function handleReportAnonymized(aProcess, aPath, aKind, aUnits, aAmount, + aDescription) + { + // Path might include an xmlns using http, which is safe to ignore. + let reducedPath = aPath.replace(XUL_NS, ""); + + // Shouldn't get http: or https: in any paths. + if (reducedPath.includes('http:')) { + present.httpWhenAnonymized = aPath; + } + + // file: URLs should have their path anonymized. + if (reducedPath.search('file:..[^<]') !== -1) { + present.unanonymizedFilePathWhenAnonymized = aPath; + } + } + + let mgr = Cc["@mozilla.org/memory-reporter-manager;1"]. + getService(Ci.nsIMemoryReporterManager); + + let amounts = [ + "vsize", + "vsizeMaxContiguous", + "resident", + "residentFast", + "residentPeak", + "residentUnique", + "heapAllocated", + "heapOverheadFraction", + "JSMainRuntimeGCHeap", + "JSMainRuntimeTemporaryPeak", + "JSMainRuntimeCompartmentsSystem", + "JSMainRuntimeCompartmentsUser", + "imagesContentUsedUncompressed", + "storageSQLite", + "lowMemoryEventsVirtual", + "lowMemoryEventsPhysical", + "ghostWindows", + "pageFaultsHard", + ]; + for (let i = 0; i < amounts.length; i++) { + try { + // If mgr[amounts[i]] throws an exception, just move on -- some amounts + // aren't available on all platforms. But if the attribute simply + // isn't present, that indicates the distinguished amounts have changed + // and this file hasn't been updated appropriately. + let dummy = mgr[amounts[i]]; + ok(dummy !== undefined, + "accessed an unknown distinguished amount: " + amounts[i]); + } catch (ex) { + } + } + + // Run sizeOfTab() to make sure it doesn't crash. We can't check the result + // values because they're non-deterministic. + let jsObjectsSize = {}; + let jsStringsSize = {}; + let jsOtherSize = {}; + let domSize = {}; + let styleSize = {}; + let otherSize = {}; + let totalSize = {}; + let jsMilliseconds = {}; + let nonJSMilliseconds = {}; + mgr.sizeOfTab(window, jsObjectsSize, jsStringsSize, jsOtherSize, + domSize, styleSize, otherSize, totalSize, + jsMilliseconds, nonJSMilliseconds); + + let asyncSteps = [ + getReportsNormal, + getReportsAnonymized, + checkResults, + test_register_strong, + test_register_strong, // Make sure re-registering works + test_register_weak, + SimpleTest.finish + ]; + + function runNext() { + setTimeout(asyncSteps.shift(), 0); + } + + function getReportsNormal() + { + mgr.getReports(handleReportNormal, null, + runNext, null, + /* anonymize = */ false); + } + + function getReportsAnonymized() + { + mgr.getReports(handleReportAnonymized, null, + runNext, null, + /* anonymize = */ true); + } + + function checkSizeReasonable(aName, aAmount) + { + // Check the size is reasonable -- i.e. not ridiculously large or small. + ok(100 * 1000 <= aAmount && aAmount <= 10 * 1000 * 1000 * 1000, + aName + "'s size is reasonable"); + } + + function checkSpecialReport(aName, aAmounts, aCanBeUnreasonable) + { + ok(aAmounts.length == 1, aName + " has " + aAmounts.length + " report"); + let n = aAmounts[0]; + if (!aCanBeUnreasonable) { + checkSizeReasonable(aName, n); + } + } + + function checkResults() + { + try { + // Nb: mgr.heapAllocated will throw NS_ERROR_NOT_AVAILABLE if this is a + // --disable-jemalloc build. Allow for skipping this test on that + // exception, but *only* that exception. + let dummy = mgr.heapAllocated; + checkSpecialReport("heap-allocated", heapAllocatedAmounts); + } catch (ex) { + is(ex.result, Cr.NS_ERROR_NOT_AVAILABLE, "mgr.heapAllocated exception"); + } + // vsize may be unreasonable if ASAN is enabled + checkSpecialReport("vsize", vsizeAmounts, /*canBeUnreasonable*/true); + checkSpecialReport("resident", residentAmounts); + + for (var reporter in jsGcHeapUsedGcThings) { + ok(jsGcHeapUsedGcThings[reporter] == 1); + } + checkSizeReasonable("js-main-runtime-gc-heap-committed/used/gc-things", + jsGcHeapUsedGcThingsTotal); + + ok(present.jsNonWindowCompartments, "js-non-window compartments are present"); + ok(present.windowObjectsJsCompartments, "window-objects/.../js compartments are present"); + ok(present.places, "places is present"); + ok(present.images, "images is present"); + ok(present.xptiWorkingSet, "xpti-working-set is present"); + ok(present.atomTablesMain, "atom-tables/main is present"); + ok(present.sandboxLocation, "sandbox locations are present"); + ok(present.bigString, "large string is present"); + ok(present.smallString1, "small string 1 is present"); + ok(present.smallString2, "small string 2 is present"); + + ok(!present.anonymizedWhenUnnecessary, + "anonymized paths are not present when unnecessary. Failed case: " + + present.anonymizedWhenUnnecessary); + ok(!present.httpWhenAnonymized, + "http URLs are anonymized when necessary. Failed case: " + + present.httpWhenAnonymized); + ok(!present.unanonymizedFilePathWhenAnonymized, + "file URLs are anonymized when necessary. Failed case: " + + present.unanonymizedFilePathWhenAnonymized); + + runNext(); + } + + // Reporter registration tests + + // collectReports() calls to the test reporter. + let called = 0; + + // The test memory reporter, testing the various report units. + // Also acts as a report collector, verifying the reported values match the + // expected ones after passing through XPConnect / nsMemoryReporterManager + // and back. + function MemoryReporterAndCallback() { + this.seen = 0; + } + MemoryReporterAndCallback.prototype = { + // The test reports. + // Each test key corresponds to the path of the report. |amount| is a + // function called when generating the report. |expected| is a function + // to be tested when receiving a report during collection. If |expected| is + // omitted the |amount| will be checked instead. + tests: { + "test-memory-reporter-bytes1": { + units: BYTES, + amount: () => 0 + }, + "test-memory-reporter-bytes2": { + units: BYTES, + amount: () => (1<<30) * 8 // awkward way to say 8G in JS + }, + "test-memory-reporter-counter": { + units: COUNT, + amount: () => 2 + }, + "test-memory-reporter-ccounter": { + units: COUNT_CUMULATIVE, + amount: () => ++called, + expected: () => called + }, + "test-memory-reporter-percentage": { + units: PERCENTAGE, + amount: () => 9999 + } + }, + // nsIMemoryReporter + collectReports: function(callback, data, anonymize) { + for (let path of Object.keys(this.tests)) { + try { + let test = this.tests[path]; + callback.callback( + "", // Process. Should be "" initially. + path, + OTHER, + test.units, + test.amount(), + "Test " + path + ".", + data); + } + catch (ex) { + ok(false, ex); + } + } + }, + // nsIMemoryReporterCallback + callback: function(process, path, kind, units, amount, data) { + if (path in this.tests) { + this.seen++; + let test = this.tests[path]; + ok(units === test.units, "Test reporter units match"); + ok(amount === (test.expected || test.amount)(), + "Test reporter values match: " + amount); + } + }, + // Checks that the callback has seen the expected number of reports, and + // resets the callback counter. + // @param expected Optional. Expected number of reports the callback + // should have processed. + finish: function(expected) { + if (expected === undefined) { + expected = Object.keys(this.tests).length; + } + is(expected, this.seen, + "Test reporter called the correct number of times: " + expected); + this.seen = 0; + } + }; + + // General memory reporter + registerStrongReporter tests. + function test_register_strong() { + let reporterAndCallback = new MemoryReporterAndCallback(); + // Registration works. + mgr.registerStrongReporter(reporterAndCallback); + + // Check the generated reports. + mgr.getReports(reporterAndCallback, null, + () => { + reporterAndCallback.finish(); + window.setTimeout(test_unregister_strong, 0, reporterAndCallback); + }, null, + /* anonymize = */ false); + } + + function test_unregister_strong(aReporterAndCallback) + { + mgr.unregisterStrongReporter(aReporterAndCallback); + + // The reporter was unregistered, hence there shouldn't be any reports from + // the test reporter. + mgr.getReports(aReporterAndCallback, null, + () => { + aReporterAndCallback.finish(0); + runNext(); + }, null, + /* anonymize = */ false); + } + + // Check that you cannot register JS components as weak reporters. + function test_register_weak() { + let reporterAndCallback = new MemoryReporterAndCallback(); + try { + // Should fail! nsMemoryReporterManager will only hold a raw pointer to + // "weak" reporters. When registering a weak reporter, XPConnect will + // create a WrappedJS for JS components. This WrappedJS would be + // successfully registered with the manager, only to be destroyed + // immediately after, which would eventually lead to a crash when + // collecting the reports. Therefore nsMemoryReporterManager should + // reject WrappedJS reporters, which is what is tested here. + // See bug 950391 comment #0. + mgr.registerWeakReporter(reporterAndCallback); + ok(false, "Shouldn't be allowed to register a JS component (WrappedJS)"); + } + catch (ex) { + ok(ex.message.indexOf("NS_ERROR_") >= 0, + "WrappedJS reporter got rejected: " + ex); + } + + runNext(); + } + + // Kick-off the async tests. + runNext(); + + ]]> + </script> +</window> diff --git a/toolkit/components/aboutmemory/tests/test_memoryReporters2.xul b/toolkit/components/aboutmemory/tests/test_memoryReporters2.xul new file mode 100644 index 000000000..0e8ba2e81 --- /dev/null +++ b/toolkit/components/aboutmemory/tests/test_memoryReporters2.xul @@ -0,0 +1,108 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> +<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" + type="text/css"?> +<window title="Memory reporters with child processes" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + <script type="application/javascript" + src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/> + + <!-- This file tests (in a rough fashion) whether the memory reporters are + producing sensible results in the presence of child processes. --> + + <!-- test results are displayed in the html:body --> + <body xmlns="http://www.w3.org/1999/xhtml"> + </body> + + <!-- test code goes here --> + <script type="application/javascript"><![CDATA[ + + const Cc = Components.classes; + const Ci = Components.interfaces; + + SimpleTest.waitForExplicitFinish(); + + let numRemotes = 3; + let numReady = 0; + + // Create some remote processes, and set up message-passing so that + // we know when each child is fully initialized. + let remotes = []; + SpecialPowers.pushPrefEnv({"set": [["dom.ipc.processCount", 3]]}, function() { + for (let i = 0; i < numRemotes; i++) { + let w = remotes[i] = window.open("remote.xul", "", "chrome"); + + w.addEventListener("load", function loadHandler() { + w.removeEventListener("load", loadHandler); + let remoteBrowser = w.document.getElementById("remote"); + let mm = remoteBrowser.messageManager; + mm.addMessageListener("test:ready", function readyHandler() { + mm.removeMessageListener("test:ready", readyHandler); + numReady++; + if (numReady == numRemotes) { + // All the remote processes are ready. Do memory reporting. + doReports(); + } + }); + mm.loadFrameScript("data:," + encodeURI("sendAsyncMessage('test:ready');"), true); + }); + } + }); + + let mgr = Cc["@mozilla.org/memory-reporter-manager;1"]. + getService(Ci.nsIMemoryReporterManager); + + function doReports() + { + let residents = {}; + + let handleReport = function(aProcess, aPath, aKind, aUnits, aAmount, aDesc) { + if (aPath === "resident") { + ok(100 * 1000 <= aAmount && aAmount <= 10 * 1000 * 1000 * 1000, + "resident is reasonable"); + residents[aProcess] = aAmount; + } + } + + let processReports = function() { + // First, test a failure case: calling getReports() before the previous + // getReports() has finished should silently abort. (And the arguments + // won't be used.) + mgr.getReports( + () => ok(false, "handleReport called for nested getReports() call"), + null, null, null, /* anonymize = */ false + ); + + // Close the remote processes. + for (let i = 0; i < numRemotes; i++) { + remotes[i].close(); + } + + // Check the results. + + let processes = Object.keys(residents); + ok(processes.length == numRemotes + 1, "correct resident count"); + + let numEmptyProcesses = 0, numNonEmptyProcesses = 0; + for (let i = 0; i < processes.length; i++) { + if (processes[i] == "") { + numEmptyProcesses++; + } else { + ok(processes[i].startsWith("Browser (") || processes[i].startsWith("Web Content ("), + "correct non-empty process name prefix: " + processes[i]); + numNonEmptyProcesses++; + } + } + ok(numEmptyProcesses == 1, "correct empty process name count"); + ok(numNonEmptyProcesses == numRemotes, + "correct non-empty process name count"); + + SimpleTest.finish(); + } + + mgr.getReports(handleReport, null, processReports, null, + /* anonymize = */ false); + } + + ]]></script> +</window> diff --git a/toolkit/components/aboutmemory/tests/test_sqliteMultiReporter.xul b/toolkit/components/aboutmemory/tests/test_sqliteMultiReporter.xul new file mode 100644 index 000000000..3452bbbc7 --- /dev/null +++ b/toolkit/components/aboutmemory/tests/test_sqliteMultiReporter.xul @@ -0,0 +1,54 @@ +<?xml version="1.0"?> +<?xml-stylesheet type="text/css" href="chrome://global/skin"?> +<?xml-stylesheet type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"?> +<window title="about:memory" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/> + <script type="text/javascript" src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script> + + <!-- test results are displayed in the html:body --> + <body xmlns="http://www.w3.org/1999/xhtml"></body> + + <!-- test code goes here --> + <script type="application/javascript"> + <![CDATA[ + + // Test for bug 708248, where the SQLite memory multi-reporter was + // crashing when a DB was closed. + + // Nb: this test is all JS and chould be done with an xpcshell test, + // but all the other memory reporter tests are mochitests, so it's easier + // if this one is too. + + const Cc = Components.classes; + const Ci = Components.interfaces; + const Cu = Components.utils; + + SimpleTest.waitForExplicitFinish(); + + // Make a fake DB file. + let file = Cc["@mozilla.org/file/directory_service;1"]. + getService(Ci.nsIProperties). + get("ProfD", Ci.nsIFile); + file.append("test_sqliteMultiReporter-fake-DB-tmp.sqlite"); + + // Open and close the DB. + let storage = Cc["@mozilla.org/storage/service;1"]. + getService(Ci.mozIStorageService); + let db = storage.openDatabase(file); + db.close(); + + // Invoke all the reporters. The SQLite multi-reporter is among + // them. It shouldn't crash. + let mgr = Cc["@mozilla.org/memory-reporter-manager;1"]. + getService(Ci.nsIMemoryReporterManager); + mgr.getReports(function(){}, null, + () => { + ok(true, "didn't crash"); + SimpleTest.finish(); + }, null, + /* anonymize = */ false); + + ]]> + </script> +</window> |