diff --git a/.circleci/config.yml b/.circleci/config.yml index c408d57941..dea93e7e5a 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -13,7 +13,7 @@ orbs: commands: checkout-and-dependencies: - description: "Checkout and install dependencies, managing a cache" + description: 'Checkout and install dependencies, managing a cache' steps: - checkout - restore_cache: @@ -96,7 +96,7 @@ jobs: steps: - add_ssh_keys: fingerprints: - - "92:e1:5d:84:70:96:c5:19:76:55:1c:b1:7a:12:9e:53" + - '92:e1:5d:84:70:96:c5:19:76:55:1c:b1:7a:12:9e:53' - checkout - run: git config user.email "perf-html@mozilla.com" - run: git config user.name "Firefox Profiler [bot]" @@ -118,8 +118,8 @@ workflows: l10n-sync: triggers: - schedule: - # CircleCI is using UTC timezone. So, this will be triggered at 8 AM UTC every day. - cron: "0 8 * * *" + # CircleCI is using UTC timezone. So, this will be triggered at 8 AM UTC every day. + cron: '0 8 * * *' filters: branches: only: diff --git a/.eslintrc.js b/.eslintrc.js index f5154b4f16..f42e958731 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -7,14 +7,12 @@ module.exports = { node: true, }, parser: '@babel/eslint-parser', - plugins: ['@babel', 'react', 'flowtype', 'import', 'prettier'], + plugins: ['@babel', 'react', 'flowtype', 'import'], extends: [ 'eslint:recommended', 'plugin:react/recommended', 'plugin:flowtype/recommended', - // This works with the prettier plugin, this needs to be at the end always. - // Replace it with the "prettier" config if we remove the plugin. - 'plugin:prettier/recommended', + 'prettier', ], parserOptions: { ecmaVersion: '2017', diff --git a/.gitpod.yml b/.gitpod.yml index 6a3361b76b..53729ca81e 100644 --- a/.gitpod.yml +++ b/.gitpod.yml @@ -1,8 +1,8 @@ ports: -- port: 4242 + - port: 4242 tasks: -- before: nvm install 18 - init: yarn install - command: FX_PROFILER_HOST="0.0.0.0" yarn start -- openMode: split-right - command: printf "\nFirefox Profiler ❤ Gitpod\nWelcome to this virtual environment.\nYou can type your commands in this terminal.\n\n" + - before: nvm install 18 + init: yarn install + command: FX_PROFILER_HOST="0.0.0.0" yarn start + - openMode: split-right + command: printf "\nFirefox Profiler ❤ Gitpod\nWelcome to this virtual environment.\nYou can type your commands in this terminal.\n\n" diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 0000000000..19c47285c8 --- /dev/null +++ b/.prettierignore @@ -0,0 +1,5 @@ +src/types/libdef/npm +docs-user +src/test/fixtures/ +res/zee-worker.js +dist diff --git a/.prettierrc.js b/.prettierrc.js index 7817942102..a425d3f761 100644 --- a/.prettierrc.js +++ b/.prettierrc.js @@ -1,5 +1,4 @@ module.exports = { singleQuote: true, trailingComma: 'es5', - parser: "babel-flow", }; diff --git a/.stylelintrc b/.stylelintrc index a824680f73..9711c2349e 100644 --- a/.stylelintrc +++ b/.stylelintrc @@ -1,22 +1,12 @@ { - "plugins": [ - "stylelint-prettier" - ], - "extends": [ - "stylelint-config-standard", - "stylelint-config-idiomatic-order", - ], + "extends": ["stylelint-config-standard", "stylelint-config-idiomatic-order"], "rules": { - "prettier/prettier": [ - true, - { "parser": "css" } - ], "alpha-value-notation": null, "function-url-quotes": null, "keyframes-name-pattern": null, "length-zero-no-unit": null, "selector-class-pattern": null, "import-notation": null, - "media-feature-range-notation": "prefix", + "media-feature-range-notation": "prefix" } } diff --git a/.vscode/launch.json b/.vscode/launch.json index bf9d397f01..7e61ecf678 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -1,17 +1,17 @@ { - "version": "0.2.0", - "configurations": [ - // Run the current file with Jest. Only works if the current tab is a test file. - { - "name": "Run Jest", - "type": "node", - "program": "${workspaceFolder}/node_modules/jest/bin/jest", - "args": ["--runInBand", "${file}"], - "cwd": "${workspaceFolder}", - "internalConsoleOptions": "openOnSessionStart", - "request": "launch", - "outputCapture": "std", - "skipFiles": ["/**"], - }, - ] -} \ No newline at end of file + "version": "0.2.0", + "configurations": [ + // Run the current file with Jest. Only works if the current tab is a test file. + { + "name": "Run Jest", + "type": "node", + "program": "${workspaceFolder}/node_modules/jest/bin/jest", + "args": ["--runInBand", "${file}"], + "cwd": "${workspaceFolder}", + "internalConsoleOptions": "openOnSessionStart", + "request": "launch", + "outputCapture": "std", + "skipFiles": ["/**"] + } + ] +} diff --git a/.vscode/settings.json b/.vscode/settings.json index 6a30a53a52..da26b8e316 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,6 +1,6 @@ { - // Bypass check for node_modules/flow-bin/SHASUM256.txt.sign since it - // doesn't exist in flow-bin 0.96.0. - "flow.useNPMPackagedFlow": false, - "flow.pathToFlow": "${workspaceFolder}/node_modules/.bin/flow", + // Bypass check for node_modules/flow-bin/SHASUM256.txt.sign since it + // doesn't exist in flow-bin 0.96.0. + "flow.useNPMPackagedFlow": false, + "flow.pathToFlow": "${workspaceFolder}/node_modules/.bin/flow" } diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md index ab6edf7ac1..ed4f8226c0 100644 --- a/CODE_OF_CONDUCT.md +++ b/CODE_OF_CONDUCT.md @@ -1,10 +1,11 @@ ## Mozilla Community Participation Guidelines -This repository is governed by Mozilla's code of conduct and etiquette guidelines. +This repository is governed by Mozilla's code of conduct and etiquette guidelines. For more details, please read the -[Mozilla Community Participation Guidelines](https://www.mozilla.org/about/governance/policies/participation/). +[Mozilla Community Participation Guidelines](https://www.mozilla.org/about/governance/policies/participation/). ## How to Report + For more information on how to report violations of the CPG, please read our '[How to Report](https://www.mozilla.org/en-US/about/governance/policies/participation/reporting/)' page. ## Project Specific Etiquette diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 65980d0035..6d47ef1d0d 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -6,22 +6,23 @@ First off, thanks for taking the time to contribute to Mozilla and the Firefox P This project is made up of a cross section of different parts of Mozilla, including people from Firefox DevTools and from Firefox's platform team who are working on the C++ and Rust internals of Firefox. Some core Mozillians on the team are: -| - | Name | Github Handle | Position | -| - | ---- | ------------- | -------- | -| ![][julienw] | Julien Wajsberg | [@julienw](https://github.com/julienw) | Firefox Frontend Engineer | -| ![][canova] | Nazim Can Altinova| [@canova](https://github.com/canova) | Firefox Platform and Frontend Engineer | -| | Markus Stange | [@mstange](https://github.com/mstange) | Firefox Platform Engineer | -| ![][AdamHarries] | Adam Harries | [@AdamHarries](https://github.com/AdamHarries) | Firefox Platform Engineer | -| ![][davehunt]| Dave Hunt | [@davehunt](https://github.com/davehunt) | Firefox Profiler Team Manager | +| - | Name | Github Handle | Position | +| ------------------------------------------------------------------------------------------ | ------------------ | ---------------------------------------------- | -------------------------------------- | +| ![][julienw] | Julien Wajsberg | [@julienw](https://github.com/julienw) | Firefox Frontend Engineer | +| ![][canova] | Nazim Can Altinova | [@canova](https://github.com/canova) | Firefox Platform and Frontend Engineer | +| | Markus Stange | [@mstange](https://github.com/mstange) | Firefox Platform Engineer | +| ![][AdamHarries] | Adam Harries | [@AdamHarries](https://github.com/AdamHarries) | Firefox Platform Engineer | +| ![][davehunt] | Dave Hunt | [@davehunt](https://github.com/davehunt) | Firefox Profiler Team Manager | + -[mstange]:https://avatars.githubusercontent.com/mstange?size=56 -[julienw]:https://avatars.githubusercontent.com/julienw?size=56 -[canova]:https://avatars.githubusercontent.com/canova?size=56 -[davehunt]:https://avatars.githubusercontent.com/davehunt?size=56 -[AdamHarries]:https://avatars.githubusercontent.com/AdamHarries?size=56 +[mstange]: https://avatars.githubusercontent.com/mstange?size=56 +[julienw]: https://avatars.githubusercontent.com/julienw?size=56 +[canova]: https://avatars.githubusercontent.com/canova?size=56 +[davehunt]: https://avatars.githubusercontent.com/davehunt?size=56 +[AdamHarries]: https://avatars.githubusercontent.com/AdamHarries?size=56 -We're friendly and we're on the [Mozilla Matrix instance](https://chat.mozilla.org/) in the [*Firefox Profiler* channel (*#profiler:mozilla.org*)](https://chat.mozilla.org/#/room/#profiler:mozilla.org). Come chat with us if you have any questions about the project. +We're friendly and we're on the [Mozilla Matrix instance](https://chat.mozilla.org/) in the [_Firefox Profiler_ channel (_#profiler:mozilla.org_)](https://chat.mozilla.org/#/room/#profiler:mozilla.org). Come chat with us if you have any questions about the project. ## Getting started with development @@ -33,12 +34,12 @@ You can install it using `npm install -g yarn`. Please refer to [its documentati To get started clone the repo and get the web application started. - 1. Run `git clone git@github.com:firefox-devtools/profiler.git` - 2. Run `cd profiler` - 3. Run `yarn install`, this will install all of the dependencies. - 4. Run `yarn start`, this will start up the webpack server. - 5. Point your browser to [http://localhost:4242](http://localhost:4242). - 6. If port `4242` is taken, then you can run the web app on a different port: `FX_PROFILER_PORT=1234 yarn start` +1. Run `git clone git@github.com:firefox-devtools/profiler.git` +2. Run `cd profiler` +3. Run `yarn install`, this will install all of the dependencies. +4. Run `yarn start`, this will start up the webpack server. +5. Point your browser to [http://localhost:4242](http://localhost:4242). +6. If port `4242` is taken, then you can run the web app on a different port: `FX_PROFILER_PORT=1234 yarn start` Other [webpack](https://webpack.js.org/configuration/) and [webpack server](https://webpack.js.org/configuration/dev-server/) options can be set in a `webpack.local-config.js` file at the repo root. For example, if you want to disable caching and the server to automatically open the home page, put in there the following code: @@ -63,16 +64,18 @@ Gitpod will automatically install all dependencies; start the webpack server for The web app doesn't include any performance profiles by default, so you'll need to load some in. Make sure the local Webpack web server is running, and then try one of the following: - #### 1. Record a profile: - - Open `about:config` in Firefox. - - Change `devtools.performance.recording.ui-base-url` to `http://localhost:4242`. Or to the localhost with the proper port you have configured. - - Ensure the profiler menu button is active by clicking the button on your local profiler deployment homepage, usually http://localhost:4242, to enable it. - - Record a profile using the menu button, and it should open up in your local environment automatically. +#### 1. Record a profile: + +- Open `about:config` in Firefox. +- Change `devtools.performance.recording.ui-base-url` to `http://localhost:4242`. Or to the localhost with the proper port you have configured. +- Ensure the profiler menu button is active by clicking the button on your local profiler deployment homepage, usually http://localhost:4242, to enable it. +- Record a profile using the menu button, and it should open up in your local environment automatically. - #### 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. +#### 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). @@ -80,17 +83,17 @@ For more information on loading a profile, visit its [documentation](./docs-deve When working on a new feature and code changes, it's important that things work correctly. We have a suite of automated tests and various automated checks to test that things are working the way we expect. These checks are run frequently, and will block certain parts of the process if they do not pass. The tests run: - * Locally when running - - `yarn test-all` - Test all the things! - - `yarn test` - Run the tests in [`./src/test/`](./src/test/). - - `yarn lint` - Run prettier, stylelint, and eslint to check for correct code formatting. - - `yarn flow` - Check the [Flow types](https://flow.org/) for correctness. - - `yarn license-check` - Check the dependencies' licenses. - * `git push` and `git commit` - - We have [husky](https://www.npmjs.com/package/husky) installed to run automated checks when committing and pushing. - - Run git commands with `--no-verify` to skip this step. This is useful for submitting broken PRs for feedback. - * Continuous integration for pull requests - - We use CircleCI to run our tests for every PR that is submitted. This gives reviewers a great way to know if things are still working as expected. +- Locally when running + - `yarn test-all` - Test all the things! + - `yarn test` - Run the tests in [`./src/test/`](./src/test/). + - `yarn lint` - Run prettier, stylelint, and eslint to check for correct code formatting. + - `yarn flow` - Check the [Flow types](https://flow.org/) for correctness. + - `yarn license-check` - Check the dependencies' licenses. +- `git push` and `git commit` + - We have [husky](https://www.npmjs.com/package/husky) installed to run automated checks when committing and pushing. + - Run git commands with `--no-verify` to skip this step. This is useful for submitting broken PRs for feedback. +- Continuous integration for pull requests + - We use CircleCI to run our tests for every PR that is submitted. This gives reviewers a great way to know if things are still working as expected. ### Updating snapshots @@ -126,7 +129,6 @@ the team will answer and assign the issue to you. If you're commenting during week-ends be aware that our team is working mostly on week days, therefore please be patient :-) - Feel free to [chat with us on Matrix](https://chat.mozilla.org/#/room/#profiler:mozilla.org) if you need help finding something you might be interested to work on or have any question. You'll need to login first, and possibly click on the link again to access our @@ -147,7 +149,7 @@ For PRs to be accepted, they go through a review process. Generally there is a f ## Merging Pull Requests Pull Requests should be merged with either option `Create a merge commit` or -`Squash and merge`, but __not__ with `Rebase and merge`. +`Squash and merge`, but **not** with `Rebase and merge`. ### Create a merge commit diff --git a/README.md b/README.md index c34e33a2cc..e124f2b6b9 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,5 @@ # Firefox Profiler + [![Matrix][matrix-badge]][matrix] The [Firefox Profiler] visualizes performance data recorded from web browsers. It is a tool designed to consume performance profiles from the Gecko Profiler but can visualize data from any profiler able to output in JSON. The interface is a web application built using [React] and [Redux] and runs entirely client-side. @@ -50,18 +51,18 @@ Please look at our [gitpod documentation](./docs-user/gitpod.md) for more inform For more detailed information on getting started contributing. We have plenty of docs available to get you started. -| | | -| ---- | --- | -|[Contributing](./CONTRIBUTING.md)| Find out in detail how to get started and get your local development environment configured. | -|[Code of Conduct](./CODE_OF_CONDUCT.md)| We want to create an open and inclusive community, we have a few guidelines to help us out. | -|[Developer Documentation](./docs-developer)| Want to know how this whole thing works? Get started here. | -|[Source Files](./src)| Dive into the inner workings of the code. Most folders have a `README.md` providing more information. | -|[End-User Documentation](https://profiler.firefox.com/docs/#/)| These docs are customized for actual users of the profiler, not just folks contributing. | -|[Gitpod documentatation](./docs-user/gitpod.md)| Start here if you want to set up a work space on gitpod. | +| | | +| -------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------- | +| [Contributing](./CONTRIBUTING.md) | Find out in detail how to get started and get your local development environment configured. | +| [Code of Conduct](./CODE_OF_CONDUCT.md) | We want to create an open and inclusive community, we have a few guidelines to help us out. | +| [Developer Documentation](./docs-developer) | Want to know how this whole thing works? Get started here. | +| [Source Files](./src) | Dive into the inner workings of the code. Most folders have a `README.md` providing more information. | +| [End-User Documentation](https://profiler.firefox.com/docs/#/) | These docs are customized for actual users of the profiler, not just folks contributing. | +| [Gitpod documentatation](./docs-user/gitpod.md) | Start here if you want to set up a work space on gitpod. | ### Discussion -Say hello on Matrix in the [*Firefox Profiler* channel (*#profiler:mozilla.org*)][matrix]. +Say hello on Matrix in the [_Firefox Profiler_ channel (_#profiler:mozilla.org_)][matrix]. ### License @@ -70,11 +71,13 @@ Say hello on Matrix in the [*Firefox Profiler* channel (*#profiler:mozilla.org*) We are very grateful to the the **zlib compression library (Jean-loup Gailly, Mark Adler and team)** for their contribution to the project. [matrix]: https://chat.mozilla.org/#/room/#profiler:mozilla.org + + [matrix-badge]: https://img.shields.io/matrix/profiler:mozilla.org?server_fqdn=mozilla.modular.im&label=matrix -[Firefox Profiler]:https://profiler.firefox.com/ -[React]:https://facebook.github.io/react/ -[Redux]:http://redux.js.org/ -[Mozilla]:https://www.mozilla.org/ -[Firefox]:https://www.mozilla.org/firefox/ +[Firefox Profiler]: https://profiler.firefox.com/ +[React]: https://facebook.github.io/react/ +[Redux]: http://redux.js.org/ +[Mozilla]: https://www.mozilla.org/ +[Firefox]: https://www.mozilla.org/firefox/ [zlib]: http://www.zlib.net/ diff --git a/__mocks__/copy-to-clipboard.js b/__mocks__/copy-to-clipboard.js index 764f168fcd..d81d23ae29 100644 --- a/__mocks__/copy-to-clipboard.js +++ b/__mocks__/copy-to-clipboard.js @@ -3,4 +3,4 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ // @flow -module.exports = (jest.fn(): string => void); +module.exports = (jest.fn(): (string) => void); diff --git a/appveyor.yml b/appveyor.yml index 8651872589..8bad0f0d07 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,6 +1,6 @@ clone_depth: 5 environment: - nodejs_version: "18.16" + nodejs_version: '18.16' platform: x64 # flow needs 64b platforms branches: diff --git a/docs-developer/README.md b/docs-developer/README.md index 81dfd5ca43..9a346683a9 100644 --- a/docs-developer/README.md +++ b/docs-developer/README.md @@ -4,27 +4,27 @@ ## The Docs - * [Firefox Profiler src docs](../src) - - [Redux action creators - `/src/actions`](../src/actions) - - [React components - `/src/components`](../src/components) - - [Manipulating and processing profiles - `/src/profile-logic`](../src/profile-logic) - - [Redux reducers - `/src/reducers`](../src/reducers) - - [Redux selectors- `/src/selectors`](../src/selectors) - - [Testing - `/src/test`](../src/test) - - [React component testing](../src/test/components) - - [Redux store testing](../src/test/store) - - [Unit testing](../src/test/unit) - - [Flow types - `/src/types`](../src/types) - - [Utility files - `/src/utils`](../src/utils) - * [Profiler architecture](./architecture.md) - * [Loading in profiles from various sources](./loading-in-profiles.md) - * [Gecko profile format](./gecko-profile-format.md) - * [Processed profile format](./processed-profile-format.md) - * [Writing a Custom Profile Importer](./custom-importer.md) - * [Markers](./markers.md) - * [Profile expiration and upgrading profiles](./upgrading-profiles.md) - * [Potential performance data sources in Gecko](./data-sources.md) - * [Call tree](./call-tree.md) - * [Frames, funcs, stacks and CallNodes in C++](./call-nodes-in-cpp.md) - * [Deploying to production](./deploying.md) - * [Symbolication](./symbolication.md) +- [Firefox Profiler src docs](../src) + - [Redux action creators - `/src/actions`](../src/actions) + - [React components - `/src/components`](../src/components) + - [Manipulating and processing profiles - `/src/profile-logic`](../src/profile-logic) + - [Redux reducers - `/src/reducers`](../src/reducers) + - [Redux selectors- `/src/selectors`](../src/selectors) + - [Testing - `/src/test`](../src/test) + - [React component testing](../src/test/components) + - [Redux store testing](../src/test/store) + - [Unit testing](../src/test/unit) + - [Flow types - `/src/types`](../src/types) + - [Utility files - `/src/utils`](../src/utils) +- [Profiler architecture](./architecture.md) +- [Loading in profiles from various sources](./loading-in-profiles.md) +- [Gecko profile format](./gecko-profile-format.md) +- [Processed profile format](./processed-profile-format.md) +- [Writing a Custom Profile Importer](./custom-importer.md) +- [Markers](./markers.md) +- [Profile expiration and upgrading profiles](./upgrading-profiles.md) +- [Potential performance data sources in Gecko](./data-sources.md) +- [Call tree](./call-tree.md) +- [Frames, funcs, stacks and CallNodes in C++](./call-nodes-in-cpp.md) +- [Deploying to production](./deploying.md) +- [Symbolication](./symbolication.md) diff --git a/docs-developer/call-nodes-in-cpp.md b/docs-developer/call-nodes-in-cpp.md index 0e59082e08..093e94825c 100644 --- a/docs-developer/call-nodes-in-cpp.md +++ b/docs-developer/call-nodes-in-cpp.md @@ -9,20 +9,20 @@ This document pertains to how C++ samples are captured, and how they are process 0x02 for (int i = 0; i < 10; i++) { 0x03 doSomething(i); 0x04 } -0x05 +0x05 0x06 someInterlude(); -0x07 +0x07 0x08 for (int i = 10; i < 20; i++) { 0x09 doSomething(i); 0x0A } -0x0B +0x0B 0x0C return 0; 0x0D } -0x0E +0x0E 0x0F void doSomething(int i) { 0x11 // do something 0x12 } -0x13 +0x13 0x14 void someInterlude() { 0x15 // do something else 0x16 } @@ -38,9 +38,9 @@ We'll have a frame for every line of the script above that was on the stack duri We have three funcs in the code above, each of them has a start address - - `main()` has address `0x01` - - `doSomething(int)` has address `0x0F` - - `someInterlude()` has address `0x14` +- `main()` has address `0x01` +- `doSomething(int)` has address `0x0F` +- `someInterlude()` has address `0x14` Each frame is assigned to one of these funcs. (We pick the func with the highest address among funcs with start address <= frame address.) @@ -48,8 +48,8 @@ Each frame is assigned to one of these funcs. (We pick the func with the highest In the first line of the `main()` function, there's only one frame on the stack: `0x02`. So we'll have a stack object with: - - frame: `0x02` - - prefix: null +- frame: `0x02` +- prefix: null When the CPU is executing `doSomething()` during the first loop, the stack will have these frames on it: @@ -60,10 +60,10 @@ When the CPU is executing `doSomething()` during the first loop, the stack will So we'll have a stack object with - - frame: `0x11` - - prefix: a stack with - - frame: `0x03` - - prefix: null +- frame: `0x11` +- prefix: a stack with + - frame: `0x03` + - prefix: null If the CPU is executing `doSomething()` from the second loop, the stack will be @@ -117,8 +117,8 @@ The takeaway here is: The symbolication process doesn't only give us strings, it In the Firefox Profiler, we want to be fully interactive before any symbol information has arrived, and stay interactive while it's arriving. That means that we need to handle collapsing of funcs gracefully without destroying any state, as far as that can be done. Here's how this works: - 1. When loading a Gecko profile, we naively create a func for every single frame. - 2. When a symbol table arrives: +1. When loading a Gecko profile, we naively create a func for every single frame. +2. When a symbol table arrives: 1. Every address that has a symbol is treated as the start of a function. 2. For each symbol, we pick one func object, and combine the func objects that we created for all frames in that address range into that one func. 3. We have a map, `oldFuncToNewFuncMap`, that records these collapsed funcs and the func they were collapsed into. diff --git a/docs-developer/call-tree.md b/docs-developer/call-tree.md index 044a9f1fdb..e3e0ee2631 100644 --- a/docs-developer/call-tree.md +++ b/docs-developer/call-tree.md @@ -41,7 +41,7 @@ This structure makes it much easier to tell where time is being spent in differe # Self and running time -With the above graph, we know that the functions at the leaf of the graph were the actual functions that were running when we took the samples. So for the leaf functions `E`, `G`, and `F`, their self times are 1ms. Their running times will be the same as their self time. Now walking up the stack, respectively the function `D`, `F`, and `H` called those functions. Since we never actually observed those functions directly running (they weren't at the end of the stack), then the self times for all of those functions would be 0ms. However, because the functions they called *did* in fact run, then the running times would be 1ms each. Walking all the way up to root of the graph we get to function `A`. This node on the call tree would have a self time of 0ms as we never actually directly observed it running, but it includes every other function that we did observe, so the running time would be 3ms. The following is a graph of all the running and self times where the first number is the running time, and the second is the self time. So `A:3,0` would be the function `A` with the running time of 3ms, and the self time of 0ms. +With the above graph, we know that the functions at the leaf of the graph were the actual functions that were running when we took the samples. So for the leaf functions `E`, `G`, and `F`, their self times are 1ms. Their running times will be the same as their self time. Now walking up the stack, respectively the function `D`, `F`, and `H` called those functions. Since we never actually observed those functions directly running (they weren't at the end of the stack), then the self times for all of those functions would be 0ms. However, because the functions they called _did_ in fact run, then the running times would be 1ms each. Walking all the way up to root of the graph we get to function `A`. This node on the call tree would have a self time of 0ms as we never actually directly observed it running, but it includes every other function that we did observe, so the running time would be 3ms. The following is a graph of all the running and self times where the first number is the running time, and the second is the self time. So `A:3,0` would be the function `A` with the running time of 3ms, and the self time of 0ms. ``` A:3,0 @@ -61,41 +61,41 @@ With the above graph, we know that the functions at the leaf of the graph were t # Functions are important not stacks and frames -One key point of the aggregation done in the call tree is that it's focused on what *functions* are called, and the relationships between them. In the profiler we collect frames, that give details about that specific frame of execution. Those are then organized using stacks. Each stack points to a frame, and its prefix (parent) stack. In C++ code a single function can have multiple frames depending on which part of the function was being executed. In JavaScript a function may suddenly be optimized and JITed midway through a series of runs. When the JIT process happens there will be new frames generated for these different implementations of the same function. The stacks in the profile describe the relationship between these individual frames. However, naively using only the frames and stacks will often not produce a particularly useful tree. So when referring to anything in the tree, what we care about is the relationship of functions called, not the individual frames and stacks from the profile. For a detailed explanation of how C++ generates multiple frames for a single function, please read [Frames, funcs, stacks and CallNodes in C++](call-nodes-in-cpp.md). +One key point of the aggregation done in the call tree is that it's focused on what _functions_ are called, and the relationships between them. In the profiler we collect frames, that give details about that specific frame of execution. Those are then organized using stacks. Each stack points to a frame, and its prefix (parent) stack. In C++ code a single function can have multiple frames depending on which part of the function was being executed. In JavaScript a function may suddenly be optimized and JITed midway through a series of runs. When the JIT process happens there will be new frames generated for these different implementations of the same function. The stacks in the profile describe the relationship between these individual frames. However, naively using only the frames and stacks will often not produce a particularly useful tree. So when referring to anything in the tree, what we care about is the relationship of functions called, not the individual frames and stacks from the profile. For a detailed explanation of how C++ generates multiple frames for a single function, please read [Frames, funcs, stacks and CallNodes in C++](call-nodes-in-cpp.md). ## Frames and stacks in JavaScript Imagine this simplified example of 3 samples of mixed C++, JavaScript Code (js), and JIT optimized JavaScript (JIT). The functions are all labeled as to their implementation. -| Sample index | Sample's stack | -| ------------ | ---------------| -| 0 | `JS::RunScript [c++] ➡ onLoad [js] ➡ a [js] ➡ b [js]` | -| 1 | `JS::RunScript [c++] ➡ onLoad [js] ➡ js::jit::IonCannon [c++] ➡ a [JIT] ➡ b [JIT]` | -| 2 | `JS::RunScript [c++] ➡ onLoad [js] ➡ js::jit::IonCannon [c++] ➡ a [JIT] ➡ b [JIT]` | +| Sample index | Sample's stack | +| ------------ | ------------------------------------------------------------------------------------------ | +| 0 | `JS::RunScript [c++] ➡ onLoad [js] ➡ a [js] ➡ b [js]` | +| 1 | `JS::RunScript [c++] ➡ onLoad [js] ➡ js::jit::IonCannon [c++] ➡ a [JIT] ➡ b [JIT]` | +| 2 | `JS::RunScript [c++] ➡ onLoad [js] ➡ js::jit::IonCannon [c++] ➡ a [JIT] ➡ b [JIT]` | This example produces the following frames: -| Frame index | Function name | Implementation | -| ----------- | ------------- | -------------- | -| 0 | `JS::RunScript` | C++ | -| 1 | `onLoad` | JavaScript | -| 2 | `a` | JavaScript | -| 3 | `b` | JavaScript | -| 4 | `js::jit::IonCannon` | C++ | -| 5 | `a` | JIT | -| 6 | `b` | JIT | +| Frame index | Function name | Implementation | +| ----------- | -------------------- | -------------- | +| 0 | `JS::RunScript` | C++ | +| 1 | `onLoad` | JavaScript | +| 2 | `a` | JavaScript | +| 3 | `b` | JavaScript | +| 4 | `js::jit::IonCannon` | C++ | +| 5 | `a` | JIT | +| 6 | `b` | JIT | For completeness, here is the stack table. It would only contain the frame index, and a stack prefix index, but it can be filled out with a little bit more information: -| Stack index | Frame index | Prefix | Frame's function | Prefix's function | -| ----------- | ----------- | ------ | ---------------- | ----------------- | -| 0 | 0 | null | `JS::RunScript` | null | -| 1 | 1 | 0 | `onLoad` | `JS::RunScript` | -| 2 | 2 | 1 | `a` | `onLoad` | -| 3 | 3 | 2 | `b` | `a` | -| 4 | 4 | 1 | `js::jit::IonCannon` | `onLoad` | -| 5 | 5 | 4 | `a` | `js::jit::IonCannon` | -| 6 | 6 | 5 | `b` | `a` | +| Stack index | Frame index | Prefix | Frame's function | Prefix's function | +| ----------- | ----------- | ------ | -------------------- | -------------------- | +| 0 | 0 | null | `JS::RunScript` | null | +| 1 | 1 | 0 | `onLoad` | `JS::RunScript` | +| 2 | 2 | 1 | `a` | `onLoad` | +| 3 | 3 | 2 | `b` | `a` | +| 4 | 4 | 1 | `js::jit::IonCannon` | `onLoad` | +| 5 | 5 | 4 | `a` | `js::jit::IonCannon` | +| 6 | 6 | 5 | `b` | `a` | Now, taking the stacks and building a call tree produces the following: @@ -113,27 +113,27 @@ Now, taking the stacks and building a call tree produces the following: This is the correct tree of what you would want to see. But since we are mixing languages together into the same stack system, it might be nice to view only JS functions. In order to do that we hide any C++ stacks, and assign them to the nearest JS stack. Our tables would be updated to look like the following. -| Sample index | Sample's stack | -| ------------ | ---------------| -| 0 | `onLoad [js] ➡ a [js] ➡ b [js]` | -| 1 | `onLoad [js] ➡ a [JIT] ➡ b [JIT]` | -| 2 | `onLoad [js] ➡ a [JIT] ➡ b [JIT]` | +| Sample index | Sample's stack | +| ------------ | ------------------------------------- | +| 0 | `onLoad [js] ➡ a [js] ➡ b [js]` | +| 1 | `onLoad [js] ➡ a [JIT] ➡ b [JIT]` | +| 2 | `onLoad [js] ➡ a [JIT] ➡ b [JIT]` | | Frame index | Function name | Implementation | | ----------- | ------------- | -------------- | -| 0 | onLoad | JavaScript | -| 1 | a | JavaScript | -| 2 | b | JavaScript | -| 3 | a | JIT | -| 4 | b | JIT | +| 0 | onLoad | JavaScript | +| 1 | a | JavaScript | +| 2 | b | JavaScript | +| 3 | a | JIT | +| 4 | b | JIT | | Stack index | Frame index | Prefix | Frame's function | Prefix's function | -| ------------ | ----------- | ------ | ---------------- | ----------------- | -| 0 | 0 | null | onLoad | null | -| 1 | 1 | 0 | a | onLoad | -| 2 | 2 | 1 | b | a | -| 3 | 3 | 0 | a | onLoad | -| 4 | 4 | 1 | b | a | +| ----------- | ----------- | ------ | ---------------- | ----------------- | +| 0 | 0 | null | onLoad | null | +| 1 | 1 | 0 | a | onLoad | +| 2 | 2 | 1 | b | a | +| 3 | 3 | 0 | a | onLoad | +| 4 | 4 | 1 | b | a | With this data if we build a call tree it now looks like this. @@ -204,19 +204,19 @@ Call trees are interesting for the information they provide, but they can be qui ## Would produce this call tree ``` - A:3,0 - | - v - B:3,0 - / \ - v v - C:2,0 H:1,0 - / \ \ - v v v + A:3,0 + | + v + B:3,0 + / \ + v v + C:2,0 H:1,0 + / \ \ + v v v D:1,0 F:1,0 F:1,1 - | | - v v - E:1,1 G:1,1 + | | + v v + E:1,1 G:1,1 ``` ## Merge (charge to caller) @@ -234,8 +234,8 @@ Merging involves removing a single CallNode from the call tree, and then assigni / \ \ | | | v v v v v v D:1,0 F:1,0 F:1,1 E:1,1 G:1,1 F:1,1 - | | - v v + | | + v v E:1,1 G:1,1 ``` @@ -272,9 +272,9 @@ The self time of an entire subtree is placed to the parent CallNode. In the case / \ \ | v v v v D:1,0 F:1,0 F:1,1 F:1,1 - | | - v v - E:1,1 G:1,1 + | | + v v + E:1,1 G:1,1 ``` ### Hide @@ -292,9 +292,9 @@ If CallNode C is hidden, then 2 samples are removed because they contain that Ca / \ \ | v v v v D:1,0 F:1,0 F:1,1 F:1,1 - | | - v v - E:1,1 G:1,1 + | | + v v + E:1,1 G:1,1 ``` ### Focus on subtree @@ -309,12 +309,12 @@ Only CallNodes that contain CallNode C are retained, and C is made as root. / \ | | v v v v C:2,0 H:1,0 E:1,1 G:1,1 - / \ \ - v v v + / \ \ + v v v D:1,0 F:1,0 F:1,1 - | | - v v - E:1,1 G:1,1 + | | + v v + E:1,1 G:1,1 ``` # How call tree modifications affect function paths diff --git a/docs-developer/custom-importer.md b/docs-developer/custom-importer.md index 6103187b89..f9d9ad024b 100644 --- a/docs-developer/custom-importer.md +++ b/docs-developer/custom-importer.md @@ -4,16 +4,16 @@ The Firefox Profiler supports a few [external profile formats](../src/profile-lo ## Useful Docs - * [Gecko profile format docs](./gecko-profile-format.md) - * [processed profile format](./processed-profile-format.md) +- [Gecko profile format docs](./gecko-profile-format.md) +- [processed profile format](./processed-profile-format.md) ## Useful code links - * [Gecko profile format type definition](../src/types/gecko-profile.js) - * [processed profile format type definition](../src/types/profile.js) - * [marker payload type definitions](../src/types/markers.js) - * [profiler data structure utilities](../src/profile-logic/data-structures.js) - * [existing importers](../src/profile-logic/import) +- [Gecko profile format type definition](../src/types/gecko-profile.js) +- [processed profile format type definition](../src/types/profile.js) +- [marker payload type definitions](../src/types/markers.js) +- [profiler data structure utilities](../src/profile-logic/data-structures.js) +- [existing importers](../src/profile-logic/import) ## How to write a profile converter @@ -35,19 +35,19 @@ The profile format uses a lot of indexes into other data structures. For instanc // Look up the stack index from the samples table. const stackIndex = thread.samples.stack[sampleIndex]; - console.log({stackIndex}) + console.log({ stackIndex }); // Look up the frame from the stack table. const frameIndex = thread.stackTable.frame[stackIndex]; - console.log({frameIndex}) + console.log({ frameIndex }); // Look up the function from the frame table. const funcIndex = thread.frameTable.func[frameIndex]; - console.log({funcIndex}) + console.log({ funcIndex }); // Look up the string index from the func table. const stringIndex = thread.funcTable.name[funcIndex]; - console.log({stringIndex}) + console.log({ stringIndex }); console.log( 'Function name of the first sample:', @@ -60,9 +60,9 @@ It is probably a good iea to read up some on profile format docs for more inform ## Tips - * Ensure that the `pid` value points to the proper threads to nest threads in the timeline. - * Processed profiles have their timestamps adjusted so that all processes use the same timeline. - * Make sure and adjust the timing for child processes. During profile processing, the Firefox Profiler adjusts Gecko profiles timings, so that markers and samples take into account the differences in start time (via `geckoProfile.meta.startTime`). See [src/profile-logic/process-profile.js](https://github.com/firefox-devtools/profiler/blob/3067dda9cbf5807948aef149e18caf4e8870ed25/src/profile-logic/process-profile.js#L997-L1010) for some examples. +- Ensure that the `pid` value points to the proper threads to nest threads in the timeline. +- Processed profiles have their timestamps adjusted so that all processes use the same timeline. +- Make sure and adjust the timing for child processes. During profile processing, the Firefox Profiler adjusts Gecko profiles timings, so that markers and samples take into account the differences in start time (via `geckoProfile.meta.startTime`). See [src/profile-logic/process-profile.js](https://github.com/firefox-devtools/profiler/blob/3067dda9cbf5807948aef149e18caf4e8870ed25/src/profile-logic/process-profile.js#L997-L1010) for some examples. ## Samples diff --git a/docs-developer/data-sources.md b/docs-developer/data-sources.md index c113f2ba22..50eaad40ce 100644 --- a/docs-developer/data-sources.md +++ b/docs-developer/data-sources.md @@ -16,20 +16,20 @@ Samples don't record every event that happens within the system, so some informa ### More Documentation on the Gecko Profiler: - * [nsIProfiler.idl](https://dxr.mozilla.org/mozilla-central/source/tools/profiler/gecko/nsIProfiler.idl) - * [ProfileEntry.h](https://dxr.mozilla.org/mozilla-central/rev/b043233ec04f06768d59dcdfb9e928142280f3cc/tools/profiler/core/ProfileBufferEntry.h#322-411) +- [nsIProfiler.idl](https://dxr.mozilla.org/mozilla-central/source/tools/profiler/gecko/nsIProfiler.idl) +- [ProfileEntry.h](https://dxr.mozilla.org/mozilla-central/rev/b043233ec04f06768d59dcdfb9e928142280f3cc/tools/profiler/core/ProfileBufferEntry.h#322-411) - * [Profile Data Format](./profile-data) +- [Profile Data Format](./profile-data) ## Timeline - DocShell Markers (unused in the Firefox Profiler) The Gecko Profiler records marker data, but it doesn't include all of the markers available in the system. There is a competing implementation of markers used exclusively by the Firefox Devtools that are recorded per DocShell. These are not currently integrated into the Firefox Profiler. -* [Timeline C++ Implementation](https://dxr.mozilla.org/mozilla-central/source/docshell/base/timeline) -* [Timeline Devtools JS Server](https://dxr.mozilla.org/mozilla-central/source/devtools/server/performance/timeline.js) +- [Timeline C++ Implementation](https://dxr.mozilla.org/mozilla-central/source/docshell/base/timeline) +- [Timeline Devtools JS Server](https://dxr.mozilla.org/mozilla-central/source/devtools/server/performance/timeline.js) ## Tracelogger (unused in the Firefox Profiler) While the previous performance tools collect information about how Gecko runs as a whole, Tracelogger is specific to the SpiderMonkey engine. Tracelogger is not sample based, therefore it records every step that the SpiderMonkey engine performs to run a given chunk of JavaScript code. It's primarily used by JavaScript engineers, and includes a firehose of information often reaching into the several gigs of information. There is no current integration of this information with the Firefox Profiler. -* [Tracelogger on GitHub](https://github.com/h4writer/tracelogger) +- [Tracelogger on GitHub](https://github.com/h4writer/tracelogger) diff --git a/docs-developer/deploying.md b/docs-developer/deploying.md index b27f267345..833d8ef3f8 100644 --- a/docs-developer/deploying.md +++ b/docs-developer/deploying.md @@ -19,7 +19,7 @@ Changes in Pontoon are being pushed into the `l10n` branch. They should be merge into `main` before the deployment. The easiest way is to -[create a pull request on GitHub](https://github.com/firefox-devtools/profiler/compare/main...l10n?expand=1&title=🔃%20Sync:%20l10n%20->%20main%20(DATE)). +[create a pull request on GitHub](%20main%20(DATE)>). It would be nice to list down the locales that are changed in the PR description. To be able to get the changed locales quickly, this command can be used (assuming that `upstream` is the remote you use for this repository): @@ -29,7 +29,7 @@ git fetch upstream && git diff --name-only upstream/main...upstream/l10n | awk - ``` Be careful to always use the **create a merge commit** functionality, not -*squash* or *rebase*, to keep a better history. +_squash_ or _rebase_, to keep a better history. ## How to deploy main to production @@ -44,7 +44,7 @@ It would be nice to write down the main changes in the PR description. After the PR is created all checks should run. When it's ready the PR can be merged. Be careful to always use the **create a merge commit** functionality, -not *squash* or *rebase*, to keep a better history. +not _squash_ or _rebase_, to keep a better history. Once it's done the new version should be deployed automatically. You can follow the process on [Netlify's dashboard](https://app.netlify.com/sites/perf-html/deploys) @@ -57,6 +57,7 @@ force push it. You'll need to enable force-pushing for the branch production, using the [Branch Settings on GitHub](https://github.com/firefox-devtools/profiler/settings/branches). You can use the following script: + ``` sh bin/revert-last-deployment.sh ``` diff --git a/docs-developer/gecko-profile-format.md b/docs-developer/gecko-profile-format.md index effc496c62..e9694fc977 100644 --- a/docs-developer/gecko-profile-format.md +++ b/docs-developer/gecko-profile-format.md @@ -11,8 +11,8 @@ The Gecko Profiler can be run on its own by executing the following code from a const settings = { entries: 1000000, interval: 0.4, - features: ["js", "stackwalk", "threads", "leaf"], - threads: ["GeckoMain", "Compositor"] + features: ['js', 'stackwalk', 'threads', 'leaf'], + threads: ['GeckoMain', 'Compositor'], }; Services.profiler.StartProfiler( @@ -25,7 +25,7 @@ The Gecko Profiler can be run on its own by executing the following code from a ); setTimeout(() => { - Services.profiler.getProfileDataAsync().then(profile => { + Services.profiler.getProfileDataAsync().then((profile) => { for (let i = 0; i < profile.threads.length; i++) { const thread = profile.threads[i]; } @@ -297,22 +297,22 @@ The threads of the processed profile use the above format and include the follow Profile data: - * samples - * markers +- samples +- markers Index-based table data: - * resourceTable - * frameTable - * funcTable - * stackTable - * stringTable +- resourceTable +- frameTable +- funcTable +- stackTable +- stringTable Miscellaneous data: - * name - * libs - * tid +- name +- libs +- tid Different frames can be created from the same function, and thus do not represent the unique set of functions. This function table is generated during [profiler.firefox.com]'s pre-processing step. Frames can provide additional information about the various ways the function was executed, while it's also useful to have a list of only the functions, so both types of information are retained. diff --git a/docs-developer/loading-in-profiles.md b/docs-developer/loading-in-profiles.md index c9fa19031a..18e9ec3ca7 100644 --- a/docs-developer/loading-in-profiles.md +++ b/docs-developer/loading-in-profiles.md @@ -60,7 +60,10 @@ const options = { // The server must be "https" const server = https.createServer(options, (request, response) => { // You must give access to profiler.firefox.com - response.setHeader('Access-Control-Allow-Origin', 'https://profiler.firefox.com'); + response.setHeader( + 'Access-Control-Allow-Origin', + 'https://profiler.firefox.com' + ); // You could also do * to allow anyone to load it: // response.setHeader('Access-Control-Allow-Origin', "*"); @@ -74,7 +77,7 @@ const server = https.createServer(options, (request, response) => { }); // Start listening on the port. -server.listen(PORT, err => { +server.listen(PORT, (err) => { if (err) { return console.log('Error starting server', err); } diff --git a/docs-developer/markers.md b/docs-developer/markers.md index 24f5b1706f..d78e6f508a 100644 --- a/docs-developer/markers.md +++ b/docs-developer/markers.md @@ -28,21 +28,21 @@ Coming soon ## Paint markers -| Category | Type | Explanation | -| ------------ | ------------------ | ----------- | -| Paint | RefreshDriverTick | This is a container marker that wraps all phases of a refresh tick. | -| Paint | FireScrollEvent | The time it took call event listeners for “scroll” events after the scroll position in a scrollable frame changed. | -| Paint | requestAnimationFrame callbacks | The time it takes to call JavaScript requestAnimationFrame callbacks during a refresh tick. | -| Paint | Styles | The time it takes to recompute CSS style information on any changed elements in the document. | -| Paint | Reflow | The time it took to recompute layout. | -| Paint | DispatchSynthMouseMove | The time it takes to fire mouseover and mouseout events (and running any JS event handlers) after a layout change or scroll caused the mouse to be over a different element. | -| Paint | DisplayList | The time it takes to build a DisplayList for the window, which is a list of primives that need to be rendered. | -| Paint | LayerBuilding | The time it took to generate a new layer tree based on the new display list. | -| Paint | Rasterize | The time it takes to turn the display items that were assigned to a PaintedLayer into pixels in that layer’s buffer. | -| Paint | ForwardTransaction | The time it takes to forward changes to the layer tree to the compositor. | -| Paint | NotifyDidPaint | The time it takes for a post-refresh garbage collection to run. (The refresh driver notifies the JS engine that it painted, and the JS engine reacts by running GC for a brief time.) | -| Paint | LayerTransaction | The time it takes on the compositor thread to process the list of changes that is contained in a layer transaction. This includes texture upload. | -| Paint | Composite | The time it takes to combine layers, on the compositor thread, and display them in the window. | +| Category | Type | Explanation | +| -------- | ------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Paint | RefreshDriverTick | This is a container marker that wraps all phases of a refresh tick. | +| Paint | FireScrollEvent | The time it took call event listeners for “scroll” events after the scroll position in a scrollable frame changed. | +| Paint | requestAnimationFrame callbacks | The time it takes to call JavaScript requestAnimationFrame callbacks during a refresh tick. | +| Paint | Styles | The time it takes to recompute CSS style information on any changed elements in the document. | +| Paint | Reflow | The time it took to recompute layout. | +| Paint | DispatchSynthMouseMove | The time it takes to fire mouseover and mouseout events (and running any JS event handlers) after a layout change or scroll caused the mouse to be over a different element. | +| Paint | DisplayList | The time it takes to build a DisplayList for the window, which is a list of primives that need to be rendered. | +| Paint | LayerBuilding | The time it took to generate a new layer tree based on the new display list. | +| Paint | Rasterize | The time it takes to turn the display items that were assigned to a PaintedLayer into pixels in that layer’s buffer. | +| Paint | ForwardTransaction | The time it takes to forward changes to the layer tree to the compositor. | +| Paint | NotifyDidPaint | The time it takes for a post-refresh garbage collection to run. (The refresh driver notifies the JS engine that it painted, and the JS engine reacts by running GC for a brief time.) | +| Paint | LayerTransaction | The time it takes on the compositor thread to process the list of changes that is contained in a layer transaction. This includes texture upload. | +| Paint | Composite | The time it takes to combine layers, on the compositor thread, and display them in the window. | ### Additional context information for paint markers diff --git a/docs-developer/symbolication.md b/docs-developer/symbolication.md index 10bcd0ba26..e08bc28c5f 100644 --- a/docs-developer/symbolication.md +++ b/docs-developer/symbolication.md @@ -18,16 +18,16 @@ So the profiler gets the rest of the call stack frames by finding addresses to c In order to translate these addresses into symbols, a few things need to happen: - 1. For each address, identify the binary that occupies that area of memory, if any. - 2. Translate the address into a binary-relative offset, by subtracting the address in memory where the mapping of the binary starts. - 3. Consult a symbol table which maps binary-relative offsets to strings. +1. For each address, identify the binary that occupies that area of memory, if any. +2. Translate the address into a binary-relative offset, by subtracting the address in memory where the mapping of the binary starts. +3. Consult a symbol table which maps binary-relative offsets to strings. ## Where symbol tables come from There are fundamentally two classes of binaries that we need to symbolicate: - - Binaries we create ourselves, by compiling our own code - - Existing binaries from other sources, most notably system libraries from the operating system that we run on +- Binaries we create ourselves, by compiling our own code +- Existing binaries from other sources, most notably system libraries from the operating system that we run on For binaries that we create ourselves, the compiler automatically creates symbol information and debug information. On Linux and macOS, the symbol information is embedded in the resulting binary itself, and on Windows, it is stored in a separate .pdb file. @@ -41,26 +41,26 @@ The following is an example breakpad entry for a function symbol. More informati FUNC c184 30 0 nsQueryInterfaceWithError::operator()(nsID const&, void**) const ``` -| Text | Explanation | -| ---- | ----------- | -| FUNC | Indicates that this is a function record. | +| Text | Explanation | +| ---- | ---------------------------------------------------------------------- | +| FUNC | Indicates that this is a function record. | | c184 | The hexadecimal memory location relative to the module's load address. | -| 30 | The hexadecimal length of bytes in the function. | -| 0 | The hexadecimal length of bytes of the size of the parameters. | -| ... | The remaining `nsQueryInterfaceWithError` text is the actual symbol. | +| 30 | The hexadecimal length of bytes in the function. | +| 0 | The hexadecimal length of bytes of the size of the parameters. | +| ... | The remaining `nsQueryInterfaceWithError` text is the actual symbol. | A binary is identified by its debugName and by an identifier. The identifier is its breakpadId, which is a string of 33 hex characters. The Mozilla symbol server makes symbol information from the breakpad symbol files available in two forms: - 1. It serves the raw breakpad symbol files at `https://symbols.mozilla.org/debugName/breakpadId/debugName.sym`, for example at [https://symbols.mozilla.org/firefox/5147A2EC44F038CCB9DE2D0AC50A15E30/firefox.sym](https://symbols.mozilla.org/firefox/5147A2EC44F038CCB9DE2D0AC50A15E30/firefox.sym). - 2. It has a [publicly accessible API to obtain symbol information for only certain addresses](https://tecken.readthedocs.io/en/latest/symbolication.html). +1. It serves the raw breakpad symbol files at `https://symbols.mozilla.org/debugName/breakpadId/debugName.sym`, for example at [https://symbols.mozilla.org/firefox/5147A2EC44F038CCB9DE2D0AC50A15E30/firefox.sym](https://symbols.mozilla.org/firefox/5147A2EC44F038CCB9DE2D0AC50A15E30/firefox.sym). +2. It has a [publicly accessible API to obtain symbol information for only certain addresses](https://tecken.readthedocs.io/en/latest/symbolication.html). These breakpad symbol files are also used for symbolicating crash reports. Breakpad is the name of the crash reporting system that Firefox uses. -Windows system libraries do not contain symbol information in the binary file, and Windows does not ship with pdb files for any system libraries. Instead, [Microsoft has a symbol server](https://msdn.microsoft.com/en-us/library/windows/desktop/ee416588\(v=vs.85\).aspx#symbol_servers) that provides the pdb files (?) for all their system libraries. +Windows system libraries do not contain symbol information in the binary file, and Windows does not ship with pdb files for any system libraries. Instead, [Microsoft has a symbol server]() that provides the pdb files (?) for all their system libraries. The Mozilla symbol server is somehow connected to the Microsoft symbol server, and automatically creates breakpad symbol files for all the libraries that Microsoft's symbol server provides symbol information for. On macOS, system libraries contain symbol information in the binary files. It can be extracted using command line tools like `nm`. @@ -73,10 +73,9 @@ The utility that the build process uses to create breakpad symbol files is calle For local Firefox builds, symbol information can be extracted the following ways: - * On Windows, you can extract it from the pdb file that the compiler produced for that library. The easiest way to do that is to run dump_syms on the pdb file. - - * On macOS and Linux, you can run dump_syms on the binary itself. Or you can run "nm" on the binary itself. +- On Windows, you can extract it from the pdb file that the compiler produced for that library. The easiest way to do that is to run dump_syms on the pdb file. +- On macOS and Linux, you can run dump_syms on the binary itself. Or you can run "nm" on the binary itself. ## How the profiler does symbolication @@ -86,19 +85,19 @@ When [profiler.firefox.com] receives the profile from the gecko profiler add-on, Then, the following things happen: - 1. [profiler.firefox.com] iterates over all addresses in the profile's call stacks, finds which binary they came from by comparing them to the library information stored in the profile, and converts them into binary-relative offsets. +1. [profiler.firefox.com] iterates over all addresses in the profile's call stacks, finds which binary they came from by comparing them to the library information stored in the profile, and converts them into binary-relative offsets. - 2. [profiler.firefox.com] checks for which of these libraries it has cached symbol tables in IndexedDB. +2. [profiler.firefox.com] checks for which of these libraries it has cached symbol tables in IndexedDB. 1. For libraries with cached symbol tables, it uses those symbol tables to map the addresses to symbols. - 3. For all other libraries, it requests symbols for the collected addresses using the Mozilla symbolication API. The results of this are *not* cached. +3. For all other libraries, it requests symbols for the collected addresses using the Mozilla symbolication API. The results of this are _not_ cached. 1. The Mozilla symbolication API will be able to symbolicate any libraries for which there exist breakpad symbol files on the Mozilla symbol server, so: official Firefox builds, most of Windows system libraries, some macOS system libraries. - 4. For any libraries which the Mozilla symbolication API was not able to find symbols, [profiler.firefox.com] requests a symbol table from the add-on, which will forward the request to the geckoProfiler WebExtension API. +4. For any libraries which the Mozilla symbolication API was not able to find symbols, [profiler.firefox.com] requests a symbol table from the add-on, which will forward the request to the geckoProfiler WebExtension API. - 1. The WebExtension API will try multiple methods to obtain symbol information. The code for this is at https://searchfox.org/mozilla-central/rev/7e663b9fa578d425684ce2560e5fa2464f504b34/browser/components/extensions/ext-geckoProfiler.js#409-473 . + 1. The WebExtension API will try multiple methods to obtain symbol information. The code for this is at https://searchfox.org/mozilla-central/rev/7e663b9fa578d425684ce2560e5fa2464f504b34/browser/components/extensions/ext-geckoProfiler.js#409-473 . 1. First, it will try to find a breakpad symbol file for the library in the objdir, if the Firefox build that is being symbolicated is a local build. These symbol files only exist if the user has run "mach buildsymbols" after compiling. @@ -108,8 +107,8 @@ Then, the following things happen: 4. On Windows, if this is a local build, it'll try to find "dump_syms.exe" in the objdir and run it on the pdb file. - 5. Symbol tables that were obtained in step 4 are sent to [profiler.firefox.com] and the page caches them in the IndexedDB table. The relevant addresses are symbolicated using the symbol table. +5. Symbol tables that were obtained in step 4 are sent to [profiler.firefox.com] and the page caches them in the IndexedDB table. The relevant addresses are symbolicated using the symbol table. - 6. Libraries for which no symbol information could be obtained stay unsymbolicated. +6. Libraries for which no symbol information could be obtained stay unsymbolicated. [profiler.firefox.com]: https://profiler.firefox.com diff --git a/docs-developer/upgrading-profiles.md b/docs-developer/upgrading-profiles.md index 89ed8dfaab..1e6c4e6d7f 100644 --- a/docs-developer/upgrading-profiles.md +++ b/docs-developer/upgrading-profiles.md @@ -8,10 +8,10 @@ The Firefox Profiler is committed to maintaining support for loading profiles fo Changes to the profile format need to happen in this order: - 1. The format change needs to be proposed and agreed upon. - 2. [profiler.firefox.com] needs to be updated to be able to deal with that new format, and an upgrader from the current format to the new format needs to be added. - 3. Usually there needs to be a corresponding change to the "processed profile" format, which is used in [profiler.firefox.com] internally. If that's the case, the processed profile format version needs to be incremented, an upgrader for old processed profiles needs to be added, and processProfile needs to be changed to output the new version of the processed profile format. - 4. Once the [profiler.firefox.com] update is rolled out, the actual Firefox change can land. +1. The format change needs to be proposed and agreed upon. +2. [profiler.firefox.com] needs to be updated to be able to deal with that new format, and an upgrader from the current format to the new format needs to be added. +3. Usually there needs to be a corresponding change to the "processed profile" format, which is used in [profiler.firefox.com] internally. If that's the case, the processed profile format version needs to be incremented, an upgrader for old processed profiles needs to be added, and processProfile needs to be changed to output the new version of the processed profile format. +4. Once the [profiler.firefox.com] update is rolled out, the actual Firefox change can land. ## Background on upgrading @@ -37,9 +37,9 @@ The setup described above only solves one direction of compatibility: You can ru That means that profile format changes need to be made in this order: - 1. The format change needs to be proposed and agreed upon. - 2. [profiler.firefox.com] needs to be updated to be able to deal with that new format, and an upgrader from the current format to the new format needs to be added to it. - 3. *After* the [profiler.firefox.com] update is rolled out, the actual Firefox change can land. +1. The format change needs to be proposed and agreed upon. +2. [profiler.firefox.com] needs to be updated to be able to deal with that new format, and an upgrader from the current format to the new format needs to be added to it. +3. _After_ the [profiler.firefox.com] update is rolled out, the actual Firefox change can land. We'll see how this works out. It might be annoying for users who have updated their Firefox Nightly but not opened up [profiler.firefox.com] between (2) and (3), and then try to load a new profile into an old version of [profiler.firefox.com] from their service worker cache. They're only one page refresh away from success, though. @@ -63,14 +63,14 @@ The processed profile version is stored in the field `profile.meta.preprocessedP When the Gecko profile format changes (due to a change in Gecko): - - The version number (`profile.meta.version`) needs to be incremented in Gecko. - - In [app-logic/constants.js](../src/app-logic/constants.js), `GECKO_PROFILE_VERSION` needs to be set to that new version number, and a conversion function from the old to the new version needs to be added to `_updaters`. - - Profile processing may need to be adjusted to parse the new Gecko profile format version. +- The version number (`profile.meta.version`) needs to be incremented in Gecko. +- In [app-logic/constants.js](../src/app-logic/constants.js), `GECKO_PROFILE_VERSION` needs to be set to that new version number, and a conversion function from the old to the new version needs to be added to `_updaters`. +- Profile processing may need to be adjusted to parse the new Gecko profile format version. When the processed profile format changes (e.g. because a different data format seems adequate, or because new data from the Gecko profile needs to be accommodated): - - `processProfile` needs to be changed to output the new format. - - In [app-logic/constants.js](../src/app-logic/constants.js), `PROCESSED_PROFILE_VERSION` needs to be incremented and an update function needs to be added to `_updaters`. +- `processProfile` needs to be changed to output the new format. +- In [app-logic/constants.js](../src/app-logic/constants.js), `PROCESSED_PROFILE_VERSION` needs to be incremented and an update function needs to be added to `_updaters`. At all times, `processProfile` only has code that converts the latest version of the Gecko profile format into the latest version of the processed profile diff --git a/examples/canvas-drawing.html b/examples/canvas-drawing.html index 264f25b95a..1f268a95f1 100644 --- a/examples/canvas-drawing.html +++ b/examples/canvas-drawing.html @@ -1,26 +1,27 @@ - + Firefox Profiler example — Canvas Drawing - +

