canova · GitHub

@canova

@canova

canova marked this pull request as ready for review

May 26, 2026 14:10

@canova

@canova

mstange

mstange

mstange

mstange

mstange

@canova

canova

@canova

mstange

@canova

@canova

@canova

@canova

…SOURCES
Version 7 of the WebChannel renames the GET_JS_SOURCES request field
from `sourceUuids` to `sourceIds`. The sender picks the field name based
on the negotiated WebChannel version so both older Firefox (v6) and
newer (v7+) builds are supported.
Lets the browser fetch source maps from URLs the frontend cannot reach.
The frontend identifies the bundle source via its sourceId, and the
browser returns the parsed source map.
Currently it's plumbing only. No caller dispatches GET_SOURCE_MAP yet.
Adds source-map and url as direct dependencies; `source-map` provides
the RawSourceMap type and runtime parser, and `url` is required by
source-map's URL resolution in browser bundles.
Extends the processed profile with a per-thread-shared SourceLocationTable
(stored at profile.shared.originalLocation) and a content column on the
SourceTable. Each frame and func now has a nullable originalLocation index,
set to null by all existing initializers and upgraders. Source content
stays null until JS symbolication populates it.
The new tables are plumbed through data-structures, profile-compacting,
merge-compare, and every importer.
It adds a v63 upgrader and a CHANGELOG entry. No call site reads the new
columns yet, behavior is unchanged.
The nonymous algorithm (johnjbarton.github.io/nonymous) is how
SpiderMonkey labels anonymous JS functions inside its
NameFunctions.cpp pass. Adds a parser and serializer for the
`/`-separated, `<`-marked name format Gecko emits, so later
symbolication can produce names that match what the browser already
shows.
Pure utility, no callers yet.
Parses compiled JS with @lezer/javascript and walks the CST to build a
tree of function scopes with name-mapping locations. The character
offsets later probed against the source map to recover original
function names. `@lezer/javascript` is already a transitive dep via
`@codemirror/lang-javascript`.
Pure utility, no callers yet. Tested via the new
source-map-scope-tree.test.ts.
Implements the off-main-thread part of JS source map symbolication.
SourceMapStore wraps the source-map library's WASM-backed
SourceMapConsumer, source-map-symbolication.ts walks every func and
frame and maps its compiled position back to the original source via
exact-match source-map lookups (with scope-tree probes to recover
function names), and source-map.worker.ts wires those pieces up as a
Web Worker entry point.
The action thunk doSourceMapSymbolication spawns the worker on demand
and dispatches BULK_SOURCE_MAP_SYMBOLICATION on success.
No caller dispatches the thunk yet.
The Web Worker added in the previous patch needs its own bundle so the
@lezer/javascript and source-map dependencies (plus the source-map
WASM mappings) ship to the browser.
Still no caller invokes the worker, so this patch only changes how
`yarn build` builds the dist directory.
Activates JS source map symbolication end-to-end. After native
symbolication settles, the receive-profile flow now fetches source
maps and compiled JS for the bundle sources visible in any track,
hands them to doSourceMapSymbolication, and lets the worker rewrite
funcTable / frameTable / sourceMapInfo / sources / stringArray
in-place via BULK_SOURCE_MAP_SYMBOLICATION.
After this patch, sourceMapInfo is populated but UI code still reads
the compiled positions. The next patch makes it look at sourceMapInfo.
The sourceMapInfo table is populated after the previous patch's
wire-up, but no UI code read it yet. This patch teaches the source
view, call tree, tooltip, line timings, and getOriginAnnotationForFunc
to prefer source-mapped positions when present and fall back to the
compiled position otherwise. Frame-level entries override func-level
entries (so inlined code lands in the right original file).
Adds the inline-content fallback in selectors/code.tsx: when the
profile already carries sources.content (from a shared profile or from
JS symbolication), the source view uses it directly instead of hitting
the browser.
The source map pipeline (fetching + worker) previously ran invisibly
after profile load. This surfaces two generic status messages in the
existing SymbolicationStatusOverlay: "Fetching source maps..." while
doResolveSourceMaps runs, and "Symbolicating JS source maps..." while
the worker runs. Native symbolication takes priority. The source map
messages only appear when the native overlay is not active.
It's tracked via a new sourceMapSymbolicationStatus on viewOptions.
Note that it looks like this component was never localized. We should do
it! But I think this is a task for a follow-up
I noticed this bug while testing the source map PR locally. I was doing
this:
- Capture a profile with the JS sources feature enabled that includes
  source maps.
- After the symbolication and source fetching, double click on a JS
  function that is source mapped.
