Bug 1439014 - Add a test that checks for the presence of JS tracked optimization info. r=julienw
☠☠ backed out by 4d09e6d03883 ☠ ☠
authorMarkus Stange <mstange@themasta.com>
Mon, 26 Mar 2018 17:03:43 -0400
changeset 466070 226c8c740c34e5e53be3282196032504a9665c2e
parent 466069 84c9b5ab7de94b7acccf1bfb1ad9821cc9015efe
child 466071 1b84d36de4720a3efadf79ded3713143ff78d85a
push id1728
push userjlund@mozilla.com
push dateMon, 18 Jun 2018 21:12:27 +0000
treeherdermozilla-release@c296fde26f5f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjulienw
bugs1439014
milestone61.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 1439014 - Add a test that checks for the presence of JS tracked optimization info. r=julienw MozReview-Commit-ID: ETJGZPhMfLv
tools/profiler/tests/chrome/chrome.ini
tools/profiler/tests/chrome/profiler_test_utils.js
tools/profiler/tests/chrome/test_profile_with_trackopts.html
--- a/tools/profiler/tests/chrome/chrome.ini
+++ b/tools/profiler/tests/chrome/chrome.ini
@@ -1,5 +1,6 @@
 [DEFAULT]
 support-files=profiler_test_utils.js
 
+[test_profile_with_trackopts.html]
 [test_profile_worker_bug_1428076.html]
 [test_profile_worker.html]
--- a/tools/profiler/tests/chrome/profiler_test_utils.js
+++ b/tools/profiler/tests/chrome/profiler_test_utils.js
@@ -37,22 +37,48 @@ function end(error) {
   if (error) {
     ok(false, `We got an error: ${error}`);
   } else {
     ok(true, "We ran the whole process");
   }
   SimpleTest.finish();
 }
 
