1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/toolkit/components/aboutmemory/tests/test_memoryReporters.xul Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,342 @@ 1.4 +<?xml version="1.0"?> 1.5 +<?xml-stylesheet type="text/css" href="chrome://global/skin"?> 1.6 +<?xml-stylesheet type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"?> 1.7 +<window title="Memory reporters" 1.8 + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> 1.9 + <script type="application/javascript" 1.10 + src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/> 1.11 + 1.12 + <!-- This file tests (in a rough fashion) whether the memory reporters are 1.13 + producing sensible results. test_aboutmemory.xul tests the 1.14 + presentation of memory reports in about:memory. --> 1.15 + 1.16 + <!-- test results are displayed in the html:body --> 1.17 + <body xmlns="http://www.w3.org/1999/xhtml"> 1.18 + <!-- In bug 773533, <marquee> elements crashed the JS memory reporter --> 1.19 + <marquee>Marquee</marquee> 1.20 + </body> 1.21 + 1.22 + <!-- test code goes here --> 1.23 + <script type="application/javascript"> 1.24 + <![CDATA[ 1.25 + 1.26 + // Nb: this test is all JS and so should be done with an xpcshell test, 1.27 + // but bug 671753 is preventing the memory-reporter-manager from being 1.28 + // accessed from xpcshell. 1.29 + 1.30 + "use strict"; 1.31 + 1.32 + const Cc = Components.classes; 1.33 + const Ci = Components.interfaces; 1.34 + const Cr = Components.results; 1.35 + 1.36 + const NONHEAP = Ci.nsIMemoryReporter.KIND_NONHEAP; 1.37 + const HEAP = Ci.nsIMemoryReporter.KIND_HEAP; 1.38 + const OTHER = Ci.nsIMemoryReporter.KIND_OTHER; 1.39 + 1.40 + const BYTES = Ci.nsIMemoryReporter.UNITS_BYTES; 1.41 + const COUNT = Ci.nsIMemoryReporter.UNITS_COUNT; 1.42 + const COUNT_CUMULATIVE = Ci.nsIMemoryReporter.UNITS_COUNT_CUMULATIVE; 1.43 + const PERCENTAGE = Ci.nsIMemoryReporter.UNITS_PERCENTAGE; 1.44 + 1.45 + let vsizeAmounts = []; 1.46 + let residentAmounts = []; 1.47 + let jsGcHeapAmounts = []; 1.48 + let heapAllocatedAmounts = []; 1.49 + let storageSqliteAmounts = []; 1.50 + 1.51 + let present = {} 1.52 + 1.53 + // Generate a long, random string. We'll check that this string is 1.54 + // reported in at least one of the memory reporters. 1.55 + let bigString = ""; 1.56 + while (bigString.length < 10000) { 1.57 + bigString += Math.random(); 1.58 + } 1.59 + let bigStringPrefix = bigString.substring(0, 100); 1.60 + 1.61 + // Generate many copies of two distinctive short strings, "!)(*&" and 1.62 + // "@)(*&". We'll check that these strings are reported in at least 1.63 + // one of the memory reporters. 1.64 + let shortStrings = []; 1.65 + for (let i = 0; i < 10000; i++) { 1.66 + let str = (Math.random() > 0.5 ? "!" : "@") + ")(*&"; 1.67 + shortStrings.push(str); 1.68 + } 1.69 + 1.70 + let mySandbox = Components.utils.Sandbox(document.nodePrincipal, 1.71 + { sandboxName: "this-is-a-sandbox-name" }); 1.72 + 1.73 + function handleReport(aProcess, aPath, aKind, aUnits, aAmount, aDescription) 1.74 + { 1.75 + // Record the values of some notable reporters. 1.76 + if (aPath === "vsize") { 1.77 + vsizeAmounts.push(aAmount); 1.78 + } else if (aPath === "resident") { 1.79 + residentAmounts.push(aAmount); 1.80 + } else if (aPath === "js-main-runtime-gc-heap-committed/used/gc-things") { 1.81 + jsGcHeapAmounts.push(aAmount); 1.82 + } else if (aPath === "heap-allocated") { 1.83 + heapAllocatedAmounts.push(aAmount); 1.84 + } else if (aPath === "storage-sqlite") { 1.85 + storageSqliteAmounts.push(aAmount); 1.86 + 1.87 + // Check the presence of some other notable reporters. 1.88 + } else if (aPath.search(/^explicit\/js-non-window\/.*compartment\(/) >= 0) { 1.89 + present.jsNonWindowCompartments = true; 1.90 + } else if (aPath.search(/^explicit\/window-objects\/top\(.*\/js-compartment\(/) >= 0) { 1.91 + present.windowObjectsJsCompartments = true; 1.92 + } else if (aPath.search(/^explicit\/storage\/sqlite\/places.sqlite/) >= 0) { 1.93 + present.places = true; 1.94 + } else if (aPath.search(/^explicit\/images/) >= 0) { 1.95 + present.images = true; 1.96 + } else if (aPath.search(/^explicit\/xpti-working-set$/) >= 0) { 1.97 + present.xptiWorkingSet = true; 1.98 + } else if (aPath.search(/^explicit\/atom-tables$/) >= 0) { 1.99 + present.atomTable = true; 1.100 + } else if (/\[System Principal\].*this-is-a-sandbox-name/.test(aPath)) { 1.101 + // A system compartment with a location (such as a sandbox) should 1.102 + // show that location. 1.103 + present.sandboxLocation = true; 1.104 + } else if (aPath.contains(bigStringPrefix)) { 1.105 + present.bigString = true; 1.106 + } else if (aPath.contains("!)(*&")) { 1.107 + present.smallString1 = true; 1.108 + } else if (aPath.contains("@)(*&")) { 1.109 + present.smallString2 = true; 1.110 + } 1.111 + } 1.112 + 1.113 + let mgr = Cc["@mozilla.org/memory-reporter-manager;1"]. 1.114 + getService(Ci.nsIMemoryReporterManager); 1.115 + 1.116 + // Access the distinguished amounts (mgr.explicit et al.) just to make sure 1.117 + // they don't crash. We can't check their actual values because they're 1.118 + // non-deterministic. 1.119 + // 1.120 + // Nb: mgr.explicit will throw NS_ERROR_NOT_AVAILABLE if this is a 1.121 + // --enable-trace-malloc build. Allow for that exception, but *only* that 1.122 + // exception. 1.123 + let dummy; 1.124 + let haveExplicit = true; 1.125 + try { 1.126 + dummy = mgr.explicit; 1.127 + } catch (ex) { 1.128 + is(ex.result, Cr.NS_ERROR_NOT_AVAILABLE, "mgr.explicit exception"); 1.129 + haveExplicit = false; 1.130 + } 1.131 + let amounts = [ 1.132 + "vsize", 1.133 + "vsizeMaxContiguous", 1.134 + "resident", 1.135 + "residentFast", 1.136 + "heapAllocated", 1.137 + "heapOverheadRatio", 1.138 + "JSMainRuntimeGCHeap", 1.139 + "JSMainRuntimeTemporaryPeak", 1.140 + "JSMainRuntimeCompartmentsSystem", 1.141 + "JSMainRuntimeCompartmentsUser", 1.142 + "imagesContentUsedUncompressed", 1.143 + "storageSQLite", 1.144 + "lowMemoryEventsVirtual", 1.145 + "lowMemoryEventsPhysical", 1.146 + "ghostWindows", 1.147 + "pageFaultsHard", 1.148 + ]; 1.149 + for (let i = 0; i < amounts.length; i++) { 1.150 + try { 1.151 + // If mgr[amounts[i]] throws an exception, just move on -- some amounts 1.152 + // aren't available on all platforms. But if the attribute simply 1.153 + // isn't present, that indicates the distinguished amounts have changed 1.154 + // and this file hasn't been updated appropriately. 1.155 + dummy = mgr[amounts[i]]; 1.156 + ok(dummy !== undefined, 1.157 + "accessed an unknown distinguished amount: " + amounts[i]); 1.158 + } catch (ex) { 1.159 + } 1.160 + } 1.161 + 1.162 + // Run sizeOfTab() to make sure it doesn't crash. We can't check the result 1.163 + // values because they're non-deterministic. 1.164 + let jsObjectsSize = {}; 1.165 + let jsStringsSize = {}; 1.166 + let jsOtherSize = {}; 1.167 + let domSize = {}; 1.168 + let styleSize = {}; 1.169 + let otherSize = {}; 1.170 + let totalSize = {}; 1.171 + let jsMilliseconds = {}; 1.172 + let nonJSMilliseconds = {}; 1.173 + mgr.sizeOfTab(window, jsObjectsSize, jsStringsSize, jsOtherSize, 1.174 + domSize, styleSize, otherSize, totalSize, 1.175 + jsMilliseconds, nonJSMilliseconds); 1.176 + 1.177 + mgr.getReportsForThisProcess(handleReport, null); 1.178 + 1.179 + function checkSpecialReport(aName, aAmounts, aCanBeUnreasonable) 1.180 + { 1.181 + ok(aAmounts.length == 1, aName + " has " + aAmounts.length + " report"); 1.182 + let n = aAmounts[0]; 1.183 + // Check the size is reasonable -- i.e. not ridiculously large or small. 1.184 + ok((100 * 1000 <= n && n <= 10 * 1000 * 1000 * 1000) || aCanBeUnreasonable, 1.185 + aName + "'s size is reasonable"); 1.186 + } 1.187 + 1.188 + // If mgr.explicit failed, we won't have "heap-allocated" either. 1.189 + if (haveExplicit) { 1.190 + checkSpecialReport("heap-allocated", heapAllocatedAmounts); 1.191 + } 1.192 + // vsize may be unreasonable if ASAN is enabled 1.193 + checkSpecialReport("vsize", vsizeAmounts, /*canBeUnreasonable*/true); 1.194 + checkSpecialReport("resident", residentAmounts); 1.195 + checkSpecialReport("js-main-runtime-gc-heap-committed/used/gc-things", jsGcHeapAmounts); 1.196 + 1.197 + ok(present.jsNonWindowCompartments, "js-non-window compartments are present"); 1.198 + ok(present.windowObjectsJsCompartments, "window-objects/.../js compartments are present"); 1.199 + ok(present.places, "places is present"); 1.200 + ok(present.images, "images is present"); 1.201 + ok(present.xptiWorkingSet, "xpti-working-set is present"); 1.202 + ok(present.atomTable, "atom-table is present"); 1.203 + ok(present.sandboxLocation, "sandbox locations are present"); 1.204 + ok(present.bigString, "large string is present"); 1.205 + ok(present.smallString1, "small string 1 is present"); 1.206 + ok(present.smallString2, "small string 2 is present"); 1.207 + 1.208 + 1.209 + // Reporter registration tests 1.210 + 1.211 + // collectReports() calls to the test reporter. 1.212 + let called = 0; 1.213 + 1.214 + // The test memory reporter, testing the various report units. 1.215 + // Also acts as a report collector, verifying the reported values match the 1.216 + // expected ones after passing through XPConnect / nsMemoryReporterManager 1.217 + // and back. 1.218 + function MemoryReporterAndCallback() { 1.219 + this.seen = 0; 1.220 + } 1.221 + MemoryReporterAndCallback.prototype = { 1.222 + // The test reports. 1.223 + // Each test key corresponds to the path of the report. |amount| is a 1.224 + // function called when generating the report. |expected| is a function 1.225 + // to be tested when receiving a report during collection. If |expected| is 1.226 + // omitted the |amount| will be checked instead. 1.227 + tests: { 1.228 + "test-memory-reporter-bytes1": { 1.229 + units: BYTES, 1.230 + amount: () => 0 1.231 + }, 1.232 + "test-memory-reporter-bytes2": { 1.233 + units: BYTES, 1.234 + amount: () => (1<<30) * 8 // awkward way to say 8G in JS 1.235 + }, 1.236 + "test-memory-reporter-counter": { 1.237 + units: COUNT, 1.238 + amount: () => 2 1.239 + }, 1.240 + "test-memory-reporter-ccounter": { 1.241 + units: COUNT_CUMULATIVE, 1.242 + amount: () => ++called, 1.243 + expected: () => called 1.244 + }, 1.245 + "test-memory-reporter-percentage": { 1.246 + units: PERCENTAGE, 1.247 + amount: () => 9999 1.248 + } 1.249 + }, 1.250 + // nsIMemoryReporter 1.251 + collectReports: function(callback, data) { 1.252 + for (let path of Object.keys(this.tests)) { 1.253 + try { 1.254 + let test = this.tests[path]; 1.255 + callback.callback( 1.256 + "", // Process. Should be "" initially. 1.257 + path, 1.258 + OTHER, 1.259 + test.units, 1.260 + test.amount(), 1.261 + "Test " + path + ".", 1.262 + data); 1.263 + } 1.264 + catch (ex) { 1.265 + ok(false, ex); 1.266 + } 1.267 + } 1.268 + }, 1.269 + // nsIMemoryReporterCallback 1.270 + callback: function(process, path, kind, units, amount, data) { 1.271 + if (path in this.tests) { 1.272 + this.seen++; 1.273 + let test = this.tests[path]; 1.274 + ok(units === test.units, "Test reporter units match"); 1.275 + ok(amount === (test.expected || test.amount)(), 1.276 + "Test reporter values match: " + amount); 1.277 + } 1.278 + }, 1.279 + // Checks that the callback has seen the expected number of reports, and 1.280 + // resets the callback counter. 1.281 + // @param expected Optional. Expected number of reports the callback 1.282 + // should have processed. 1.283 + finish: function(expected) { 1.284 + if (expected === undefined) { 1.285 + expected = Object.keys(this.tests).length; 1.286 + } 1.287 + is(expected, this.seen, 1.288 + "Test reporter called the correct number of times: " + expected); 1.289 + this.seen = 0; 1.290 + } 1.291 + }; 1.292 + 1.293 + // General memory reporter + registerStrongReporter tests. 1.294 + function test_register_strong() { 1.295 + let reporterAndCallback = new MemoryReporterAndCallback(); 1.296 + // Registration works. 1.297 + mgr.registerStrongReporter(reporterAndCallback); 1.298 + 1.299 + // Check the generated reports. 1.300 + mgr.getReportsForThisProcess(reporterAndCallback, null); 1.301 + reporterAndCallback.finish(); 1.302 + 1.303 + // Unregistration works. 1.304 + mgr.unregisterStrongReporter(reporterAndCallback); 1.305 + 1.306 + // The reporter was unregistered, hence there shouldn't be any reports from 1.307 + // the test reporter. 1.308 + mgr.getReportsForThisProcess(reporterAndCallback, null); 1.309 + reporterAndCallback.finish(0); 1.310 + } 1.311 + 1.312 + test_register_strong(); 1.313 + 1.314 + // Check strong reporters a second time, to make sure a reporter can be 1.315 + // re-registered. 1.316 + test_register_strong(); 1.317 + 1.318 + 1.319 + // Check that you cannot register JS components as weak reporters. 1.320 + function test_register_weak() { 1.321 + let reporterAndCallback = new MemoryReporterAndCallback(); 1.322 + try { 1.323 + // Should fail! nsMemoryReporterManager will only hold a raw pointer to 1.324 + // "weak" reporters. When registering a weak reporter, XPConnect will 1.325 + // create a WrappedJS for JS components. This WrappedJS would be 1.326 + // successfully registered with the manager, only to be destroyed 1.327 + // immediately after, which would eventually lead to a crash when 1.328 + // collecting the reports. Therefore nsMemoryReporterManager should 1.329 + // reject WrappedJS reporters, which is what is tested here. 1.330 + // See bug 950391 comment #0. 1.331 + mgr.registerWeakReporter(reporterAndCallback); 1.332 + ok(false, "Shouldn't be allowed to register a JS component (WrappedJS)"); 1.333 + } 1.334 + catch (ex) { 1.335 + ok(ex.message.indexOf("NS_ERROR_") >= 0, 1.336 + "WrappedJS reporter got rejected: " + ex); 1.337 + } 1.338 + } 1.339 + 1.340 + test_register_weak(); 1.341 + 1.342 + ]]> 1.343 + </script> 1.344 +</window> 1.345 +