toolkit/components/aboutmemory/tests/test_memoryReporters.xul

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

     1 <?xml version="1.0"?>
     2 <?xml-stylesheet type="text/css" href="chrome://global/skin"?>
     3 <?xml-stylesheet type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"?>
     4 <window title="Memory reporters"
     5         xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
     6   <script type="application/javascript"
     7           src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
     9   <!-- This file tests (in a rough fashion) whether the memory reporters are
    10        producing sensible results.  test_aboutmemory.xul tests the
    11        presentation of memory reports in about:memory. -->
    13   <!-- test results are displayed in the html:body -->
    14   <body xmlns="http://www.w3.org/1999/xhtml">
    15   <!-- In bug 773533, <marquee> elements crashed the JS memory reporter -->
    16   <marquee>Marquee</marquee>
    17   </body>
    19   <!-- test code goes here -->
    20   <script type="application/javascript">
    21   <![CDATA[
    23   // Nb: this test is all JS and so should be done with an xpcshell test,
    24   // but bug 671753 is preventing the memory-reporter-manager from being
    25   // accessed from xpcshell.
    27   "use strict";
    29   const Cc = Components.classes;
    30   const Ci = Components.interfaces;
    31   const Cr = Components.results;
    33   const NONHEAP = Ci.nsIMemoryReporter.KIND_NONHEAP;
    34   const HEAP    = Ci.nsIMemoryReporter.KIND_HEAP;
    35   const OTHER   = Ci.nsIMemoryReporter.KIND_OTHER;
    37   const BYTES = Ci.nsIMemoryReporter.UNITS_BYTES;
    38   const COUNT = Ci.nsIMemoryReporter.UNITS_COUNT;
    39   const COUNT_CUMULATIVE = Ci.nsIMemoryReporter.UNITS_COUNT_CUMULATIVE;
    40   const PERCENTAGE = Ci.nsIMemoryReporter.UNITS_PERCENTAGE;
    42   let vsizeAmounts = [];
    43   let residentAmounts = [];
    44   let jsGcHeapAmounts = [];
    45   let heapAllocatedAmounts = [];
    46   let storageSqliteAmounts = [];
    48   let present = {}
    50   // Generate a long, random string.  We'll check that this string is
    51   // reported in at least one of the memory reporters.
    52   let bigString = "";
    53   while (bigString.length < 10000) {
    54     bigString += Math.random();
    55   }
    56   let bigStringPrefix = bigString.substring(0, 100);
    58   // Generate many copies of two distinctive short strings, "!)(*&" and
    59   // "@)(*&".  We'll check that these strings are reported in at least
    60   // one of the memory reporters.
    61   let shortStrings = [];
    62   for (let i = 0; i < 10000; i++) {
    63     let str = (Math.random() > 0.5 ? "!" : "@") + ")(*&";
    64     shortStrings.push(str);
    65   }
    67   let mySandbox = Components.utils.Sandbox(document.nodePrincipal,
    68                     { sandboxName: "this-is-a-sandbox-name" });
    70   function handleReport(aProcess, aPath, aKind, aUnits, aAmount, aDescription)
    71   {
    72     // Record the values of some notable reporters.
    73     if (aPath === "vsize") {
    74       vsizeAmounts.push(aAmount);
    75     } else if (aPath === "resident") {
    76       residentAmounts.push(aAmount);
    77     } else if (aPath === "js-main-runtime-gc-heap-committed/used/gc-things") {
    78       jsGcHeapAmounts.push(aAmount); 
    79     } else if (aPath === "heap-allocated") {
    80       heapAllocatedAmounts.push(aAmount);
    81     } else if (aPath === "storage-sqlite") {
    82       storageSqliteAmounts.push(aAmount);
    84     // Check the presence of some other notable reporters.
    85     } else if (aPath.search(/^explicit\/js-non-window\/.*compartment\(/) >= 0) {
    86       present.jsNonWindowCompartments = true;
    87     } else if (aPath.search(/^explicit\/window-objects\/top\(.*\/js-compartment\(/) >= 0) {
    88       present.windowObjectsJsCompartments = true;
    89     } else if (aPath.search(/^explicit\/storage\/sqlite\/places.sqlite/) >= 0) {
    90       present.places = true;
    91     } else if (aPath.search(/^explicit\/images/) >= 0) {
    92       present.images = true;
    93     } else if (aPath.search(/^explicit\/xpti-working-set$/) >= 0) {
    94       present.xptiWorkingSet = true;
    95     } else if (aPath.search(/^explicit\/atom-tables$/) >= 0) {
    96       present.atomTable = true;
    97     } else if (/\[System Principal\].*this-is-a-sandbox-name/.test(aPath)) {
    98       // A system compartment with a location (such as a sandbox) should
    99       // show that location.
   100       present.sandboxLocation = true;
   101     } else if (aPath.contains(bigStringPrefix)) {
   102       present.bigString = true;
   103     } else if (aPath.contains("!)(*&")) {
   104       present.smallString1 = true;
   105     } else if (aPath.contains("@)(*&")) {
   106       present.smallString2 = true;
   107     }
   108   }
   110   let mgr = Cc["@mozilla.org/memory-reporter-manager;1"].
   111             getService(Ci.nsIMemoryReporterManager);
   113   // Access the distinguished amounts (mgr.explicit et al.) just to make sure
   114   // they don't crash.  We can't check their actual values because they're
   115   // non-deterministic.
   116   //
   117   // Nb: mgr.explicit will throw NS_ERROR_NOT_AVAILABLE if this is a
   118   // --enable-trace-malloc build.  Allow for that exception, but *only* that
   119   // exception.
   120   let dummy;
   121   let haveExplicit = true;
   122   try {
   123     dummy = mgr.explicit;
   124   } catch (ex) {
   125     is(ex.result, Cr.NS_ERROR_NOT_AVAILABLE, "mgr.explicit exception");
   126     haveExplicit = false;
   127   }
   128   let amounts = [
   129     "vsize",
   130     "vsizeMaxContiguous",
   131     "resident",
   132     "residentFast",
   133     "heapAllocated",
   134     "heapOverheadRatio",
   135     "JSMainRuntimeGCHeap",
   136     "JSMainRuntimeTemporaryPeak",
   137     "JSMainRuntimeCompartmentsSystem",
   138     "JSMainRuntimeCompartmentsUser",
   139     "imagesContentUsedUncompressed",
   140     "storageSQLite",
   141     "lowMemoryEventsVirtual",
   142     "lowMemoryEventsPhysical",
   143     "ghostWindows",
   144     "pageFaultsHard",
   145   ];
   146   for (let i = 0; i < amounts.length; i++) {
   147     try {
   148       // If mgr[amounts[i]] throws an exception, just move on -- some amounts
   149       // aren't available on all platforms.  But if the attribute simply
   150       // isn't present, that indicates the distinguished amounts have changed
   151       // and this file hasn't been updated appropriately.
   152       dummy = mgr[amounts[i]];
   153       ok(dummy !== undefined,
   154          "accessed an unknown distinguished amount: " + amounts[i]);
   155     } catch (ex) {
   156     }
   157   }
   159   // Run sizeOfTab() to make sure it doesn't crash.  We can't check the result
   160   // values because they're non-deterministic.
   161   let jsObjectsSize = {};
   162   let jsStringsSize = {};
   163   let jsOtherSize = {};
   164   let domSize = {};
   165   let styleSize = {};
   166   let otherSize = {};
   167   let totalSize = {};
   168   let jsMilliseconds = {};
   169   let nonJSMilliseconds = {};
   170   mgr.sizeOfTab(window, jsObjectsSize, jsStringsSize, jsOtherSize,
   171                 domSize, styleSize, otherSize, totalSize,
   172                 jsMilliseconds, nonJSMilliseconds);
   174   mgr.getReportsForThisProcess(handleReport, null);
   176   function checkSpecialReport(aName, aAmounts, aCanBeUnreasonable)
   177   {
   178     ok(aAmounts.length == 1, aName + " has " + aAmounts.length + " report");
   179     let n = aAmounts[0];
   180     // Check the size is reasonable -- i.e. not ridiculously large or small.
   181     ok((100 * 1000 <= n && n <= 10 * 1000 * 1000 * 1000) || aCanBeUnreasonable,
   182        aName + "'s size is reasonable");
   183   }
   185   // If mgr.explicit failed, we won't have "heap-allocated" either.
   186   if (haveExplicit) {
   187     checkSpecialReport("heap-allocated", heapAllocatedAmounts);
   188   }
   189   // vsize may be unreasonable if ASAN is enabled
   190   checkSpecialReport("vsize",          vsizeAmounts, /*canBeUnreasonable*/true);
   191   checkSpecialReport("resident",       residentAmounts);
   192   checkSpecialReport("js-main-runtime-gc-heap-committed/used/gc-things", jsGcHeapAmounts);
   194   ok(present.jsNonWindowCompartments,     "js-non-window compartments are present");
   195   ok(present.windowObjectsJsCompartments, "window-objects/.../js compartments are present");
   196   ok(present.places,                      "places is present");
   197   ok(present.images,                      "images is present");
   198   ok(present.xptiWorkingSet,              "xpti-working-set is present");
   199   ok(present.atomTable,                   "atom-table is present");
   200   ok(present.sandboxLocation,             "sandbox locations are present");
   201   ok(present.bigString,                   "large string is present");
   202   ok(present.smallString1,                "small string 1 is present");
   203   ok(present.smallString2,                "small string 2 is present");
   206   // Reporter registration tests
   208   // collectReports() calls to the test reporter.
   209   let called = 0;
   211   // The test memory reporter, testing the various report units.
   212   // Also acts as a report collector, verifying the reported values match the
   213   // expected ones after passing through XPConnect / nsMemoryReporterManager
   214   // and back.
   215   function MemoryReporterAndCallback() {
   216     this.seen = 0;
   217   }
   218   MemoryReporterAndCallback.prototype = {
   219     // The test reports.
   220     // Each test key corresponds to the path of the report.  |amount| is a
   221     // function called when generating the report.  |expected| is a function
   222     // to be tested when receiving a report during collection.  If |expected| is
   223     // omitted the |amount| will be checked instead.
   224     tests: {
   225       "test-memory-reporter-bytes1": {
   226         units: BYTES,
   227         amount: () => 0
   228       },
   229       "test-memory-reporter-bytes2": {
   230         units: BYTES,
   231         amount: () => (1<<30) * 8 // awkward way to say 8G in JS
   232       },
   233       "test-memory-reporter-counter": {
   234         units: COUNT,
   235         amount: () => 2
   236       },
   237       "test-memory-reporter-ccounter": {
   238         units: COUNT_CUMULATIVE,
   239         amount: () => ++called,
   240         expected: () => called
   241       },
   242       "test-memory-reporter-percentage": {
   243         units: PERCENTAGE,
   244         amount: () => 9999
   245       }
   246     },
   247     // nsIMemoryReporter
   248     collectReports: function(callback, data) {
   249       for (let path of Object.keys(this.tests)) {
   250         try {
   251           let test = this.tests[path];
   252           callback.callback(
   253             "", // Process. Should be "" initially.
   254             path,
   255             OTHER,
   256             test.units,
   257             test.amount(),
   258             "Test " + path + ".",
   259             data);
   260         }
   261         catch (ex) {
   262           ok(false, ex);
   263         }
   264       }
   265     },
   266     // nsIMemoryReporterCallback
   267     callback: function(process, path, kind, units, amount, data) {
   268       if (path in this.tests) {
   269         this.seen++;
   270         let test = this.tests[path];
   271         ok(units === test.units, "Test reporter units match");
   272         ok(amount === (test.expected || test.amount)(),
   273            "Test reporter values match: " + amount);
   274       }
   275     },
   276     // Checks that the callback has seen the expected number of reports, and
   277     // resets the callback counter.
   278     // @param expected  Optional.  Expected number of reports the callback
   279     //                  should have processed.
   280     finish: function(expected) {
   281       if (expected === undefined) {
   282         expected = Object.keys(this.tests).length;
   283       }
   284       is(expected, this.seen,
   285          "Test reporter called the correct number of times: " + expected);
   286       this.seen = 0;
   287     }
   288   };
   290   // General memory reporter + registerStrongReporter tests.
   291   function test_register_strong() {
   292     let reporterAndCallback = new MemoryReporterAndCallback();
   293     // Registration works.
   294     mgr.registerStrongReporter(reporterAndCallback);
   296     // Check the generated reports.
   297     mgr.getReportsForThisProcess(reporterAndCallback, null);
   298     reporterAndCallback.finish();
   300     // Unregistration works.
   301     mgr.unregisterStrongReporter(reporterAndCallback);
   303     // The reporter was unregistered, hence there shouldn't be any reports from
   304     // the test reporter.
   305     mgr.getReportsForThisProcess(reporterAndCallback, null);
   306     reporterAndCallback.finish(0);
   307   }
   309   test_register_strong();
   311   // Check strong reporters a second time, to make sure a reporter can be
   312   // re-registered.
   313   test_register_strong();
   316   // Check that you cannot register JS components as weak reporters.
   317   function test_register_weak() {
   318     let reporterAndCallback = new MemoryReporterAndCallback();
   319     try {
   320       // Should fail! nsMemoryReporterManager will only hold a raw pointer to
   321       // "weak" reporters.  When registering a weak reporter, XPConnect will
   322       // create a WrappedJS for JS components.  This WrappedJS would be
   323       // successfully registered with the manager, only to be destroyed
   324       // immediately after, which would eventually lead to a crash when
   325       // collecting the reports.  Therefore nsMemoryReporterManager should
   326       // reject WrappedJS reporters, which is what is tested here.
   327       // See bug 950391 comment #0.
   328       mgr.registerWeakReporter(reporterAndCallback);
   329       ok(false, "Shouldn't be allowed to register a JS component (WrappedJS)");
   330     }
   331     catch (ex) {
   332       ok(ex.message.indexOf("NS_ERROR_") >= 0,
   333          "WrappedJS reporter got rejected: " + ex);
   334     }
   335   }
   337   test_register_weak();
   339   ]]>
   340   </script>
   341 </window>

mercurial