How to Debug WebAssembly Pipelines in Your Web Browser

By: Matt McCormick ORCID, Mary Elise Dedicke , Jean-Christophe Fillion-Robin , Will Schroeder

Did you know that your web browser comes bundled with extremely powerful development tools?

Modern web browsers are the foundation of the The Web Platform, a full-featured computing environment. However, full-featured platforms are not useful in and of themselves. A large community of software developers is required to program the applications on a platform that people love. And, effective debugging results in effective programming.

In this tutorial, we will learn how to debug itk-wasm C++ data processing pipelines built to WebAssembly (wasm) with the full-featured graphical debugger built into Chromium-based browsers.

In the following sections, we will first explain how to obtain a useful JavaScript backtrace in Node.js, then debug C++ code built to WebAssembly in a Chromium-based web browser. Let’s get started! :rocket:

0. Preliminaries

Before starting this tutorial, check out our WASI WebAssembly command line debugging tutorial.

As with the previous tutorial, we will be debugging the following C++ code:

#include <iostream>

int main() {
  std::cout << "Hello debugger world!" << std::endl;

  const char * wasmDetails = "are no longer hidden";

  const int a = 1;
  const int b = 2;
  const auto c = a + b;

  // Simulate a crash.
  abort();
  return 0;
}

The tutorial code provides npm scripts as a convenient way to execute debugging commands, which you may also invoke directly in a command line shell.

1. Node.js Backtrace

When debugging WebAssembly built with the itk-wasm Emscripten toolchain, set the CMAKE_BUILD_TYPE to Debug just like with native binary debug builds.

As with native builds, this build configuration adds debugging symbols, the human-readable names of functions, variables, etc., into the binary. This also adds support for C++ exceptions and retrieving the string name associated with exceptions. Without this itk-wasm instrumentation, a C++ exception will throw an error with an opaque integer value. And, Emscripten JavaScript WebAssembly bindings will not be minified, which facilitates debugging.

When built with the default Release build type:

the JavaScript support code is minified, and difficult to debug:

However, when built with the Debug build type:

you can obtain a useful backtrace:

This is helpful for debugging issues that occur in the Emscripten JavaScript interface. The next section describes how to debug issues inside the Emscripten-generated WebAssembly.

2. Debugging in Chromium-based Browsers

Recent Chromium-based browsers have support for debugging C++ -based WebAssembly in the browser. With a few extra steps described in this section, it is possible to interactively step through and inspect WebAssembly that is compiled with C++ running in the browser.

WebAssembly debugging in DevTools requires a few extra setup steps compared to a default browser installation.

First, install the Chrome WebAssembly Debugging extension.

Next, enable it in DevTools:

  1. In DevTools, click the gear (:gear:) icon in the top-right corner.
  2. Go to the Experiments panel.
  3. Select WebAssembly Debugging: Enable DWARF support.

Exit Settings, then reload DevTools as prompted.

Next, open the options for the Chrome WebAssembly Debugging extension:

Wasm Debugging Options

Since itk-wasm builds in a clean Docker environment, the debugging source paths in the Docker environment are different from the paths on the host system. The debugging extension has a path substitution system that can account for these differences. In the Docker image, the directory where itk-wasm is invoked is mounted as /work. Substitute /work with the directory where the itk-wasm CLI is invoked. For example, if itk-wasm was invoked at /home/matt/src/itk-wasm/examples/Debugging, then set the path substitution as shown below:

Build the project with itk-wasm and the Debug CMAKE_BUILD_TYPE to include DWARF debugging information:

Here we load and run the WebAssembly with a simple HTML file and server:

<html>
  <head>
    <script src="https://cdn.jsdelivr.net/npm/itk-wasm@1.0.0-a.11/dist/umd/itk-wasm.js"></script>
  </head>
  <body>
    <p>This is an example to demonstrate browser-based debugging of
    C++-generated WebAssembly. For more information, please see the
    <a target="_blank" href="https://wasm.itk.org/examples/debugging.html">associated
      documentation</a>.</p>

    <script>
      window.addEventListener('load', (event) => {
        const pipeline = new URL('emscripten-build-debug/DebugMe', document.location)
        itk.runPipeline(null, pipeline)
      });
    </script>

  </body>
</html>

And we can debug the C++ code in Chrome’s DevTools debugger alongside the executing JavaScript!

What’s Next

In our next post, we will provide a tutorial on how to generate JavaScript and TypeScript bindings for Node.js and the browser.

Enjoy ITK!

2 Likes