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.

Remote debugging a page on a phone The phone runs the page or WebView. A USB connection links it to a desktop. Chrome's inspect page for Android or Safari's Develop menu for iOS lists debuggable pages. Attaching opens full developer tools on the desktop, showing the phone's console, sources, network and performance data. page runs on phone browser or WebView USB / pairing trusted computer chrome://inspect or Develop menu lists pages attach DevTools desktop window console, sources, profiles from the device

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.

Remote debugging setup by platform Android Chrome tabs appear in chrome://inspect after enabling USB debugging. Android WebViews additionally require the app to enable web contents debugging. iOS Safari pages appear in the Mac's Develop menu after enabling Web Inspector. iOS WKWebView content additionally requires isInspectable in the app. target device setting app requirement desktop tool Android Chrome USB debugging — chrome://inspect Android WebView USB debugging setWebContentsDebuggingEnabled chrome://inspect iOS Safari Web Inspector on — Safari Develop menu iOS WKWebView Web Inspector on isInspectable = true Safari Develop menu

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.

Filter time on a phone over a session Milliseconds per image filter on a mid-range Android phone at the start of a session and after five minutes of continuous use, compared with a desktop with four times CPU throttling. ms per filter desktop, 4× throttling 33 ms phone, start of session 38 ms phone, after 5 minutes 61 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.

← Back to Debugging & Profiling Wasm Modules