Deploy June 29, 2026#6131
Merged
Merged
Conversation
This doesn't change anything about the profile format, this is just about the derived in-memory representation.
`JSON.stringify` serializes typed arrays as objects with stringified
numeric keys (e.g. `{"0": 1, "1": 2}`), which is not what we want
when a profile contains typed arrays.
`jsonEncodeObjectWithTypedArraysAsRegularArrays` traverses the object
and converts any typed array it finds to a regular array of numbers
before it calls `JSON.stringify`.
The new function is not used yet; the next patch in this series will
switch profile serialization to use it.
…erialization. This will allow us to have typed arrays in the profile and still serialize them as regular JSON arrays whenever the profile is serialized to JSON.
Some of our tests were auto-stringifying profiles (e.g. the ones using fetchMock), which will produce bad results once profiles start containing typed arrays. Use explicit serializeProfileToJsonString calls in those places.
`RawStackTable` is being prepared to allow `frame` to be an `Int32Array` in a later commit. `Int32Array` is fixed-size and doesn't support `push`, so the existing "push to .frame and bump .length" pattern needs a builder that uses a plain `number[]` during construction and converts to the final representation via `finishRawStackTableBuilder` at the end. Switch all stack table construction sites to use the builder. The builder still produces a plain `number[]` for `frame` in this commit; the type change happens in a follow-up.
This is the first typed array that we're supporting inside the profile format. When a profile is saved in the JsonSlabs format, it will now have this column as a separate slab that doesn't require JSON parsing. Profile compacting now always turns `stackTable.frame` into a typed array, even if that column was a regular JS array in the input profile. We still allow a regular JSON array here, because profiles stored as JSON cannot contain typed arrays, and we want to use the same type definition for JSON and JSLB profiles. Here's how this change impacts profile sizes and loading times on this profile: https://storage.googleapis.com/profiler-get-symbols-fixtures/large-speedometer3-profile.json.gz | Version | .jslb.gz size | .jslb size | Load time | Profile of it loading | |---------|---------------|------------|-------------|-----------------------------------| | 64 | 122 MB | 605 MB | 7.6 seconds | https://share.firefox.dev/4ogUKba | | 65 | 125 MB | 544 MB | 6.0 seconds | https://share.firefox.dev/3Qopiem | The compressed size has grown a small bit, but the other savings are significant: - We no longer have 131 MB of text for the frame column in the JSON - the frame column is now stored in a 70 MB i32 slab. - Less time in GZ decompression, because the uncompressed size is now smaller. - There is a lot less time spent in TextDecoder.decode and JSON.parse, because we're no longer decoding and parsing 131 MB of text for the frame column.
…32Array (#6087) [Main branch (loading v60 JSLB profile)](https://main--perf-html.netlify.app/from-url/https%3A%2F%2Fstorage.googleapis.com%2Fprofiler-get-symbols-fixtures%2Flarge-speedometer3-profile.jslb.gz/) | [Deploy preview (loading v65 JSLB profile)](https://deploy-preview-6087--perf-html.netlify.app/from-url/https%3A%2F%2Fstorage.googleapis.com%2Fprofiler-get-symbols-fixtures%2Flarge-speedometer3-profile-v65.jslb.gz) --- This is the first typed array that we're supporting inside the profile format. When a profile is saved in the JsonSlabs format, it will now have this column as a separate slab that doesn't require JSON parsing. Profile compacting now always turns `stackTable.frame` into a typed array, even if that column was a regular JS array in the input profile. We still allow a regular JSON array here, because profiles stored as JSON cannot contain typed arrays, and we want to use the same type definition for JSON and JSLB profiles. Here's how this change impacts profile sizes and loading times on this profile: https://storage.googleapis.com/profiler-get-symbols-fixtures/large-speedometer3-profile.json.gz (with [this corresponding size profile](https://share.firefox.dev/49IHi9U)) | Version | .jslb.gz size | .jslb size | Load time | Profile of it loading | |---------|---------------|------------|-------------|-----------------------------------| | 64 | 122 MB | 605 MB | 7.6 seconds | https://share.firefox.dev/4ogUKba | | 65 | 125 MB | 544 MB | 6.0 seconds | https://share.firefox.dev/3Qopiem | The compressed size has grown a small bit, but the other savings are significant: - We no longer have 131 MB of text for the frame column in the JSON - the frame column is now stored in a 70 MB i32 slab. - Less time in GZ decompression, because the uncompressed size is now smaller. - There is a lot less time spent in TextDecoder.decode and JSON.parse, because we're no longer decoding and parsing 131 MB of text for the frame column.
Co-authored-by: depfu[bot] <23717796+depfu[bot]@users.noreply.github.com> Co-authored-by: Nazım Can Altınova <canaltinova@gmail.com>
This will allow us to see the package name from the cli source code and suggest updating it via npm.
…h the frontend and the cli I initially wanted to add extra information to the profile version errors in the cli, because currently we don't give any hint about how to update it. But while doing that, I realized that we could also improve the error handling of the frontend a bit more. The old frontend error was a non-localized text. This commit creates a new localized text for this type of error and serializes it in a more friendly way. Probably it's a bit of an overkill for this error as it should ideally be not seen by the users, but there were existing errors with the same way, so I wanted to be consistent. Also, the cli now shows a tip about how to update the cli.
…th the frontend and the cli (#6107) Fixes #6105 I initially wanted to add extra information to the profile version errors in the cli, because currently we don't give any hint about how to update it. But while doing that, I realized that we could also improve the error handling of the frontend a bit more. The old frontend error was a non-localized text. This commit creates a new localized text for this type of error and serializes it in a more friendly way. Probably it's a bit of an overkill for this error as it should ideally be not seen by the users, but there were existing errors with the same way, so I wanted to be consistent. Also, the cli now shows a tip about how to update the cli. Frontend: Before: <img width="693" height="417" alt="Screenshot 2026-06-17 at 15 05 53" src="https://github.com/user-attachments/assets/2cbb8876-4011-4c3e-9fa6-765319f5f4bd" /> After: <img width="758" height="488" alt="Screenshot 2026-06-17 at 15 22 31" src="https://github.com/user-attachments/assets/96bb0c5a-a6ac-47dd-9f4e-5a3b87274639" /> CLI: Before: <img width="1227" height="120" alt="Screenshot 2026-06-17 at 15 23 58" src="https://github.com/user-attachments/assets/e0d52008-6d17-4943-9e2e-cff9a8806ab7" /> After: <img width="1171" height="127" alt="Screenshot 2026-06-17 at 15 03 34" src="https://github.com/user-attachments/assets/3d9031ed-b1ec-40d5-8c13-c77fe7c82465" />
Co-authored-by: Francesco Lodolo [:flod] <flod+pontoon@mozilla.com> (it)
Co-authored-by: Théo Chevalier <theo@theochevalier.org> (fr) Co-authored-by: Mark Heijl <markh@babelzilla.org> (nl)
Co-authored-by: Michael Köhler <michael.koehler1@gmx.de> (de)
Co-authored-by: Fjoerfoks <fryskefirefox@gmail.com> (fy-NL)
Co-authored-by: Ian Neal <iann_bugzilla@blueyonder.co.uk> (en-GB)
Co-authored-by: Pin-guang Chen <petercpg@mail.moztw.org> (zh-TW)
Co-authored-by: Luna Jernberg <bittin@cafe8bitar.se> (sv-SE)
Co-authored-by: Melo46 <melo@carmu.com> (ia)
Co-authored-by: Valery Ledovskoy <valery@ledovskoy.com> (ru)
Co-authored-by: Jim Spentzos <jimspentzos2000@gmail.com> (el)
When we add the function list, we'll want to be able to sort the table by either the self column or the total column. For that use case it makes sense to have separate methods to query these values. Functionally neutral change.
…splitters under .DetailsContainer. For the function list, we'll want to split the "main content" of the panel into the list on the left and the "wings" on the right, with a splitter. But this splitter should look like a normal splitter, not like the extra thick sidebar splitter. But these sidebar splitter rules were applying to all splitters under .DetailsContainer; this reduces the scope to only apply to the actual sidebar splitter.
The function handleCallNodeTransformShortcut takes a call node index as a parameter, but it was getting the call node info separately from a selector. At the moment that's fine because this is always the right call node info. But once we add separate call node infos for things like "the callees of the selected function in the function list", the two might get out of sync, so it's better to pass the correct call node info that is compatible with the passed call node index. Functionally-neutral change.
This shortcut key is advertised by the context menu but I forgot to actually implement it.
When we add the function list, we'll want to be able to sort the table by either the self column or the total column. For that use case it makes sense to have separate methods to query these values. Functionally neutral change.
This shortcut key is advertised by the context menu but I forgot to actually implement it in #5774. https://github.com/firefox-devtools/profiler/blob/4bd8b75f1932febe86294629f66ac9d91fea4b4b/src/components/shared/CallNodeContextMenu.tsx#L703
…splitters under .DetailsContainer (#6114) For the function list, we'll want to split the "main content" of the panel into the list on the left and the "wings" on the right, with a splitter. But this splitter should look like a normal splitter, not like the extra thick sidebar splitter. But these sidebar splitter rules were applying to all splitters under .DetailsContainer; this reduces the scope to only apply to the actual sidebar splitter.
The function handleCallNodeTransformShortcut takes a call node index as a parameter, but it was getting the call node info separately from a selector. At the moment that's fine because this is always the right call node info. But once we add separate call node infos for things like "the callees of the selected function in the function list", the two might get out of sync, so it's better to pass the correct call node info that is compatible with the passed call node index. Functionally-neutral change.
Co-authored-by: depfu[bot] <23717796+depfu[bot]@users.noreply.github.com>
Co-authored-by: depfu[bot] <23717796+depfu[bot]@users.noreply.github.com> Co-authored-by: fatadel <afatkhutdinov@mozilla.com>
This will let us use them from other tree views, like the planned function list tree view.
This will let us use them from other tree views, like the planned function list tree view.
Co-authored-by: ravmn <ravmn@ravmn.cl> (es-CL)
When a marker stayed selected, its sticky tooltip rendered on top of the Re-upload and other top-bar menu panels, covering them and intercepting clicks. The panels already carry a higher z-index than tooltips (--z-arrow-panel above --z-tooltip), but .profileViewer set z-index: 0, creating a stacking context that trapped the panels at its own altitude. Tooltips render in the body-level #root-overlay layer, which paints later, so they always won regardless of the scale. Removing that z-index lets the panels and tooltips share the root stacking context, where the scale orders them correctly. Closes #6102
…ation is requested (#6127) This was a regression from #6018. Previously, we didn't have a content column in the sources table. But we now added it, and they are filled automatically during the source map resolution. We would like to add a sharing feature soon, but this needs to be an explicit approval. Due to the early return in this `sanitizePII` function, we were mistakenly keeping these content values if we hit the early return (if the user wants to sanitize nothing). But even if the user wants to upload everything, we should unconditionally sanitize the sources.
Co-authored-by: Fabio Tomat <dark.tmtfx@gmail.com> (fur)
This lets us build the timing rows lazily, as the flame graph scrolls them into view. Building the rows isn't that expensive with the regular flame graph, but it'll be more expensive for the inverted "calls to the selected function" icicle flame graph that I'm planning to add to the function list panel.
This takes advantage of the fact that the flame graph now requests a row only once it's on the screen. Note that there's a difference between "FlameGraphRows" and "FlameGraphTiming" rows. The FlameGraphRows are still computed eagerly. It's just the timing that is now computed lazily per displayed row. The FlameGraphRows are based on the call node info and independent of sample counts and preview selection. When switching to the Flame Graph panel on https://share.firefox.dev/4g4xGue , with a flame graph canvas height of 564px, I'm getting the following profiles: Before: https://share.firefox.dev/4w2QEG3 (410ms tab switch) After: https://share.firefox.dev/3QYB7IA (272ms tab switch) In those profiles you can also see the different memory usage characteristics.
Before this PR, we were always computing positioning information for the entire flame graph, even though the viewport is usually too small to display the entire flame graph - only the N root-most rows are displayed, until the user starts scrolling / panning. This PR changes it so that we only compute the box positions / "FlameGraphTiming" for the rows that get rendered by the flame graph canvas. This speeds up the initial render of the flame graph panel. When switching to the Flame Graph panel on https://share.firefox.dev/4g4xGue , with a flame graph canvas height of 564px, I'm getting the following profiles: Before: https://share.firefox.dev/4w2QEG3 (410ms tab switch) After: https://share.firefox.dev/3QYB7IA (272ms tab switch)
This will let us use flame graphs in other places, for example in the function list panel (for callee / caller views) or in a benchmark comparison view. ConnectedFlameGraph is the connected version and works as before. This commit also removes MaybeFlameGraph. The flame graph no longer respects the global "is inverted" flag, so the performance warning in MaybeFlameGraph was never shown. The only other functionality of MaybeFlameGraph was that it displayed "empty reasons" when the preview selection was empty; this part has been subsumed into ConnectedFlameGraph.
This will let us use flame graphs in other places, for example in the function list panel (for callee / caller views) or in a benchmark comparison view. ConnectedFlameGraph is the connected version and works as before. This commit also removes MaybeFlameGraph. The flame graph no longer respects the global "is inverted" flag, so the performance warning in MaybeFlameGraph was never shown. The only other functionality of MaybeFlameGraph was that it displayed "empty reasons" when the preview selection was empty; this part has been subsumed into ConnectedFlameGraph.
Updated locales: de, el, en-GB, es-CL, fr, fur, fy-NL, ia, it, nl, ru, sv-SE, zh-TW.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Changes:
[Nazım Can Altınova] Bump profiler-cli version to 0.3.0 (#6104)
[Markus Stange] First typed array: Allow profile.shared.stackTable.frame to be an Int32Array (#6087)
[Nazım Can Altınova] Show more user friendly errors for unsupported profile version in both the frontend and the cli (#6107)
[fatadel] Expose counter information in profiler-cli (#6084)
[Markus Stange] Split getSelfAndTotal. (#6113)
[Markus Stange] Add missing transform shortcut key handling for S (focus-self) (#6117)
[Markus Stange] Remove unused isInverted prop from FlameGraphCanvas. (#6116)
[Markus Stange] Change sidebar splitter CSS to only apply to the sidebar, not to all splitters under .DetailsContainer (#6114)
[Markus Stange] Pass callNodeInfo to handleCallNodeTransformShortcut. (#6115)
[Markus Stange] Move column declarations out into a separate file (#6119)
[fatadel] Keep menu panels above the selected-marker tooltip (#6125)
[Nazım Can Altınova] Make sure to always sanitize source contents even when no PII sanitization is requested (#6127)
[Markus Stange] Compute FlameGraphTiming rows lazily (#6126)
[Markus Stange] Create a non-connected FlameGraph component (#6118)
[Nazım Can Altınova] 🔃 Sync: l10n -> main (June 29, 2026) (#6130)
And special thanks to our localizers:
de: Michael Köhler
el: Jim Spentzos
en-GB: Ian Neal
es-CL: ravmn
fr: Théo Chevalier
fur: Fabio Tomat
fy-NL: Fjoerfoks
ia: Melo46
it: Francesco Lodolo [:flod]
nl: Mark Heijl
ru: Valery Ledovskoy
sv-SE: Luna Jernberg
zh-TW: Pin-guang Chen