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
This commit is contained in:
Greg Tatum 2019-11-13 16:17:06 +00:00
parent 4c3bf72271
commit e4d043f0aa
10 changed files with 143 additions and 123 deletions

View File

@ -1,5 +1,6 @@
[DEFAULT]
support-files =
../shared-head.js
head.js
do_work_500ms.html
fixed_height.html

View File

@ -1,27 +1,16 @@
/* 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;
}

View File

@ -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;
}
}
}

View File

@ -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");
const { 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);
}
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

View File

@ -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();
}

View File

@ -113,7 +113,7 @@ async function startProfilerAndgetFileIOPayloads(features, filename) {
const profile = await Services.profiler.getProfileDataAsync();
Services.profiler.StopProfiler();
return getAllPayloadsOfType(profile, "FileIO");
return getPayloadsOfTypeFromAllThreads(profile, "FileIO");
}
/**

View File

@ -18,7 +18,7 @@ add_task(async () => {
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;

View File

@ -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
const sampleIndex = await functionA();
const profile = await Services.profiler.getProfileDataAsync();
@ -71,5 +71,5 @@ function functionB() {
}
async function functionC() {
return doAtLeastOnePeriodicSample();
return captureAtLeastOneJsSample();
}

View File

@ -49,5 +49,5 @@ function doSyncWork(milliseconds) {
function functionA() {
doSyncWork(100);
doAtLeastOnePeriodicSample();
captureAtLeastOneJsSample();
}

View File

@ -1,5 +1,7 @@
[DEFAULT]
head = head_profiler.js
head = head.js
support-files =
../shared-head.js
skip-if = toolkit == 'android'
[test_active_configuration.js]