Add a --symbolicate-wasm arg to profiler-edit.#6008
Conversation
52ba22e to
ab1e4b6
Compare
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #6008 +/- ##
==========================================
- Coverage 83.82% 83.78% -0.04%
==========================================
Files 328 329 +1
Lines 34254 34391 +137
Branches 9572 9607 +35
==========================================
+ Hits 28712 28814 +102
- Misses 5114 5148 +34
- Partials 428 429 +1 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
0202adf to
be15b28
Compare
This allows applying wasm symbols to existing profiles that were captured with a stripped wasm bundle. The script looks for functions with names of the shape `wasm-function[123]`, which is what Firefox uses when the wasm file doesn't have a names section. Usage: ``` yarn build-node-tools && \ node node-tools-dist/profiler-edit.js -i input.json.gz \ --symbolicate-wasm http://host/a.wasm=./a-unstripped.wasm \ --symbolicate-wasm http://host/b.wasm=./b-unstripped.wasm \ -o out.json.gz ```
be15b28 to
2b6584b
Compare
a70577c to
a8608c4
Compare
canova
left a comment
There was a problem hiding this comment.
Nice, thanks for adding some tests! I added a bunch of nits to make things a bit clearer, but otherwise I don't see any issues.
This also made me look into the wasm spec a bit and learn more about it, so I think it was a good use of time 😄
This also made me think more about the source map support that I've been working on. These 2 things seem like solving it for 2 different use cases, so it seems very interesting. I didn't know about the name section in the wasm spec. I wonder how devtools handles it (or if it does). It looks like it's mostly looking at the dwarf info for now, but need to look deeper.
| - name: Upload node-tools-dist artifact | ||
| if: matrix.os == 'ubuntu-latest' | ||
| uses: actions/upload-artifact@v7 | ||
| with: | ||
| name: node-tools-dist | ||
| path: node-tools-dist/ | ||
|
|
There was a problem hiding this comment.
Oh, that's a good idea. It makes me think if we should do the same for the profiler-cli.
| path: string; | ||
| url?: string; |
There was a problem hiding this comment.
I don't immediately understand which one is from the stripped one and which one is from unstripped. How about strippedWasmUrl / unstrippedWasmPath (or symbolsWasmPath) or something?
| const symbolicateWasm: WasmSymbolicationCliSpec[] = []; | ||
| const rawWasmArg = argv['symbolicate-wasm']; | ||
| let wasmArgs: unknown[]; | ||
| if (rawWasmArg === undefined) { | ||
| wasmArgs = []; | ||
| } else if (Array.isArray(rawWasmArg)) { | ||
| wasmArgs = rawWasmArg; | ||
| } else { | ||
| wasmArgs = [rawWasmArg]; | ||
| } | ||
| for (const arg of wasmArgs) { | ||
| if (typeof arg !== 'string' || arg === '') { | ||
| throw new Error('--symbolicate-wasm requires a value'); | ||
| } | ||
| // Accept "<url>=<path>" if the LHS looks like a URL, otherwise treat the | ||
| // whole string as a path and infer the URL from the profile. Split on | ||
| // the last `=` so URLs containing `=` (e.g. in query strings) survive | ||
| // intact; this assumes file paths don't contain `=`. | ||
| const eqIndex = arg.lastIndexOf('='); | ||
| if (eqIndex !== -1 && /^[a-z]+:\/\//i.test(arg.slice(0, eqIndex))) { | ||
| symbolicateWasm.push({ | ||
| url: arg.slice(0, eqIndex), | ||
| path: arg.slice(eqIndex + 1), | ||
| }); | ||
| } else { | ||
| symbolicateWasm.push({ path: arg }); | ||
| } | ||
| } |
There was a problem hiding this comment.
Not for this PR, but now that we have commander in the package.json, we can possibly simplify these things by using it.
| // Accept "<url>=<path>" if the LHS looks like a URL, otherwise treat the | ||
| // whole string as a path and infer the URL from the profile. Split on | ||
| // the last `=` so URLs containing `=` (e.g. in query strings) survive | ||
| // intact; this assumes file paths don't contain `=`. |
There was a problem hiding this comment.
Can we add a comment to the WasmSymbolicationCliSpec type about this wasn't clear to me at first.
| profile.meta.symbolicated = true; | ||
| } | ||
|
|
||
| if (options.symbolicateWasm !== undefined) { |
There was a problem hiding this comment.
Nit: We construct symbolicateWasm as an empty array, so it should never be undefined.
| // Parses the function-name subsection of a wasm "name" custom section and | ||
| // returns a map from function index to name. Returns an empty map if the | ||
| // module has no name section. The function index space includes imports | ||
| // (imports come first) — same numbering used in `wasm-function[N]` strings. |
There was a problem hiding this comment.
Oh can we add a link to the Firefox source code where we generate this N number?
| const namesByIndex = parseWasmFunctionNames(spec.bytes); | ||
| if (namesByIndex.size === 0) { | ||
| throw new Error( | ||
| `${tag} has no function names — is this an unstripped wasm file?` |
There was a problem hiding this comment.
Nit: ${tag}: would be better for consistency. (the others seem to be that way)
| continue; | ||
| } | ||
| const sectionName = readName(); | ||
| if (sectionName !== 'name') { |
There was a problem hiding this comment.
Oh I see, so this looks for the "name" section and the creates a map of the names. But this is not a dwarf info. I found some info here, maybe we can put it a link here for future reference: https://webassembly.github.io/spec/core/appendix/custom.html#name-section
| const subId = bytes[pos++]; | ||
| const subSize = readVarUint(); | ||
| const subEnd = pos + subSize; | ||
| if (subId === 1) { |
There was a problem hiding this comment.
It would be good to demystify 1 here, maybe we can create a constant called FUNC_NAMES_SUB_ID.
| const sectionId = bytes[pos++]; | ||
| const sectionSize = readVarUint(); | ||
| const sectionEnd = pos + sectionSize; | ||
| if (sectionId !== 0) { |
There was a problem hiding this comment.
Can we also demystify this section id by adding a const like CUSTOM_SECTION_ID? And possibly link: https://webassembly.github.io/exception-handling/core/binary/modules.html#sections
This allows applying wasm symbols to existing profiles that were captured with a stripped wasm bundle.
The script looks for functions with names of the shape
wasm-function[123], which is what Firefox uses when the wasm file doesn't have a names section.Usage:
I've successfully used it to turn https://share.firefox.dev/4f9O7oh into https://share.firefox.dev/4nd6GKk .