From e4d043f0aacee13f96e84672bcad9076144942b1 Mon Sep 17 00:00:00 2001 From: Greg Tatum Date: Wed, 13 Nov 2019 16:17:06 +0000 Subject: [PATCH] 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 --- tools/profiler/tests/browser/browser.ini | 1 + tools/profiler/tests/browser/head.js | 59 ++-------- tools/profiler/tests/shared-head.js | 110 ++++++++++++++++++ .../xpcshell/{head_profiler.js => head.js} | 78 +++---------- .../tests/xpcshell/test_feature_js.js | 4 +- .../xpcshell/test_feature_mainthreadio.js | 2 +- .../xpcshell/test_feature_stackwalking.js | 2 +- .../tests/xpcshell/test_merged_stacks.js | 4 +- .../tests/xpcshell/test_responsiveness.js | 2 +- tools/profiler/tests/xpcshell/xpcshell.ini | 4 +- 10 files changed, 143 insertions(+), 123 deletions(-) create mode 100644 tools/profiler/tests/shared-head.js rename tools/profiler/tests/xpcshell/{head_profiler.js => head.js} (64%) diff --git a/tools/profiler/tests/browser/browser.ini b/tools/profiler/tests/browser/browser.ini index a3d08e1de81a..72026c26517f 100644 --- a/tools/profiler/tests/browser/browser.ini +++ b/tools/profiler/tests/browser/browser.ini @@ -1,5 +1,6 @@ [DEFAULT] support-files = + ../shared-head.js head.js do_work_500ms.html fixed_height.html diff --git a/tools/profiler/tests/browser/head.js b/tools/profiler/tests/browser/head.js index 5e74d3ebfdc8..0b58ab8a5017 100644 --- a/tools/profiler/tests/browser/head.js +++ b/tools/profiler/tests/browser/head.js @@ -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; -} diff --git a/tools/profiler/tests/shared-head.js b/tools/profiler/tests/shared-head.js new file mode 100644 index 000000000000..e80aa08497aa --- /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; + } + } +} diff --git a/tools/profiler/tests/xpcshell/head_profiler.js b/tools/profiler/tests/xpcshell/head.js similarity index 64% rename from tools/profiler/tests/xpcshell/head_profiler.js rename to tools/profiler/tests/xpcshell/head.js index 02398efd7621..82e6ef627405 100644 --- a/tools/profiler/tests/xpcshell/head_profiler.js +++ b/tools/profiler/tests/xpcshell/head.js @@ -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 diff --git a/tools/profiler/tests/xpcshell/test_feature_js.js b/tools/profiler/tests/xpcshell/test_feature_js.js index b04918681bdb..a2c328c0516a 100644 --- a/tools/profiler/tests/xpcshell/test_feature_js.js +++ b/tools/profiler/tests/xpcshell/test_feature_js.js @@ -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(); } diff --git a/tools/profiler/tests/xpcshell/test_feature_mainthreadio.js b/tools/profiler/tests/xpcshell/test_feature_mainthreadio.js index e720af02d7dd..76ab8dadec20 100644 --- a/tools/profiler/tests/xpcshell/test_feature_mainthreadio.js +++ b/tools/profiler/tests/xpcshell/test_feature_mainthreadio.js @@ -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"); } /** diff --git a/tools/profiler/tests/xpcshell/test_feature_stackwalking.js b/tools/profiler/tests/xpcshell/test_feature_stackwalking.js index 13ba72d61b84..628f46f417ab 100644 --- a/tools/profiler/tests/xpcshell/test_feature_stackwalking.js +++ b/tools/profiler/tests/xpcshell/test_feature_stackwalking.js @@ -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; diff --git a/tools/profiler/tests/xpcshell/test_merged_stacks.js b/tools/profiler/tests/xpcshell/test_merged_stacks.js index 70f8f5c0f160..526807d80b56 100644 --- a/tools/profiler/tests/xpcshell/test_merged_stacks.js +++ b/tools/profiler/tests/xpcshell/test_merged_stacks.js @@ -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(); } diff --git a/tools/profiler/tests/xpcshell/test_responsiveness.js b/tools/profiler/tests/xpcshell/test_responsiveness.js index 22cdac6c5523..b039af51b1ff 100644 --- a/tools/profiler/tests/xpcshell/test_responsiveness.js +++ b/tools/profiler/tests/xpcshell/test_responsiveness.js @@ -49,5 +49,5 @@ function doSyncWork(milliseconds) { function functionA() { doSyncWork(100); - doAtLeastOnePeriodicSample(); + captureAtLeastOneJsSample(); } diff --git a/tools/profiler/tests/xpcshell/xpcshell.ini b/tools/profiler/tests/xpcshell/xpcshell.ini index 453a9a1a329a..fecca59d7c34 100644 --- a/tools/profiler/tests/xpcshell/xpcshell.ini +++ b/tools/profiler/tests/xpcshell/xpcshell.ini @@ -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]