Bug 1593318 - De-duplicate head.js files in profiler tests; r=canaltinova
authorGreg Tatum <gtatum@mozilla.com>
Wed, 13 Nov 2019 16:17:06 +0000
changeset 501814 9cdc93b402a207bd714de113f4bcfd7086062cf2
parent 501813 355f090421a6b38618b3f2e2e0c08d9166a78e72
child 501815 b1c0de5b727cc3bb5c03866793f7e2669c65ce3c
push id100439
push usergtatum@mozilla.com
push dateWed, 13 Nov 2019 20:05:58 +0000
treeherderautoland@e2b8a34e2aac [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerscanaltinova
bugs1593318
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 1593318 - De-duplicate head.js files in profiler tests; r=canaltinova The tests for xpcshell and mochitests were pretty similar, and need to do similar things. This commit creates a shread-head.js file where those functions can be shared. This patch also renames a few shared functions to give them more clarity in their current usage. Differential Revision: https://phabricator.services.mozilla.com/D51933
tools/profiler/tests/browser/browser.ini
tools/profiler/tests/browser/head.js
tools/profiler/tests/shared-head.js
tools/profiler/tests/xpcshell/head.js
tools/profiler/tests/xpcshell/head_profiler.js
tools/profiler/tests/xpcshell/test_feature_js.js
tools/profiler/tests/xpcshell/test_feature_mainthreadio.js
tools/profiler/tests/xpcshell/test_feature_stackwalking.js
tools/profiler/tests/xpcshell/test_merged_stacks.js
tools/profiler/tests/xpcshell/test_responsiveness.js
tools/profiler/tests/xpcshell/xpcshell.ini
--- a/tools/profiler/tests/browser/browser.ini
+++ b/tools/profiler/tests/browser/browser.ini
@@ -1,10 +1,11 @@
 [DEFAULT]
 support-files =
+  ../shared-head.js
   head.js
   do_work_500ms.html
   fixed_height.html
   multi_frame.html
   simple.html
   single_frame.html
 
 [browser_test_feature_ipcmessages.js]
--- a/tools/profiler/tests/browser/head.js
+++ b/tools/profiler/tests/browser/head.js
@@ -1,32 +1,21 @@
+/* import-globals-from ../shared-head.js */
+
+Services.scriptloader.loadSubScript(
+  "chrome://mochitests/content/browser/tools/profiler/tests/browser/shared-head.js",
+  this
+);
+
 const { BrowserTestUtils } = ChromeUtils.import(
   "resource://testing-common/BrowserTestUtils.jsm"
 );
 
 const BASE_URL = "http://example.com/browser/tools/profiler/tests/browser/";
 
-const defaultSettings = {
-  entries: 1000000, // 9MB
-  interval: 1, // ms
-  features: ["threads"],
-  threads: ["GeckoMain"],
-};
-
-function startProfiler(callersSettings) {
-  const settings = Object.assign({}, defaultSettings, callersSettings);
-  Services.profiler.StartProfiler(
-    settings.entries,
-    settings.interval,
-    settings.features,
-    settings.threads,
-    settings.duration
-  );
-}
-
 /**
  * This is a helper function that will stop the profiler of the browser running
  * with PID contentPid.
  * This happens immediately, without waiting for any sampling to happen or
  * finish. Use stopProfilerAndGetThreads (without "Now") below instead to wait
  * for samples before stopping.
  *
  * @param {number} contentPid
@@ -65,42 +54,8 @@ async function stopProfilerNowAndGetThre
  * @param {number} contentPid
  * @returns {Promise}
  */
 async function stopProfilerAndGetThreads(contentPid) {
   await Services.profiler.waitOnePeriodicSampling();
 
   return stopProfilerNowAndGetThreads(contentPid);
 }
-
-/**
- * This is a helper function be able to run `await wait(500)`. Unfortunately this
- * is needed as the act of collecting functions relies on the periodic sampling of
- * the threads. See: https://bugzilla.mozilla.org/show_bug.cgi?id=1529053
- *
- * @param {number} time
- * @returns {Promise}
- */
-function wait(time) {
-  return new Promise(resolve => {
-    // eslint-disable-next-line mozilla/no-arbitrary-setTimeout
-    setTimeout(resolve, time);
-  });
-}
-
-/**
- * Get the payloads of a type from a single thread.
- *
- * @param {Object} thread The thread from a profile.
- * @param {string} type The marker payload type, e.g. "DiskIO".
- * @return {Array} The payloads.
- */
-function getPayloadsOfType(thread, type) {
-  const { markers } = thread;
-  const results = [];
-  for (const markerTuple of markers.data) {
-    const payload = markerTuple[markers.schema.data];
-    if (payload && payload.type === type) {
-      results.push(payload);
-    }
-  }
-  return results;
-}
new file mode 100644
--- /dev/null
+++ b/tools/profiler/tests/shared-head.js
@@ -0,0 +1,110 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/**
+ * This file contains utilities that can be shared between xpcshell tests and mochitests.
+ */
+
+// This Services declaration may shadow another from head.js, so define it as
+// a var rather than a const.
+var { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
+
+const defaultSettings = {
+  entries: 1000000, // 9MB
+  interval: 1, // ms
+  features: ["threads"],
+  threads: ["GeckoMain"],
+};
+
+function startProfiler(callersSettings) {
+  const settings = Object.assign({}, defaultSettings, callersSettings);
+  Services.profiler.StartProfiler(
+    settings.entries,
+    settings.interval,
+    settings.features,
+    settings.threads,
+    settings.duration
+  );
+}
+
+/**
+ * This is a helper function be able to run `await wait(500)`. Unfortunately
+ * this is needed as the act of collecting functions relies on the periodic
+ * sampling of the threads. See:
+ * https://bugzilla.mozilla.org/show_bug.cgi?id=1529053
+ *
+ * @param {number} time
+ * @returns {Promise}
+ */
+function wait(time) {
+  return new Promise(resolve => {
+    // eslint-disable-next-line mozilla/no-arbitrary-setTimeout
+    setTimeout(resolve, time);
+  });
+}
+
+/**
+ * Get the payloads of a type recursively, including from all subprocesses.
+ *
+ * @param {Object} profile The gecko profile.
+ * @param {string} type The marker payload type, e.g. "DiskIO".
+ * @param {Array} payloadTarget The recursive list of payloads.
+ * @return {Array} The final payloads.
+ */
+function getPayloadsOfTypeFromAllThreads(profile, type, payloadTarget = []) {
+  for (const { markers } of profile.threads) {
+    for (const markerTuple of markers.data) {
+      const payload = markerTuple[markers.schema.data];
+      if (payload && payload.type === type) {
+        payloadTarget.push(payload);
+      }
+    }
+  }
+
+  for (const subProcess of profile.processes) {
+    getPayloadsOfTypeFromAllThreads(subProcess, type, payloadTarget);
+  }
+
+  return payloadTarget;
+}
+
+/**
+ * Get the payloads of a type from a single thread.
+ *
+ * @param {Object} thread The thread from a profile.
+ * @param {string} type The marker payload type, e.g. "DiskIO".
+ * @return {Array} The payloads.
+ */
+function getPayloadsOfType(thread, type) {
+  const { markers } = thread;
+  const results = [];
+  for (const markerTuple of markers.data) {
+    const payload = markerTuple[markers.schema.data];
+    if (payload && payload.type === type) {
+      results.push(payload);
+    }
+  }
+  return results;
+}
+
+/**
+ * It can be helpful to force the profiler to collect a JavaScript sample. This
+ * function spins on a while loop until at least one more sample is collected.
+ *
+ * @return {number} The index of the collected sample.
+ */
+function captureAtLeastOneJsSample() {
+  function getProfileSampleCount() {
+    const profile = Services.profiler.getProfileData();
+    return profile.threads[0].samples.data.length;
+  }
+
+  const sampleCount = getProfileSampleCount();
+  // Create an infinite loop until a sample has been collected.
+  while (true) {
+    if (sampleCount < getProfileSampleCount()) {
+      return sampleCount;
+    }
+  }
+}
rename from tools/profiler/tests/xpcshell/head_profiler.js
rename to tools/profiler/tests/xpcshell/head.js
--- a/tools/profiler/tests/xpcshell/head_profiler.js
+++ b/tools/profiler/tests/xpcshell/head.js
@@ -1,58 +1,32 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
+/* import-globals-from ../shared-head.js */
+
+// This Services declaration may shadow another from head.js, so define it as
+// a var rather than a const.
 var { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
-var { AppConstants } = ChromeUtils.import(
+
+const { AppConstants } = ChromeUtils.import(
   "resource://gre/modules/AppConstants.jsm"
 );
-var { setTimeout } = ChromeUtils.import("resource://gre/modules/Timer.jsm");
-
-/**
- * Get the payloads of a type recursively, including from all subprocesses.
- *
- * @param {Object} profile The gecko profile.
- * @param {string} type The marker payload type, e.g. "DiskIO".
- * @param {Array} payloadTarget The recursive list of payloads.
- * @return {Array} The final payloads.
- */
-function getAllPayloadsOfType(profile, type, payloadTarget = []) {
-  for (const { markers } of profile.threads) {
-    for (const markerTuple of markers.data) {
-      const payload = markerTuple[markers.schema.data];
-      if (payload && payload.type === type) {
-        payloadTarget.push(payload);
-      }
-    }
-  }
+const { setTimeout } = ChromeUtils.import("resource://gre/modules/Timer.jsm");
 
-  for (const subProcess of profile.processes) {
-    getAllPayloadsOfType(subProcess, type, payloadTarget);
-  }
-
-  return payloadTarget;
+// Load the shared head
+const sharedHead = do_get_file("shared-head.js", false);
+if (!sharedHead) {
+  throw new Error("Could not load the shared head.");
 }
-
-/**
- * This is a helper function be able to run `await wait(500)`. Unfortunately
- * this is needed as the act of collecting functions relies on the periodic
- * sampling of the threads. See:
- * https://bugzilla.mozilla.org/show_bug.cgi?id=1529053
- *
- * @param {number} time
- * @returns {Promise}
- */
-function wait(time) {
-  return new Promise(resolve => {
-    // eslint-disable-next-line mozilla/no-arbitrary-setTimeout
-    setTimeout(resolve, time);
-  });
-}
+Services.scriptloader.loadSubScript(
+  Services.io.newFileURI(sharedHead).spec,
+  this
+);
 
 /**
  * This function takes a thread, and a sample tuple from the "data" array, and
  * inflates the frame to be an array of strings.
  *
  * @param {Object} thread - The thread from the profile.
  * @param {Array} sample - The tuple from the thread.samples.data array.
  * @returns {Array<string>} An array of function names.
@@ -77,38 +51,16 @@ function getInflatedStackLocations(threa
     stackIndex = stackEntry[STACK_PREFIX_SLOT];
   }
 
   // The profiler tree is inverted, so reverse the array.
   return locations.reverse();
 }
 
 /**
- * It can be helpful to deterministically do at least one more profile sample.
- * Sampling is done based on a timer. This function spins on a while loop until
- * at least one more sample is collected.
- *
- * @return {number} The index of the collected sample.
- */
-function doAtLeastOnePeriodicSample() {
-  function getProfileSampleCount() {
-    const profile = Services.profiler.getProfileData();
-    return profile.threads[0].samples.data.length;
-  }
-
-  const sampleCount = getProfileSampleCount();
-  // Create an infinite loop until a sample has been collected.
-  while (true) {
-    if (sampleCount < getProfileSampleCount()) {
-      return sampleCount;
-    }
-  }
-}
-
-/**
  * This utility matches up stacks to see if they contain a certain sequence of
  * stack frames. A correctly functioning profiler will have a certain sequence
  * of stacks, but we can't always determine exactly which stacks will show up
  * due to implementation changes, as well as memory addresses being arbitrary to
  * that particular build.
  *
  * This function triggers a test failure with a nice debug message when it
  * fails.
--- a/tools/profiler/tests/xpcshell/test_feature_js.js
+++ b/tools/profiler/tests/xpcshell/test_feature_js.js
@@ -12,17 +12,17 @@ add_task(async () => {
   const entries = 10000;
   const interval = 1;
   const threads = [];
   const features = ["js"];
 
   Services.profiler.StartProfiler(entries, interval, features, threads);
 
   // Call the following to get a nice stack in the profiler:
-  // functionA -> functionB -> functionC -> doAtLeastOnePeriodicSample
+  // functionA -> functionB -> functionC -> captureAtLeastOneJsSample
   const sampleIndex = await functionA();
 
   const profile = await Services.profiler.getProfileDataAsync();
   const [thread] = profile.threads;
   const { samples } = thread;
 
   const inflatedStackFrames = getInflatedStackLocations(
     thread,
@@ -54,10 +54,10 @@ function functionA() {
   return functionB();
 }
 
 function functionB() {
   return functionC();
 }
 
 async function functionC() {
-  return doAtLeastOnePeriodicSample();
+  return captureAtLeastOneJsSample();
 }
--- a/tools/profiler/tests/xpcshell/test_feature_mainthreadio.js
+++ b/tools/profiler/tests/xpcshell/test_feature_mainthreadio.js
@@ -108,17 +108,17 @@ async function startProfilerAndgetFileIO
   await wait(500);
 
   // Pause the profiler as we don't need to collect more samples as we retrieve
   // and serialize the profile.
   Services.profiler.PauseSampling();
 
   const profile = await Services.profiler.getProfileDataAsync();
   Services.profiler.StopProfiler();
-  return getAllPayloadsOfType(profile, "FileIO");
+  return getPayloadsOfTypeFromAllThreads(profile, "FileIO");
 }
 
 /**
  * See if a list of payloads has a write operation from a file.
  *
  * @param {Array<Object>} payloads The payloads captured from the profiler.
  * @param {string} filename The filename used to test a write operation.
  */
--- a/tools/profiler/tests/xpcshell/test_feature_stackwalking.js
+++ b/tools/profiler/tests/xpcshell/test_feature_stackwalking.js
@@ -13,17 +13,17 @@ add_task(async () => {
     return;
   }
   const entries = 10000;
   const interval = 1;
   const threads = [];
   const features = ["stackwalk"];
 
   Services.profiler.StartProfiler(entries, interval, features, threads);
-  const sampleIndex = await doAtLeastOnePeriodicSample();
+  const sampleIndex = await captureAtLeastOneJsSample();
 
   const profile = await Services.profiler.getProfileDataAsync();
   const [thread] = profile.threads;
   const { samples } = thread;
 
   const inflatedStackFrames = getInflatedStackLocations(
     thread,
     samples.data[sampleIndex]
--- a/tools/profiler/tests/xpcshell/test_merged_stacks.js
+++ b/tools/profiler/tests/xpcshell/test_merged_stacks.js
@@ -12,17 +12,17 @@ add_task(async () => {
   const entries = 10000;
   const interval = 1;
   const threads = [];
   const features = ["js", "stackwalk"];
 
   Services.profiler.StartProfiler(entries, interval, features, threads);
 
   // Call the following to get a nice stack in the profiler:
-  // functionA -> functionB -> functionC -> doAtLeastOnePeriodicSample
+  // functionA -> functionB -> functionC
   const sampleIndex = await functionA();
 
   const profile = await Services.profiler.getProfileDataAsync();
   const [thread] = profile.threads;
   const { samples } = thread;
 
   const inflatedStackFrames = getInflatedStackLocations(
     thread,
@@ -66,10 +66,10 @@ function functionA() {
   return functionB();
 }
 
 function functionB() {
   return functionC();
 }
 
 async function functionC() {
-  return doAtLeastOnePeriodicSample();
+  return captureAtLeastOneJsSample();
 }
--- a/tools/profiler/tests/xpcshell/test_responsiveness.js
+++ b/tools/profiler/tests/xpcshell/test_responsiveness.js
@@ -44,10 +44,10 @@ function doSyncWork(milliseconds) {
     if (Date.now() - start > milliseconds) {
       return;
     }
   }
 }
 
 function functionA() {
   doSyncWork(100);
-  doAtLeastOnePeriodicSample();
+  captureAtLeastOneJsSample();
 }
--- a/tools/profiler/tests/xpcshell/xpcshell.ini
+++ b/tools/profiler/tests/xpcshell/xpcshell.ini
@@ -1,10 +1,12 @@
 [DEFAULT]
-head = head_profiler.js
+head = head.js
+support-files =
+  ../shared-head.js
 skip-if = toolkit == 'android'
 
 [test_active_configuration.js]
 [test_start.js]
 skip-if = true
 [test_get_features.js]
 [test_responsiveness.js]
 [test_shared_library.js]