toolkit/components/aboutmemory/tests/test_memoryReporters.xul

changeset 0
6474c204b198
     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 +

mercurial