✨ [RUM-16985] Capture wasm module build_ids and enrich error events - #4920
✨ [RUM-16985] Capture wasm module build_ids and enrich error events#4920ImaneLargou wants to merge 9 commits into
Conversation
Intercepts WebAssembly.instantiate / instantiateStreaming at SDK
script-load time to record (url, build_id) per loaded module, including
lazily-loaded modules. On error capture, attaches error.wasm_modules[]
and sets source_type='browser+wasm' so the backend can dispatch wasm
symbolication.
- New wasmModules/wasmModuleTracking.ts: hooks all four WebAssembly
entry points; reads build_id via a minimal custom-section parser;
registry stays live for lazy module loads
- New wasmModules/wasmBinaryParser.ts: walks wasm binary sections,
extracts build_id custom section or falls back to external_debug_info
- errorCollection: populates error.wasm_modules[] and flips source_type
to 'browser+wasm' when any module is registered
- rawRumEvent.types: adds wasm_modules?: Array<{url, build_id}> to
RawRumErrorEvent
- main.ts: installs tracking synchronously at script-load time to close
the race window before DD_RUM.init()'s deferred microtask
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
All contributors have signed the CLA ✍️ ✅ |
🎉 All green!🧪 All tests passed 🎯 Code Coverage (details) 🔗 Commit SHA: 420fdee | Docs | Datadog PR Page | Give us feedback! |
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Bundles Sizes Evolution
|
|
I have read the CLA Document and I hereby sign the CLA |
| // wrapper only resolves once both are done. This guarantees that an error | ||
| // thrown immediately by an exported function can reference the loaded module. | ||
| function captureFromResponse(response: Response): Promise<void> { | ||
| const url = response.url || '<wasm-instantiate-streaming-no-url>' |
There was a problem hiding this comment.
❓ question: How could the URL be empty? Is this just because of the types?
| trackingClients += 1 | ||
| if (!stopTracking) { | ||
| stopTracking = installWasmModuleTracking() | ||
| } | ||
|
|
||
| let stopped = false | ||
| return () => { | ||
| if (stopped) { | ||
| return | ||
| } | ||
| stopped = true | ||
| trackingClients -= 1 | ||
| if (trackingClients === 0) { | ||
| stopTracking?.() | ||
| stopTracking = undefined | ||
| registry.clear() | ||
| } | ||
| } |
There was a problem hiding this comment.
❓ question: How many clients could we expect tracking WebAssembly?
| import { makeLogsPublicApi } from '../boot/logsPublicApi' | ||
|
|
||
| // Install WebAssembly hooks before deferred Logs initialization so eagerly loaded modules are captured. | ||
| startWasmModuleTracking() |
There was a problem hiding this comment.
🔨 warning: The philosophy of the SDK is not to override any APIs unless we are allowed to track the user.
This should be deferred until SDK initialization.
| // reads it to set source_type='browser+wasm' and error.wasm_modules. | ||
| // Must start before any wasm load — RUM is initialised before the page's | ||
| // wasm fetch in typical setups. | ||
| const stopWasmModuleTracking = startWasmModuleTracking() |
There was a problem hiding this comment.
❓ question: Are we not initializing this in different places?
| // wasm modules — by the time the deferred wrap installs, instantiateStreaming | ||
| // may have already been called. Installing here (before any deferral) closes | ||
| // that race. | ||
| startWasmModuleTracking() |
There was a problem hiding this comment.
🔨 warning: Mentioned before.
| import { makeProfilerApiStub } from '../boot/stubProfilerApi' | ||
|
|
||
| // Install WebAssembly hooks before deferred RUM initialization so eagerly loaded modules are captured. | ||
| startWasmModuleTracking() |
There was a problem hiding this comment.
🔨 warning: Keeping track of all the places where this is automatically tracked.
Motivation
Browser WebAssembly runtime errors currently lack the module metadata required for WASM symbolication.
This change identifies errors containing WebAssembly stack frames and reports the loaded WASM module URL and build ID. This allows the error-processing pipeline to distinguish ordinary browser errors from errors that should use WASM/DWARF symbolication.
Related upstream schema change: DataDog/rum-events-format#427
Changes
WebAssembly.instantiateWebAssembly.instantiateStreamingWebAssembly.compileWebAssembly.compileStreamingbuild_idcustom section, withexternal_debug_infoas a fallback.error.source_type: "browser+wasm"error.wasm_modules, containing the module URL and build ID.jsframes do not qualify an error as WASM.rum-events-formatschema change.Test instructions
Run the relevant unit tests:
Run the E2E scenario:
yarn test:e2e -g "send WebAssembly runtime errors with module metadata"The E2E test loads a real .wasm module using
WebAssembly.instantiateStreaming, triggers a runtime error, and verifies that both RUM and Logs events contain:{ "source_type": "browser+wasm", "wasm_modules": [ { "url": "<test module URL>", "build_id": "abcd" } ] }Checklist