Repository navigation
feat(debug-files): Add wasm-upload command for WASM debug setup - #1628
Open
sentry-junior[bot] wants to merge 8 commits into
Open
sentry-junior[bot] wants to merge 8 commits into
sentry-junior[bot] wants to merge 8 commits into
Conversation
Preparing WebAssembly for Sentry took two tools: `wasm-split` to inject a `build_id` and pull DWARF into a companion, then `debug-files upload` on that companion. Nothing flagged modules built without usable debug info, so the mistake only surfaced as an unsymbolicated stack trace. `sentry debug-files wasm-upload <path>...` does both steps. It scans for `.wasm` modules, and for each one with inline DWARF it injects a `build_id` if missing, writes a `<stem>.<build_id>.debug.wasm` companion (keeping the Code section, since DWARF addresses are relative to it), strips `.debug_*` from the deployable in place, points it at the companion via `external_debug_info`, and uploads the companion. Modules that can't be split are still stamped and reported with a warning; `--require-dwarf` turns that into a CI failure. `--wait`/`--wait-for` parsing, source-file reading, and the ignore matcher move into shared helpers so `upload`, `bundle-sources`, and the sourcemap commands reuse them instead of carrying copies. Ported from getsentry/cli#1572, which is being closed with the repo archive. Co-Authored-By: BYK <burak.kaya@sentry.io>
|
The latest updates on your projects. Learn more about Vercel for GitHub. 1 Skipped Deployment
|
`--out-dir` was passed directly to `writeFile`. If the directory didn't exist yet, including the documented `./symbols` example, the command threw ENOENT before writing any companion. The command now creates the companion's directory first. Co-Authored-By: BYK <burak.kaya@sentry.io>
`wasm-upload` stripped modules in place and wrote companions before resolving the org, project, and chunk-upload options. A missing target or an auth failure then aborted after the files had changed, leaving stripped modules whose companions were never uploaded. When an upload will run, the target is now resolved before any module is touched, the same way `debug-files upload` does it. A module whose `external_debug_info` companion was missing also got a random `build_id` stamped on the skip path. If the companion showed up later, that random id could never match it, so the pair could no longer be repaired. These modules are now left unstamped.
Adapt wasm-upload to conventions that landed on main: completion-tagged parsers for every flag and positional, examples in command metadata, the sentry`...` usage-hint tag, and the toolkit-core Markdown builders. Co-Authored-By: BYK <burak.kaya@sentry.io>
sentry-junior Bot
added a commit
that referenced
this pull request
Nov 12, 2026
Bring in main through the rebased #1628 and fit wasm pairs into what landed there: - `sourcemap upload <glob>` now narrows wasm pairs the way it narrows JS pairs: a pair is kept when the module or its map matches, and the zero-pairs diagnostic counts only matching files so the `.wasm.map` subtraction lines up with the JS tally. - `--dry-run` now reports what a real run would change. `mapWritten` and `moduleStamped` were always false in a dry run, so `sourcemap inject --dry-run` counted every wasm pair as skipped. `setSourcemapDebugId` returns `changed` instead of `written` to say the same in both modes. - `relativePosix` is shared with JS discovery instead of duplicated. Co-Authored-By: bete <28983162+betegon@users.noreply.github.com> Co-Authored-By: BYK <burak.kaya@sentry.io>
A module that still had inline DWARF and an `external_debug_info` pointer left by another tool kept the old pointer next to the new one after `wasm-upload` split it. `inspectWasm` reads the last pointer and the companion lookup reads the first, so the two could follow different files. The split now drops any existing pointer before appending the one for the new companion. `splitWasm` itself is unchanged, so `wasm-split` keeps parity with the Rust tool.
Producers that predate the tool-conventions length prefix write the id as a bare 16-byte body. The decoder rejected it, so callers treated the module as unstamped and appended a second, random build_id. symbolic reads the last section and @sentry/wasm the first, so events stopped matching both maps and debug files for a module that matched before. Read a 16-byte body as is, mirroring getsentry/symbolic#1093. Co-Authored-By: bete <28983162+betegon@users.noreply.github.com>
wasm-upload skipped every *.debug.wasm, so a real debug build named app.debug.wasm was never uploaded. Companions are now recognized by the name this command writes (<stem>.<build_id>.debug.wasm), by another module's external_debug_info, or by sharing a build id with a sibling <stem>.wasm (wasm-split -d with a remote --external-dwarf-url). A *.debug.wasm with a differently identified sibling is prepared as a module. One with no sibling is skipped with a warning, since splitting a companion would strip the DWARF its module points at. Co-Authored-By: bete <28983162+betegon@users.noreply.github.com>
Contributor
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit 3f580b3. Configure here.
Dropping companions could leave nothing to prepare, but the command still resolved the org, project, and auth before noticing. A normal run over a directory of wasm-split companions then failed with a missing org/project error. The empty check now runs after companions are dropped.
This branch was previously deployed
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.

