Bug 1582741 - Create a test for balanced native allocation; r=canaltinova
authorGreg Tatum <gtatum@mozilla.com>
Wed, 13 Nov 2019 16:19:07 +0000
changeset 501800 5991133b6ac7b2535495c4f0a1ea3011f08885d1
parent 501799 b37cf462c34198a34ffe38ec1b9fc0ca56ab9a25
child 501801 439f620efb11ff3e421a3ffdab1deb6b76b79281
push id36801
push userdvarga@mozilla.com
push dateThu, 14 Nov 2019 17:12:31 +0000
treeherdermozilla-central@a19a226a8c6a [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerscanaltinova
bugs1582741
milestone72.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1582741 - Create a test for balanced native allocation; r=canaltinova This file adds coverage for the balanced native allocations feature from the previous commit. It asserts that a de-allocation will have a matching allocation. Differential Revision: https://phabricator.services.mozilla.com/D51936
tools/profiler/tests/xpcshell/test_feature_nativeallocations.js
tools/profiler/tests/xpcshell/xpcshell.ini
--- a/tools/profiler/tests/xpcshell/test_feature_nativeallocations.js
+++ b/tools/profiler/tests/xpcshell/test_feature_nativeallocations.js
@@ -13,49 +13,60 @@ add_task(async () => {
 
   info(
     "Test that the profiler can install memory hooks and collect native allocation " +
       "information in the marker payloads."
   );
   {
     info("Start the profiler.");
     startProfiler({
-      // Increase the entries so we don't overflow the buffer.
-      entries: 1e8,
+      entries: 1e6,
       // Only instrument the main thread.
       threads: ["GeckoMain"],
       features: ["threads", "leaf", "nativeallocations"],
     });
 
     info(
       "Do some JS work for a little bit. This will increase the amount of allocations " +
         "that take place."
     );
     doWork();
 
-    info(
-      "Go ahead and wait for a periodic sample as well, in order to make sure that " +
-        "the native allocations play nicely with the rest of the profiler machinery."
-    );
-    await Services.profiler.waitOnePeriodicSampling();
-
     info("Get the profile data and analyze it.");
     const profile = await stopAndGetProfile();
 
-    const allocationPayloads = getPayloadsOfType(
-      profile.threads[0],
-      "Native allocation"
-    );
+    const {
+      allocationPayloads,
+      unmatchedAllocations,
+      logAllocationsAndDeallocations,
+    } = getAllocationInformation(profile);
 
     Assert.greater(
       allocationPayloads.length,
       0,
       "Native allocation payloads were recorded for the parent process' main thread when " +
         "the Native Allocation feature was turned on."
     );
+
+    if (unmatchedAllocations.length !== 0) {
+      info(
+        "There were unmatched allocations. Log all of the allocations and " +
+          "deallocations in order to aid debugging."
+      );
+      logAllocationsAndDeallocations();
+      ok(
+        false,
+        "Found a deallocation that did not have a matching allocation site. " +
+          "This could happen if balanced allocations is broken, or if the the " +
+          "buffer size of this test was too small, and some markers ended up " +
+          "rolling off."
+      );
+    }
+
+    ok(true, "All deallocation sites had matching allocations.");
   }
 
   info("Restart the profiler, to ensure that we get no more allocations.");
   {
     startProfiler({ features: ["threads", "leaf"] });
     info("Do some work again.");
     doWork();
     info("Wait for the periodic sampling.");
@@ -76,8 +87,69 @@ add_task(async () => {
 });
 
 function doWork() {
   this.n = 0;
   for (let i = 0; i < 1e5; i++) {
     this.n += Math.random();
   }
 }
+
+/**
+ * Extract the allocation payloads, and find the unmatched allocations.
+ */
+function getAllocationInformation(profile) {
+  // Get all of the allocation payloads.
+  const allocationPayloads = getPayloadsOfType(
+    profile.threads[0],
+    "Native allocation"
+  );
+
+  // Decide what is an allocation and deallocation.
+  const allocations = allocationPayloads.filter(
+    payload => ensureIsNumber(payload.size) >= 0
+  );
+  const deallocations = allocationPayloads.filter(
+    payload => ensureIsNumber(payload.size) < 0
+  );
+
+  // Now determine the unmatched allocations by building a set
+  const allocationSites = new Set(
+    allocations.map(({ memoryAddress }) => memoryAddress)
+  );
+
+  const unmatchedAllocations = deallocations.filter(
+    ({ memoryAddress }) => !allocationSites.has(memoryAddress)
+  );
+
+  // Provide a helper to log out the allocations and deallocations on failure.
+  function logAllocationsAndDeallocations() {
+    for (const { memoryAddress } of allocations) {
+      console.log("Allocations", formatHex(memoryAddress));
+      allocationSites.add(memoryAddress);
+    }
+
+    for (const { memoryAddress } of deallocations) {
+      console.log("Deallocations", formatHex(memoryAddress));
+    }
+
+    for (const { memoryAddress } of unmatchedAllocations) {
+      console.log("Deallocation with no allocation", formatHex(memoryAddress));
+    }
+  }
+
+  return {
+    allocationPayloads,
+    unmatchedAllocations,
+    logAllocationsAndDeallocations,
+  };
+}
+
+function ensureIsNumber(value) {
+  if (typeof value !== "number") {
+    throw new Error(`Expected a number: ${value}`);
+  }
+  return value;
+}
+
+function formatHex(number) {
+  return `0x${number.toString(16)}`;
+}
--- a/tools/profiler/tests/xpcshell/xpcshell.ini
+++ b/tools/profiler/tests/xpcshell/xpcshell.ini
@@ -17,17 +17,22 @@ skip-if = true
 skip-if = (os == "win" && processor == "aarch64") # aarch64 due to 1536652
 [test_enterjit_osr_disabling.js]
 skip-if = !debug
 [test_enterjit_osr_enabling.js]
 skip-if = !debug
 [test_asm.js]
 [test_feature_mainthreadio.js]
 skip-if = release_or_beta || (os == "win" && processor == "aarch64") # The IOInterposer is in an ifdef, aarch64 due to 1536657
+
+# The address sanitization checks appears to overwrite our own memory hooks in
+# xpcshell tests, and no allocation markers are gathered. Skip this test in that
+# configuration.
 [test_feature_nativeallocations.js]
+skip-if = asan
 
 # Native stackwalking is somewhat unreliable depending on the platform.
 #
 # We don't have frame pointers on macOS release and beta, so stack walking does not
 # work. See Bug 1571216 for more details.
 #
 # Linux can be very unreliable when native stackwalking through JavaScript code.
 # See Bug 1434402 for more details.