- Refresh the page.
I was expecting it to work, but it was throwing errors because the
source that I already opened wasn't in the profile and string table
anymore (since it's added after the source map symbolication). So this
fixes this issue by checking if the string table index is undefined
…debugging
It proved somewhat difficult when I had the debug the source map
symbolication worker on the worker thread. It makes things a lot easier
when it runs on the main thread instead. I tried adding a const flag for
this with its proper function, but esbuild wasn't properly removing the
dead code, so it was inflating the bundle a lot when this function was
there even if it was unused.
I tried also converting that into an async import, which extracted the
symbolication into a different bundle, but it was still outputting the
extra bundle even if it was false constant value. So, I decided to
include it as a commented out documentation in case we need it in the
future. It's not the best, but it should still help us when we need to
debug the worker in the future.
`fetchSourceAnnotation` was always reading from
funcTable.source[funcIndex], which points at the compiled bundle. For
JS-symbolicated functions the original file already lives behind
funcTable.sourceMapInfo, and its text is typically embedded on the
shared source table either by sourcesContent during symbolication or by
the publish-with-sources path.
Resolve the effective source through getOriginalPositionForFrame so the
filename, line hit attribution, and content lookup all target the
original source when sourceMapInfo is present. When sources.content is
populated, use it directly and skip the network fetch entirely so the
source view works offline for published-with-sources profiles.
…h native
Refactor the source-map worker to return position-keyed resolutions instead
of full table snapshots. The main thread applies the response against the
current shared state via applySourceMapSymbolicationResponse, allocating
sourceMapInfo rows, interning names, and deduping URLs against whatever
native symbolication has committed by then.
With per-funcIndex/per-frameIndex idempotency (skip rows already populated)
and JS funcs/frames being insulated from native symbolication, the worker
no longer needs to wait for native symbolication to finish. finalizeProfileView
now runs source-map fetch, native symbolication, and the source-map worker
all in parallel.
getStackLineInfo runs once per stack in the stack table, which can
mean hundreds of thousands of iterations on large profiles. Calling
getOriginalPositionForFrame in that loop allocated a fresh
{source, line, column} object per stack and always resolved the line
(and column) even though the line is only used when the stack's
source matches the source view, and the column is never used here.
Inline the 3-tier source-map fallback so we drop the per-stack
allocation, skip the column entirely, and defer the line lookup
until matchesSource is true. Apply the same inlining to
getTotalLineTimingsForCallNode, which has the same shape and only
needs the line.
Measured on a large profile: getStackLineInfo went from ~373ms back
to ~199ms.

@canova

@canova

Merged

canova added a commit that referenced this pull request

Jun 16, 2026
Changes:
[Nazım Can Altınova] Fix call node context menu being hidden behind
source view bottom box (#6045)
[Nazım Can Altınova] Pass `--use-env-proxy` only when the node version
is >= 24 (#6064)
[fatadel] Upgrade @firefox-devtools/react-contextmenu to 5.2.4 (#6066)
[Markus Stange] Switch profiler-edit from minimist to commander (#6065)
[Markus Stange] Support reading profiles from JsonSlabs files (#6037)
[Florian Quèze] Don't fail profile processing when a marker's stack
field is not a backtrace (#6069)
[fatadel] Replace the footer-links overlay with a settings menu (#6042)
[fatadel] Upgrade @types/node to match Node 24 (#6070)
[fatadel] Remove unused undici-types package (#6074)
[cathaysia] Update isLocalURL to include LAN addresses, .local domains,
and hostn… (#5973)
[Markus Stange] Fix from-url with binary profiles (#6072)
[fatadel] Upgrade to React 19 (#6067)
[Markus Stange] Add an insertStackLabels helper. (#6076)
[fatadel] Drive counter tooltips from a tooltipRows schema (#6023)
[fatadel] Add TrackPower--tooltip-average-power-microwatt (#6080)
[Markus Stange] Downgrade to React 19.1 to fix unusable dev build
performance. (#6082)
[Nazım Can Altınova] Add source map symbolication and source view
support (#6018)
[spokodev] fix(FilterNavigatorBar): clip overflow so many breadcrumbs do
not expand the parent (#6085)
[Markus Stange] Move paddings inside the tree header cells. (#6002)
[Markus Stange] Add an --insert-label-frames argument to the
profiler-edit tool (#5966)
[Markus Stange] Stop printing "error: too many arguments" during tests.
(#6088)
[Markus Stange] More additions to profiler-edit, for sp3 profiles
(#6009)
[Nazım Can Altınova] Do not rely on localized texts in the settings menu
tests (#6101)
And special thanks to our localizers:
be: Andrei Mukamolau
de: Ger
de: Michael Köhler
de: Ralf Duehnfahr
el: Jim Spentzos
en-CA: chutten
en-GB: Ian Neal
es-CL: ravmn
fr: Théo Chevalier
fr: wy
fur: Fabio Tomat
fy-NL: Fjoerfoks
ia: Melo46
it: Francesco Lodolo [:flod]
nl: Mark Heijl
ru: Valery Ledovskoy
sr: Марко Костић (Marko Kostić)
sv-SE: Andreas Pettersson
tr: Grk
tr: Selim Şumlu
zh-CN: Olvcpr423
zh-TW: Pin-guang Chen

canova added a commit to canova/perf.html that referenced this pull request

Jun 25, 2026
…ation is requested
This was a regression from firefox-devtools#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.

Merged

canova added a commit that referenced this pull request

Jun 25, 2026
…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.

This was referenced

Jul 17, 2026

Closed

Closed

Read the original on github.com ↗