-async function runTest(settings, workload) {
+function getBufferInfo() {
+  let position = {}, totalSize = {}, generation = {};
+  Services.profiler.GetBufferInfo(position, totalSize, generation);
+  return {
+    position: position.value,
+    totalSize: totalSize.value,
+    generation: generation.value
+  };
+}
+
+async function runTest(settings, workload,
+                       checkProfileCallback = function(profile) {}) {
   SimpleTest.waitForExplicitFinish();
   try {
     await startProfiler(settings);
-    await workload();
-    await getProfile();
+
+    // Run workload() one or more times until at least one sample has been taken.
+    const bufferInfoAtStart = getBufferInfo();
+    while (true) {
+      await workload();
+      const bufferInfoAfterWorkload = getBufferInfo();
+      if (bufferInfoAfterWorkload.generation > bufferInfoAtStart.generation ||
+          bufferInfoAfterWorkload.position > bufferInfoAtStart.position) {
+        // The buffer position advanced, so we've either added a marker or a
+        // sample. It would be better to have conclusive evidence that we
+        // actually have a sample...
+        break;
+      }
+    }
+
+    const profile = await getProfile();
+    await checkProfileCallback(profile);
     await stopProfiler();
     await end();
   } catch (e) {
     // By catching and handling the error, we're being nice to mochitest
     // runners: instead of waiting for the timeout, we fail right away.
     await end(e);
   }
 }
new file mode 100644
--- /dev/null
+++ b/tools/profiler/tests/chrome/test_profile_with_trackopts.html
@@ -0,0 +1,220 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1439014
+-->
+<head>
+  <meta charset="utf-8">
+  <title>Test for Bug 1439014</title>
+  <link rel="stylesheet" type="text/css" href="chrome://global/skin"/>
+  <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1439014">Mozilla Bug 1439014</a>
+
+<script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+<script type="application/javascript" src="profiler_test_utils.js"></script>
+<script type="application/javascript">
+/* globals runTest */
+
+"use strict";
+
+const settings = {
+  entries: 1000000, // 9MB
+  interval: 1, // ms
+  features: ["js", "threads", "trackopts"],
+  threads: ["GeckoMain"]
+};
+
+function innerFunction(x) {
+  return x * 0.7; // This is line 30.
+}
+
+function middleFunction(x) {
+  return innerFunction(x) * 1.4;
+}
+
+function outerFunction() {
+  let k = 0;
+  for (let i = 0; i < 1000000; i++) {
+    k += middleFunction(i); // This is line 40.
+  }
+  return k;
+}
+
+function workload() {
+  let m = 0;
+  for (let i = 0; i < 20; i++) {
+    m += outerFunction();
+  }
+  return m;
+}
+
+runTest(settings, workload, profile => {
+  const thisThread = profile.threads[0];
+  const { frameTable, stringTable } = thisThread;
+
+  function prettifyOptimizationSites(optimizationSites) {
+    return optimizationSites.map(optimizationSite => {
+      const result = {};
+      if (optimizationSite.site) {
+        result.site = stringTable[optimizationSite.site];
+      }
+      if (optimizationSite.mirType) {
+        result.mirType = stringTable[optimizationSite.mirType];
+      }
+      if (optimizationSite.typeset) {
+        result.typeset = optimizationSite.typeset.map(({ keyedBy, name }) => ({
+          keyedBy: stringTable[keyedBy],
+          name: stringTable[name],
+        }));
+      }
+      return result;
+    });
+  }
+  function prettifyAttempts(attempts) {
+    const { strategy, outcome } = attempts.schema;
+    return attempts.data.map(data => ({
+      strategy: stringTable[data[strategy]],
+      outcome: stringTable[data[outcome]],
+    }));
+  }
+  function prettifyOptimizations(optimizations) {
+    if (!optimizations) {
+      return null;
+    }
+    return {
+      types: prettifyOptimizationSites(optimizations.types),
+      attempts: prettifyAttempts(optimizations.attempts),
+      line: optimizations.line,
+      column: optimizations.column,
+    };
+  }
+  function framesForFunc(functionName) {
+    const { location, implementation, optimizations, line } = frameTable.schema;
+    return frameTable.data.filter(data => {
+      return stringTable[data[location]].startsWith(functionName + " ");
+    }).map(data => ({
+      implementation: stringTable[data[implementation]],
+      optimizations: prettifyOptimizations(data[optimizations]),
+      line: data[line],
+    }));
+  }
+
+  const outerFunctionFrames = framesForFunc("outerFunction");
+  const innerFunctionFrames = framesForFunc("innerFunction");
+
+  // console.log("outerFunction:", outerFunctionFrames);
+  // console.log("innerFunction:", innerFunctionFrames);
+  //
+  // Example output:
+  //
+  // console.log: "outerFunction:" [
+  //   {
+  //     implementation: "baseline",
+  //     optimizations: null,
+  //     line: undefined
+  //   },
+  //   {
+  //     implementation: "ion",
+  //     optimizations: null,
+  //     line: undefined
+  //   },
+  //   {
+  //     implementation: "ion",
+  //     optimizations: {
+  //       types: [
+  //         {
+  //           site: "Operand",
+  //           mirType: "Double",
+  //           typeset: [
+  //             { keyedBy: "primitive", name: "int" },
+  //             { keyedBy: "primitive", name: "float" }
+  //           ]
+  //         },
+  //         {
+  //           site: "Operand",
+  //           mirType: "Double"
+  //         }
+  //       ],
+  //       attempts: [
+  //         {
+  //           strategy: "BinaryArith_Concat",
+  //           outcome: "OperandNotString"
+  //         },
+  //         {
+  //           strategy: "BinaryArith_SpecializedTypes",
+  //           outcome: "GenericSuccess"
+  //         }
+  //       ],
+  //       line:40,
+  //       column:9
+  //     },
+  //     line: undefined
+  //   },
+  //   {
+  //     implementation: "ion",
+  //     optimizations: null,
+  //     line: undefined
+  //   }
+  // ]
+  // console.log: "innerFunction:" [
+  //   {
+  //     implementation: "ion",
+  //     optimizations: {
+  //       types: [
+  //         {
+  //           site: "Operand",
+  //           mirType: "Int32",
+  //           typeset: [
+  //             { keyedBy: "primitive", name: "int" }
+  //           ]
+  //         },
+  //         {
+  //           site: "Operand",
+  //           mirType: "Double"
+  //         }
+  //       ],
+  //       attempts: [
+  //         {
+  //           strategy: "BinaryArith_SpecializedTypes",
+  //           outcome: "GenericSuccess"
+  //         }
+  //       ],
+  //       line: 30,
+  //       column: 2
+  //     },
+  //     line: undefined
+  //   }
+  // ]
+
+  ok(outerFunctionFrames.length > 0, "should have sampled at least one frame of outerFunction() running");
+  const outerFunctionIonFrames = outerFunctionFrames.filter(frame => frame.implementation === "ion");
+  ok(outerFunctionIonFrames.length > 0, "should have observed outerFunction() running in ion");
+  const outerFunctionIonFramesWithOptimizations = outerFunctionIonFrames.filter(frame => frame.optimizations !== undefined);
+  ok(outerFunctionIonFramesWithOptimizations.length > 0, "should have optimizations info for some frames of outerFunction()");
+
+  // Try to check for one specific optimization. If the JS engine changes, this
+  // test may need changing. In this test we only care about the fact that we
+  // get useful optimization information, we don't care about how exactly this
+  // JS code was optimized.
+  ok(outerFunctionIonFramesWithOptimizations.some(frame => {
+    return frame.optimizations.line === 40 && frame.optimizations.attempts.some(attempt => {
+      return attempt.strategy === "BinaryArith_SpecializedTypes" &&
+             attempt.outcome === "GenericSuccess";
+    }) && frame.optimizations.types.some(optimizationSite => {
+      return optimizationSite.site === "Operand" &&
+             optimizationSite.mirType === "Double";
+    });
+  }), "should find a successful arithmetic specialization for the += operation on line 40");
+
+  ok(innerFunctionFrames.some(frame => {
+    return frame.implementation === "ion" &&
+           frame.optimizations !== null &&
+           frame.optimizations.line === 30;
+  }), "should find a piece of optimization info about innerFunction for line 30");
+});
+
+</script>
+</body>
+</html>