Ported from getsentry/cli#1572 by @d2anamaria, which is being closed as part of the
getsentry/cliarchive. Toolkit already had the low-level pieces (lib/wasm/binary.ts,build-id.ts,split.ts,sentry wasm-split). Nothing on main or in another open PR covers this command.Problem
Getting WebAssembly ready for Sentry takes two tools today. You run
wasm-splitto inject abuild_idand move DWARF into a companion file. Then you runsentry debug-files upload --type wasmon that companion. If a module was built without usable debug info, nothing tells you. You find out later from an unsymbolicated stack trace.Before / after
Before (v3, and
maintoday):You name the companion yourself, run this once per module, and get no warning when a module has no DWARF.
After:
One command for every module under
dist/. On the Emscripten fixture used in the end-to-end check below (emcc -g):dist/app.wasm.debug_*sections.debug_*, onebuild_id,external_debug_infopointing at the companion. Same path, so the deploy doesn't change.-ddist/app.<build_id>.debug.wasm(87,151 bytes), uploadedbuild_idand a warning.--require-dwarffails the run.wasm-splitagain would strip an already-stripped modulealready-prepared, no bytes changeSolution
sentry debug-files wasm-upload <path>...does both steps. It scans the paths for.wasmmodules. For each module that has inline DWARF, it:build_idif the module doesn't have one.<stem>.<build_id>.debug.wasmcompanion. The companion keeps the Code section, because DWARF addresses are relative to it..debug_*sections from the deployable module in place, so the artifact keeps its path.external_debug_info, replacing any pointer a previous tool left.A module that can't be split still gets a
build_idand a warning.--require-dwarfturns that warning into a failure for CI. If a module has a companion but nobuild_id, the command repairs the pair instead of only warning about it. Running the command again on the same files is safe.--dry-runand--no-uploadwork without credentials. The org, project, and auth are resolved before any module is modified, so a missing one fails before anything is stripped.The command also moves some duplicated code into shared helpers, as asked in review on the original PR:
--wait/--wait-forparsing moves tocommands/debug-files/wait.tsreadSourceFilemoves tocommands/debug-files/read-file.tsbuildIgnoreMatchermoves tolib/scan/ignore.tsupload,bundle-sources, and the sourcemap commands now import these helpers instead of keeping their own copies.Which
*.debug.wasmfiles are companionsA companion is a full copy of its module, so its contents can't tell it apart from a module with DWARF. Splitting a companion by mistake strips the DWARF its module points at. A scanned
*.debug.wasmis treated as a companion only when:<stem>.<build_id>.debug.wasmname this command writes, orexternal_debug_infopoints at it, or<stem>.debug.wasmnext to a<stem>.wasmwith the samebuild_id. This is whatwasm-split -d app.debug.wasmleaves, including with a remote--external-dwarf-url.A
<stem>.debug.wasmwhose sibling has a differentbuild_idis a separate debug build, so it's prepared and uploaded like any module. One with no sibling can't be told apart, so it's skipped with a warning that names the file.build_id convention
The
build_idsection body is a varuint32 length prefix followed by the id, and the CLI reads the id after the prefix. A body of exactly 16 bytes with no prefix (the legacy form some producers write) is read as a raw UUID, the way symbolic reads it. It is not stamped again: a secondbuild_idwould make symbolic (last section wins) and@sentry/wasm(first section wins) disagree.The readers on the other side now agree with this: symbolic#1093 (14.0.0-alpha.4), getsentry/sentry#128493 (stores the correct id plus the legacy shifted one), and
@sentry/wasm11.11.0 (sentry-javascript#24857). One gap remains on the SDK side:@sentry/wasm11.12.0 only strips the prefix for 16-byte ids, so awasm-ld --build-id=sha1module (20-byte id) still produces events with the shifted id. That needs a fix in@sentry/wasm, not here.Review notes carried over from cli#1572
preparetowasm-uploadafter BYK pointed out thatpreparedoesn't suggest an upload and only applies to wasm.build_id, as agreed in review.lib/wasm/prepare.tsyet, so that file needs the closest look here.Merged with main
mainmoved by about 390 commits. The merge adaptswasm-uploadto the conventions that landed there:sentry`…`tag@sentry/toolkit-core/markdownbuildersdebug-files uploaddrops itsValidationErrorimport now that wait parsing lives inwait.ts.sentry-cli v3 compatibility
v3 documents WASM as
wasm-split app.wasm -d app.debug.wasm --stripfollowed bysentry-cli debug-files upload. That flow still works unchanged in v4 (sentry wasm-splittakes the same flags).wasm-uploadis a new v4 shortcut and changes no existing command. Runningwasm-uploadover a directory that the v3 flow already processed leaves theapp.debug.wasmcompanion alone.Validation
End-to-end with real Emscripten builds (emsdk,
-g) of a small C crash, one perbuild_idflavour.debug-files wasm-uploadran against a mock Sentry, then each stripped module was crashed in headless Chromium with@sentry/wasm11.12.0. "Uploaded companion" compares the id symbolic reads from the uploaded file with the stripped module's id.build_id@sentry/wasm11.12.0 event matchesbuild_idsections.debug_*left in module--build-id=uuid--build-id=fast--build-id=sha1A second run reported
already-preparedfor every module and left them byte-identical. Naming cases, also end to end: asentry wasm-split -d app.debug.wasm --external-dwarf-url https://…output keeps its companion untouched, a separateapp.debug.wasmdebug build is split and uploaded, and a loneapp.debug.wasmis skipped with a warning. This check doesn't cover server-side symbolication of a frame through the uploaded companion.Checks:
tsc --noEmitforpackages/clivitestacrosstest/lib/wasm,test/commands/debug-files,test/commands/sourcemap,test/lib/sourcemap,test/lib/scanci:policy,oxlint, andoxfmt --checkforpackages/clicheck:fragments,check:docs-sections,check:errorsgenerate:docsleaves no diffRefs getsentry/cli#1572
via BYK, @betegon.
--
View Session [Sentry]