Firefox Profiler example — Canvas Drawing

- This example shows the performance impacts of drawing rectangles using canvas - context calls. It shows how the CSS parser is used when setting the colors for - fillStyle, as well as what requestAnimationFrame loops look like. + This example shows the performance impacts of drawing rectangles using + canvas context calls. It shows how the CSS parser is used when setting the + colors for fillStyle, as well as what requestAnimationFrame loops look + like.

- - + +

- +

-
+
@@ -32,38 +33,36 @@

Firefox Profiler example — Canvas Drawing

const toggleEl = document.querySelector('#toggle'); canvas.width = container.offsetWidth; - canvas.height = container.offsetWidth * 9 / 16; + canvas.height = (container.offsetWidth * 9) / 16; const w = canvas.width; const h = canvas.height; let useMixedColors = false; - toggleEl.addEventListener('click', () => { - useMixedColors = !useMixedColors; - }, false); + toggleEl.addEventListener( + 'click', + () => { + useMixedColors = !useMixedColors; + }, + false + ); - function draw () { + function draw() { const count = +numberEl.value; - const now = Date.now() + const now = Date.now(); ctx.clearRect(0, 0, w, h); ctx.fillStyle = 'red'; for (let i = 0; i < count; i++) { const unitI = i / count; - const waveHeight = ( + const waveHeight = 0.1 * Math.sin(now * 0.013 + 100 * unitI) + - 0.8 * Math.sin(now * 0.001 + 10 * unitI) - ); + 0.8 * Math.sin(now * 0.001 + 10 * unitI); if (useMixedColors) { const hue = Math.floor((waveHeight + 1) * 0.5 * 360); - ctx.fillStyle = `hsl(${hue}, 100%, 50%)` + ctx.fillStyle = `hsl(${hue}, 100%, 50%)`; } - ctx.fillRect( - w * unitI, - h * 0.5 + h * 0.5 * waveHeight - 2.5, - 5, - 5 - ); + ctx.fillRect(w * unitI, h * 0.5 + h * 0.5 * waveHeight - 2.5, 5, 5); } requestAnimationFrame(draw); } diff --git a/examples/dom-mutation.html b/examples/dom-mutation.html index bc9cd44b5b..d29dfb856b 100644 --- a/examples/dom-mutation.html +++ b/examples/dom-mutation.html @@ -1,21 +1,22 @@ - + Firefox Profiler example — DOM Mutation - +

Firefox Profiler example — DOM Mutation

- This demo creates 100 divs, and then randomly mutates the contents of some divs - with random letters. It either changes the content straight, or flushes the layout - through computing the boundingClientRect, forcing the browser to compute the layout. - Reflows are a common performance problem in web apps. + This demo creates 100 divs, and then randomly mutates the contents of some + divs with random letters. It either changes the content straight, or + flushes the layout through computing the boundingClientRect, forcing the + browser to compute the layout. Reflows are a common performance problem in + web apps.

- - + +

@@ -24,29 +25,32 @@

Firefox Profiler example — DOM Mutation

-
+