| name | wasm-binary-analysis |
| description | Analyze WebAssembly binaries from .NET WASM builds using wasm-objdump and related tools. USE FOR comparing dotnet.native.wasm across runtime pack versions, verifying SIMD instruction presence, diagnosing execution mode changes, file size forensics. DO NOT USE FOR building WASM apps from scratch or running benchmarks. |
WASM Binary Analysis
Analyze WebAssembly binaries produced by .NET WASM builds to verify SIMD support, compare versions, and diagnose regressions at the binary level.
Prerequisites
| Requirement | Details |
|---|
wabt | WebAssembly Binary Toolkit — install via npm install -g wabt or apt install wabt |
| .NET SDK | With wasm-tools workload installed (dotnet workload install wasm-tools) |
| Runtime packs | Downloaded from NuGet or present in SDK packs/ directory |
Key Files in a WASM Runtime Pack
The Microsoft.NETCore.App.Runtime.Mono.browser-wasm nupkg contains:
runtimes/browser-wasm/native/
dotnet.native.wasm ← Main native binary (interpreter + runtime + CoreLib AOT)
dotnet.native.js ← JS glue code
dotnet.native.worker.mjs ← Web worker support
runtimes/browser-wasm/lib/net{X}.0/
System.Private.CoreLib.dll ← Managed CoreLib
System.*.dll ← Framework assemblies
Extracting Runtime Packs
From NuGet (any version)
curl -s "https://api.nuget.org/v3-flatcontainer/microsoft.netcore.app.runtime.mono.browser-wasm/index.json" | jq '.versions[-10:]'
curl -LO "https://api.nuget.org/v3-flatcontainer/microsoft.netcore.app.runtime.mono.browser-wasm/{VERSION}/microsoft.netcore.app.runtime.mono.browser-wasm.{VERSION}.nupkg"
unzip -o *.nupkg -d pack-{VERSION}
From dnceng Azure DevOps feed (preview builds)
FEED="https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet10/nuget/v3/flat2"
curl -s "$FEED/microsoft.netcore.app.runtime.mono.browser-wasm/index.json" | jq '.versions[-10:]'
From installed SDK
ls $DOTNET_ROOT/packs/Microsoft.NETCore.App.Runtime.Mono.browser-wasm/
Finding Versions for a Time Window
When bisecting, you need to map a date range to specific SDK or runtime pack versions. The NuGet feed gives you version strings but not dates. To correlate:
eng/Version.Details.xml in dotnet/runtime or dotnet/performance — tracks which dependency versions were used at any point in time. Use git log with --after/--before on this file to find what versions were active during the regression window.
- Daily prerelease SDKs are published to dotnet NuGet feeds and can be installed with
dotnet-install by version number. This is much faster than building runtime from source.
- NuGet package timestamps — the flat container API doesn't expose dates, but the package metadata endpoint does. Use the catalog API or check Azure DevOps build history to correlate versions to dates.
Analysis Commands
SIMD Instruction Count
The single most useful diagnostic. If SIMD count drops to 0, SIMD was disabled (build config regression).
wasm-objdump -d dotnet.native.wasm | grep -c 'v128\.'
Compare Two Versions
echo "=== Version A ==="
wasm-objdump -d pack-A/runtimes/browser-wasm/native/dotnet.native.wasm | grep -c 'v128\.'
ls -la pack-A/runtimes/browser-wasm/native/dotnet.native.wasm
echo "=== Version B ==="
wasm-objdump -d pack-B/runtimes/browser-wasm/native/dotnet.native.wasm | grep -c 'v128\.'
ls -la pack-B/runtimes/browser-wasm/native/dotnet.native.wasm
SIMD Instruction Breakdown
wasm-objdump -d dotnet.native.wasm | grep 'v128\.' | sed 's/.*: //' | awk '{print $1}' | sort | uniq -c | sort -rn | head -20
Full Disassembly Search
wasm-objdump -d dotnet.native.wasm | grep -A 5 'PackedSimd'
wasm-objdump -x dotnet.native.wasm | grep '<.*>' | head -50
wasm2wat dotnet.native.wasm -o dotnet.native.wat
grep -c 'v128' dotnet.native.wat
File Size Comparison
for d in pack-*/; do
ver=$(basename "$d" | sed 's/pack-//')
size=$(stat -f%z "$d/runtimes/browser-wasm/native/dotnet.native.wasm" 2>/dev/null || stat -c%s "$d/runtimes/browser-wasm/native/dotnet.native.wasm")
echo "$ver: $size bytes ($(echo "scale=1; $size/1048576" | bc)MB)"
done
Interpreting Results
SIMD Verification Matrix
| v128 count | File size | Diagnosis |
|---|
| ~900-1000 | ~35-40MB | Healthy — SIMD enabled, normal build |
| 0 | ~30-35MB | SIMD disabled — check WasmEnableSIMD, -msimd128 flag |
| ~900-1000 | Significantly larger | Extra code linked — check trimming config |
| Same across versions | Same across versions | No runtime change — regression is infrastructure/methodology |
Common Build Flags
These flags control WASM binary output in dotnet/runtime:
| Flag | Default | Effect |
|---|
WasmEnableSIMD | true | Enables SIMD intrinsic transform in interpreter |
-msimd128 | Set when SIMD enabled | Emscripten flag enabling WASM SIMD |
PublishTrimmed | Varies | Affects binary size and linked code |
| Emscripten version | 3.1.56 (as of .NET 10) | Codegen differences between versions |
dotnet/runtime Source Locations
| Path | Purpose |
|---|
src/mono/mono/mini/interp/transform-simd.c | SIMD intrinsic transform (MINT_SIMD opcodes) |
src/mono/mono/mini/interp/interp-simd.c | SIMD execution |
src/mono/mono/mini/interp/interp.h | INTERP_OPT_SIMD flag in INTERP_OPT_DEFAULT |
src/mono/mono/mini/interp/transform.c | Main interpreter transform |
src/mono/browser/runtime/ | JS interop and browser host |
Swapping Runtime Packs for Bisection
To test a specific runtime pack version with your current SDK:
SDK_VERSION=$(dotnet --version)
PACK_DIR="$DOTNET_ROOT/packs/Microsoft.NETCore.App.Runtime.Mono.browser-wasm/$SDK_VERSION"
cp -r "$PACK_DIR" "${PACK_DIR}.bak"
cp pack-{VERSION}/runtimes/browser-wasm/native/* "$PACK_DIR/runtimes/browser-wasm/native/"
dotnet publish -c Release
rm -rf "$PACK_DIR"
mv "${PACK_DIR}.bak" "$PACK_DIR"
Codespace-Based Bisection
Shared machines introduce variance that can mask or fabricate small regressions. For definitive bisection, use a dedicated GitHub Codespace.
Creating the Codespace
gh codespace create --repo dotnet/runtime --machine largePremiumLinux \
--devcontainer-path .devcontainer/devcontainer.json
Environment Setup
The default devcontainer is missing tools needed for WASM work:
curl -fsSL https://deb.nodesource.com/setup_20.x | sudo -E bash -
sudo apt-get install -y nodejs
sudo apt-get install -y libglib2.0-0 libnss3 libnspr4 libdbus-1-3 \
libatk1.0-0 libatk-bridge2.0-0 libcups2 libdrm2 libxkbcommon0 \
libxcomposite1 libxdamage1 libxrandr2 libgbm1 libpango-1.0-0 \
libcairo2 libasound2
npm install -g wabt
Installing Multiple SDK Versions Side-by-Side
export DOTNET_ROOT=$HOME/.dotnet
./eng/common/dotnet-install.sh --version 10.0.100-preview.3.25201.16
dotnet workload install wasm-tools
./eng/common/dotnet-install.sh --version 10.0.100-preview.4.25258.1
dotnet workload install wasm-tools
Interleaved A/B Testing
Run baseline and comparison alternately to eliminate thermal throttling and background process drift:
#!/bin/bash
BASELINE_PACK="pack-baseline"
COMPARE_PACK="pack-compare"
ROUNDS=5
WARMUP=5
ITERATIONS=10000000
for round in $(seq 1 $ROUNDS); do
echo "=== Round $round ==="
cp "$BASELINE_PACK/runtimes/browser-wasm/native/"* "$PACK_DIR/runtimes/browser-wasm/native/"
dotnet publish -c Release -o out-baseline 2>/dev/null
echo -n "Baseline: "
node --experimental-vm-modules run-benchmark.mjs out-baseline $WARMUP $ITERATIONS
cp "$COMPARE_PACK/runtimes/browser-wasm/native/"* "$PACK_DIR/runtimes/browser-wasm/native/"
dotnet publish -c Release -o out-compare 2>/dev/null
echo -n "Compare: "
node --experimental-vm-modules run-benchmark.mjs out-compare $WARMUP $ITERATIONS
done
Interpreting Codespace Results
| Variance (CoV) | Verdict |
|---|
| <3% | High confidence — results are reliable |
| 3-7% | Moderate — differences <10% may be noise |
| >7% | Unreliable — check for background processes, try larger machine |
If baseline and comparison results overlap across 5 interleaved rounds → no runtime regression (the auto-filed issue is an infrastructure artifact).
Quick Reference
wasm-objdump -d dotnet.native.wasm | grep -c 'v128\.' | xargs -I{} echo "SIMD instructions: {}"
diff <(wasm-objdump -x packA/dotnet.native.wasm) <(wasm-objdump -x packB/dotnet.native.wasm) | head -40