diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 1d7f1850a4..659e70fb54 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -72,6 +72,7 @@ The web app doesn't include any performance profiles by default, so you'll need #### 2. Use an existing profile: - On the web, replace the https://profiler.firefox.com with your local server, usually `http://localhost:4242`. Be sure that that the protocol is `http` and not `https` when running the server locally. - Alternatively, if a profile has been previously downloaded, drag and drop it to the loading screen. Compared to the previous solution, refreshing won't work with this particular solution. + - A third alternative on Linux is to run the provided [fp.sh](./bin/fp.sh) script, giving the profile file as the first argument. Both refreshing and symlinking to the script are supported. For more information on loading a profile, visit its [documentation](./docs-developer/loading-in-profiles.md). diff --git a/bin/launch-fp.sh b/bin/launch-fp.sh new file mode 100755 index 0000000000..41411418fe --- /dev/null +++ b/bin/launch-fp.sh @@ -0,0 +1,23 @@ +#!/bin/sh +# This script is primarily for running Firefox Profiler on a saved profile. +# +# For example: +# launch-fp.sh CPU.20230124.095804.8433.0.001.cpuprofile +# will launch the Firefox Profiler server and open a browser tab with the above +# Node.js profile. If the profile is omitted: +# launch-fp.sh +# then only the server will be launched. + +if [ "x$1" != "x" ]; then + PROFILEPATH=$(realpath "$1") +fi +PWD=$(pwd) +SCRIPTPATH=$(realpath "$0") +SCRIPTDIR=$(dirname "$SCRIPTPATH") +cd "$SCRIPTDIR/.." || exit +if [ "x$1" = "x" ]; then + yarn start +else + yarn start "$PROFILEPATH" +fi +cd "$PWD" || exit diff --git a/docs-developer/loading-in-profiles.md b/docs-developer/loading-in-profiles.md index e9f6838dcd..c9fa19031a 100644 --- a/docs-developer/loading-in-profiles.md +++ b/docs-developer/loading-in-profiles.md @@ -92,6 +92,8 @@ server.listen(PORT, err => { }); ``` +Note that if you have a copy of the project locally, you can add the profile path as a parameter to `yarn start` and a server serving this profile will be spawned for you. On Linux, the provided [launch-fp.sh](../bin/launch-fp.sh) script can do this for you as well, from any working directory. + ### Directly from Firefox > `https://profiler.firefox.com/from-browser/` diff --git a/package.json b/package.json index 3e3eb7a91a..eae4007ff4 100644 --- a/package.json +++ b/package.json @@ -146,6 +146,7 @@ "mkdirp": "^2.1.3", "node-fetch": "^2.6.7", "npm-run-all": "^4.1.5", + "open": "^8.4.1", "postcss": "^8.4.21", "postcss-loader": "^7.0.2", "prettier": "^2.8.4", @@ -159,7 +160,8 @@ "webpack": "^5.75.0", "webpack-cli": "^5.0.1", "webpack-dev-server": "^4.11.1", - "workbox-webpack-plugin": "^6.5.4" + "workbox-webpack-plugin": "^6.5.4", + "yargs": "^17.6.2" }, "jest": { "collectCoverageFrom": [ diff --git a/server.js b/server.js index bd3a9b092d..884ac634a5 100644 --- a/server.js +++ b/server.js @@ -4,16 +4,27 @@ // @noflow const webpack = require('webpack'); const WebpackDevServer = require('webpack-dev-server'); +const http = require('node:http'); const config = require('./webpack.config'); const { oneLine, stripIndent } = require('common-tags'); const port = process.env.FX_PROFILER_PORT || 4242; const host = process.env.FX_PROFILER_HOST || 'localhost'; const fs = require('fs'); const path = require('path'); +const yargs = require('yargs'); +const { hideBin } = require('yargs/helpers'); +const open = require('open'); const localConfigExists = fs.existsSync( path.join(__dirname, './webpack.local-config.js') ); +const argv = yargs(hideBin(process.argv)) + .command('* [profile]', 'Open Firefox Profiler, on [profile] if included.') + // Disabled --version flag since no version number in package.json. + .version(false) + .strict() + .parseSync(); + const serverConfig = { allowedHosts: ['localhost', '.gitpod.io'], host, @@ -64,6 +75,44 @@ if (localConfigExists) { } } +const profilerUrl = `http://${host}:${port}`; +if (argv.profile) { + // Spin up a simple http server serving the profile file. + const profileServer = http.createServer((req, res) => { + res.setHeader('Access-Control-Allow-Origin', profilerUrl); + const fileStream = fs.createReadStream(argv.profile); + fileStream.pipe(res); + }); + + // Close the profile server on CTRL-C. + process.on('SIGINT', () => profileServer.close()); + process.on('SIGTERM', () => profileServer.close()); + + // Delete "open" target (if any) in serverConfig. + if ( + typeof serverConfig.open === 'object' && + !Array.isArray(serverConfig.open) && + serverConfig.open !== null + ) { + delete serverConfig.open.target; + } else { + delete serverConfig.open; + } + + // Save and delete "open" property from serverConfig so that + // webpack-dev-server doesn't open anything in tandem. + const openOptions = serverConfig.open; + delete serverConfig.open; + + // Open on profile. + profileServer.listen(0, host, () => { + const profileFromUrl = `${profilerUrl}/from-url/${encodeURIComponent( + `http://${host}:${profileServer.address().port}/` + )}`; + open(profileFromUrl, openOptions); + }); +} + const server = new WebpackDevServer(serverConfig, webpack(config)); server .start() @@ -72,7 +121,7 @@ server '------------------------------------------------------------------------------------------'; console.log(barAscii); - console.log(`> Firefox Profiler is listening at: http://${host}:${port}\n`); + console.log(`> Firefox Profiler is listening at: ${profilerUrl}\n`); if (port === 4242) { console.log( '> You can change this default port with the environment variable FX_PROFILER_PORT.\n' diff --git a/yarn.lock b/yarn.lock index 39fbf9581b..30f21fbcab 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3555,6 +3555,15 @@ cliui@^7.0.2: strip-ansi "^6.0.0" wrap-ansi "^7.0.0" +cliui@^8.0.1: + version "8.0.1" + resolved "https://registry.yarnpkg.com/cliui/-/cliui-8.0.1.tgz#0c04b075db02cbfe60dc8e6cf2f5486b1a3608aa" + integrity sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ== + dependencies: + string-width "^4.2.0" + strip-ansi "^6.0.1" + wrap-ansi "^7.0.0" + clone-deep@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/clone-deep/-/clone-deep-4.0.1.tgz#c19fd9bdbbf85942b4fd979c84dcf7d5f07c2387" @@ -9179,10 +9188,10 @@ only@~0.0.2: resolved "https://registry.yarnpkg.com/only/-/only-0.0.2.tgz#2afde84d03e50b9a8edc444e30610a70295edfb4" integrity sha1-Kv3oTQPlC5qO3EROMGEKcCle37Q= -open@^8.0.9, open@^8.4.0: - version "8.4.0" - resolved "https://registry.yarnpkg.com/open/-/open-8.4.0.tgz#345321ae18f8138f82565a910fdc6b39e8c244f8" - integrity sha512-XgFPPM+B28FtCCgSb9I+s9szOC1vZRSwgWsRUA5ylIxRTgKozqjOCrVOqGsYABPYK5qnfqClxZTFBa8PKt2v6Q== +open@^8.0.9, open@^8.4.0, open@^8.4.1: + version "8.4.1" + resolved "https://registry.yarnpkg.com/open/-/open-8.4.1.tgz#2ab3754c07f5d1f99a7a8d6a82737c95e3101cff" + integrity sha512-/4b7qZNhv6Uhd7jjnREh1NjnPxlTq+XNWPG88Ydkj5AILcA5m3ajvcg57pB24EQjKv0dK62XnDqk9c/hkIG5Kg== dependencies: define-lazy-prop "^2.0.0" is-docker "^2.1.1" @@ -13179,7 +13188,7 @@ yargs-parser@^20.2.2, yargs-parser@^20.2.3: resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.9.tgz#2eb7dc3b0289718fc295f362753845c41a0c94ee" integrity sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w== -yargs-parser@^21.0.0, yargs-parser@^21.1.1: +yargs-parser@^21.1.1: version "21.1.1" resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-21.1.1.tgz#9096bceebf990d21bb31fa9516e0ede294a77d35" integrity sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw== @@ -13214,18 +13223,18 @@ yargs@^16.0.0, yargs@^16.0.3: y18n "^5.0.5" yargs-parser "^20.2.2" -yargs@^17.3.1: - version "17.4.1" - resolved "https://registry.yarnpkg.com/yargs/-/yargs-17.4.1.tgz#ebe23284207bb75cee7c408c33e722bfb27b5284" - integrity sha512-WSZD9jgobAg3ZKuCQZSa3g9QOJeCCqLoLAykiWgmXnDo9EPnn4RPf5qVTtzgOx66o6/oqhcA5tHtJXpG8pMt3g== +yargs@^17.3.1, yargs@^17.6.2: + version "17.6.2" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-17.6.2.tgz#2e23f2944e976339a1ee00f18c77fedee8332541" + integrity sha512-1/9UrdHjDZc0eOU0HxOHoS78C69UD3JRMvzlJ7S79S2nTaWRA/whGCTV8o9e/N/1Va9YIV7Q4sOxD8VV4pCWOw== dependencies: - cliui "^7.0.2" + cliui "^8.0.1" escalade "^3.1.1" get-caller-file "^2.0.5" require-directory "^2.1.1" string-width "^4.2.3" y18n "^5.0.5" - yargs-parser "^21.0.0" + yargs-parser "^21.1.1" ylru@^1.2.0: version "1.2.1"