Debugging Wasm on Android and iOS Devices
This page answers one task: a WebAssembly feature misbehaves only on phones — it crashes on an iPhone, runs slowly on mid-range Android, or fails inside an app’s WebView — and desktop emulation does not reproduce it. You want to attach real developer tools to the device, see console errors and traps, set breakpoints, and profile on the hardware where the problem occurs.
Prerequisites
- [ ] An Android device with Developer options and USB debugging enabled, and Chrome on a desktop.
- [ ] An iPhone or iPad with Web Inspector enabled, and Safari on a Mac.
- [ ] A USB cable (or network pairing), and the app’s debug build if you are debugging a WebView.
Why phones need their own debugging sessions
Desktop device emulation changes the viewport and user agent, not the engine’s behaviour on the device. Phones differ in ways that matter for WebAssembly: lower memory ceilings (iOS in particular terminates tabs that use too much memory), slower CPUs with thermal throttling, different engine versions (especially in WebViews embedded in apps), more aggressive background-tab suspension, and touch-driven event timing. Bugs that depend on any of these only reproduce on a real device, so you need tools attached to it.
Both mobile platforms support remote debugging: the phone runs the page, and desktop developer tools connect to it over USB (or the network), giving you the console, sources with breakpoints, network inspection and performance profiling for the page running on the phone.
Step 1 — Android with Chrome remote debugging
Enable Developer options (tap the build number seven times), turn on USB debugging, connect the phone and accept the debugging prompt. On the desktop, open
chrome://inspect/#devices; the phone’s open tabs appear, each with an “inspect” link that opens DevTools for that tab. Everything you use on desktop works: the
console (including Wasm compile errors and traps), Sources with Wasm disassembly and DWARF debugging if the extension is installed on the desktop, the Memory
panel, and the Performance panel recording on the device’s CPU.
Step 2 — iOS with Safari Web Inspector
On the iPhone, enable Settings → Safari → Advanced → Web Inspector. Connect to a Mac, open Safari’s Develop menu, and choose the device and the page. Web
Inspector opens with the console, Sources (Wasm functions as disassembly with breakpoints), Network and Timelines for the page running on the phone. Memory limit
problems — the most common iOS-specific Wasm failure — show up as RangeErrors on allocation or as the page reloading unexpectedly when iOS kills it.
Step 3 — debug WebViews in native apps
Many Wasm features run inside apps’ WebViews (Capacitor, React Native WebView, custom shells). On Android, the app must enable debugging
(WebView.setWebContentsDebuggingEnabled(true) in debug builds); the WebView then appears in chrome://inspect. On iOS 16.4 and later, set
webView.isInspectable = true (debug builds) for WKWebView content to appear in Safari’s Develop menu. WebViews may run older engine versions than the
browser on the same device — check the version in the console (navigator.userAgent) when behaviour differs.
Step 4 — reproduce memory and thermal problems
To reproduce memory-related crashes, use the heaviest realistic input and watch memory: log memory.buffer.byteLength after operations, and on Android, the
Memory panel shows the JavaScript heap while performance.memory and the module’s own counters show linear memory. iOS terminates pages above limits that vary
by device; reduce initial and maximum memory, process in chunks, and recreate instances to release memory. For performance, run the scenario several minutes to
include thermal throttling, and profile late in the run as well as early.
Step 5 — capture errors you cannot watch live
Some bugs happen when no cable is connected. Add a debug overlay or a “copy diagnostics” button that collects recent console errors, Wasm load results, memory
size and device information, so testers can send it. Forward window.onerror and unhandledrejection events — which include Wasm traps — to your error
tracker with device context. For the native-app case, route WebView console messages to the native log (Logcat or the Xcode console) in debug builds.
On-device profiling
Record performance profiles on the device rather than relying on desktop CPU throttling. In Chrome DevTools connected to Android, the Performance panel records the device’s main thread and workers, with Wasm functions named if the module keeps names. In Safari, Timelines records the iPhone’s activity. Compare profiles with desktop ones: functions that dominate only on the phone often involve memory bandwidth or large working sets that fit desktop caches but not mobile ones.
Page lifecycle on mobile
Mobile browsers suspend and discard pages far more readily than desktop ones. When a user switches apps, the page may be frozen — timers and workers stop — and
later resumed, or discarded entirely and reloaded when the user returns. Wasm features that keep long-lived state in a worker or in memory must handle this:
a worker killed while the page was frozen leaves pending promises that never resolve, and a resumed page may find its instance gone after a discard. Listen for
visibilitychange, pagehide and the Page Lifecycle API’s freeze and resume events where available, save important state before backgrounding, and
check on resume that workers still respond (a ping with a timeout), recreating them if not. These bugs are hard to see on desktop; on a phone, switch apps in
the middle of a long operation and come back after a minute while Web Inspector or DevTools is attached.
Network and storage differences
Mobile networks change speed and drop connections, and some mobile browsers clear storage under pressure. Test module loading with the device on cellular data, with flaky connectivity, and after the system has been under storage pressure. Remote DevTools show the device’s network requests, so you can confirm whether a failing Wasm load was a network error, a cache miss that refetched a large module, or a response altered by a carrier proxy. On iOS, also check private browsing, where storage limits are tighter and IndexedDB or OPFS-based caches may not persist.
Building a device test routine
Keep a short checklist for every release that touches Wasm: load cold and warm on one Android and one iPhone, run the heaviest supported input, background the app mid-operation and return, and watch the console for errors throughout.
Expected output
A crash on iPhone is reproduced with Web Inspector attached, revealing a RangeError when the module grows memory past about 1 GB; the fix caps memory and
processes in tiles; a slow Android WebView is traced to an old engine version without SIMD, triggering the scalar fallback; and testers can copy a diagnostics
report from a hidden menu when a cable is not connected.
Gotchas
- Relying on desktop emulation. Engines and limits differ. Use real devices.
- WebViews not inspectable. Enable debugging in the app’s debug build.
- Assuming the WebView matches the browser. Engine versions differ. Check the user agent.
- Short profiling sessions. Thermal throttling appears later. Profile over minutes.
- No way to capture field errors. Add diagnostics export and error forwarding.
- Ignoring page freezing and discards. Workers vanish while backgrounded. Check and recreate on resume.
Performance note
The same Wasm image filter took 38 ms on a mid-range Android phone at the start of a session and 61 ms after five minutes of continuous use, as the device throttled; the desktop with 4× CPU throttling stayed at 33 ms.
Frequently Asked Questions
Can I debug iOS from Windows or Linux? Safari Web Inspector requires a Mac; third-party tools exist with varying support.
Do DWARF source maps work on Android? Yes, with the C/C++ DevTools extension installed in the desktop Chrome that attaches to the device.
Why does the page reload on iOS? iOS terminated it, often for memory; there may be no error message.
Can I debug over Wi-Fi? Android supports wireless debugging via ADB; iOS supports network pairing for Web Inspector after initial setup.
Why do pending operations never finish after switching apps? The page or its worker was frozen or discarded; check workers on resume with a ping and recreate them if they do not answer.
How do I test backgrounding behaviour? Start a long operation, switch apps for a minute, return with DevTools or Web Inspector attached, and watch for stalled workers.
Does private browsing affect Wasm caching on iOS? Yes — storage is more limited and may not persist, so cached modules and data can disappear.
Related
- Debugging Wasm in Safari Web Inspector — Safari’s tools.
- Benchmarking Wasm on mobile devices — measuring on phones.
- Running Wasm in a Capacitor mobile app — WebView apps.
- Understanding Wasm linear memory limits — memory ceilings.
← Back to Debugging & Profiling Wasm Modules