Commit 1b8031b3 authored by Greg Tatum's avatar Greg Tatum
Browse files

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

--HG--
rename : tools/profiler/tests/xpcshell/head_profiler.js => tools/profiler/tests/xpcshell/head.js
extra : moz-landing-system : lando
parent 9c2cda9a
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
[DEFAULT]
support-files =
  ../shared-head.js
  head.js
  do_work_500ms.html
  fixed_height.html
+7 −52
Original line number Diff line number Diff line
/* 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.
@@ -70,37 +59,3 @@ async function stopProfilerAndGetThreads(contentPid) {

  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;
}
+110 −0
Original line number Diff line number Diff line
/* 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;
    }
  }
}
+15 −63
Original line number Diff line number Diff line
@@ -2,52 +2,26 @@
 * 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);
      }
    }
  }

  for (const subProcess of profile.processes) {
    getAllPayloadsOfType(subProcess, type, payloadTarget);
  }
const { setTimeout } = ChromeUtils.import("resource://gre/modules/Timer.jsm");

  return payloadTarget;
}

/**
 * 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);
  });
// Load the shared head
const sharedHead = do_get_file("shared-head.js", false);
if (!sharedHead) {
  throw new Error("Could not load the shared head.");
}
Services.scriptloader.loadSubScript(
  Services.io.newFileURI(sharedHead).spec,
  this
);

/**
 * This function takes a thread, and a sample tuple from the "data" array, and
@@ -81,28 +55,6 @@ function getInflatedStackLocations(thread, sample) {
  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
+2 −2
Original line number Diff line number Diff line
@@ -17,7 +17,7 @@ add_task(async () => {
  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();
@@ -59,5 +59,5 @@ function functionB() {
}

async function functionC() {
  return doAtLeastOnePeriodicSample();
  return captureAtLeastOneJsSample();
}
Loading