WebAssembly, (or Wasm), allows developers to use code gathered from languages like Rust, C or C+ within an internet browser at close to native speeds.
Wasm can also be successfully utilized as a lightweight docker replacement for platforms that require fast sandboxing. An interface that is platform independent such as standard WASI can enable file system access, basic functions such as the standard input and output, and other similar functions.
Despite the many benefits, debugging Wasm can be difficult, as real bugs are growing more complicated to reproduce. Source maps can be used to find reliable stack traces, to see what they truly look like and to achieve reliable file names in Wasm.
In this article, we will talk about Wasm, why it’s difficult to debug, and some workarounds you can use when debugging Wasm that involve source maps and stack unwinding.
Wasm is code for modern web browsers that relies on language with a very compact binary format, and provides other coding languages with a compilation target so they can work on the web. It works in tandem with JavaScript, sharing many functions.
As a result of this, even developers who don’t know how to write Wasm code can still use it. WASM’s compatibility is especially attractive considering 75 percent of web developers report using JavaScript for the majority of their coding needs.
Not all functions can be accessed in the same memory space, hence the utilization of stacking in the program. DWARF is useful for debugging other programs, but is also not fully functional with Wasm in any current execution engines. Over a year ago, changes in DWARF were implemented to permit understanding of WebAssembly. This came along with major modifications to the LLVM backend for Wasm.
Ultimately, mastering different types of code and their debugging methods is important for developers, especially those working in the cloud and in the Software-as-a-Service (SaaS) industry specifically. That’s because SaaS allows data to be accessed from any device with a browser and relies on code that makes up an application hosted in the browser’s server. Code that is compatible in different browsers and is bug-free ultimately enhances customer experience and increases customer retention.
First, you want to get a stack trace. How do we do this? It begins with unwinding the stack. WebAssembly’s unwinding scheme needs to be activated, which is usually done with libraries such as libunwind. For unwinding stacks for Wasm, the main thing to focus on is the return addresses. Anything beyond that is simply not necessary.
You can unwind a stack by capturing the registers, thus unwinding in the program as it’s running. This method is best used when Rust error alerts or C++ exceptions are presented to you. Stack winding can execute destructors when faced with exceptions,
Another way to unwind a stack is by using a memory dump. The full stack memory with registers is dumped into a memory dump, then unwound. It’s apparent that WebAssembly is not the best at facilitating stack unwinding, but this is not a huge deal-breaker if your browser works with JavaScript, as most do.
Since Wasm is essentially a stack machine, the function calls can be viewed within JavaScript’s own stack trace. By creating an exception object in JavaScript, you can then analyze its stack trace via this method.
The DWARF debugging standard has long been used for step-through debugging of binaries. DWARF is frequently used in the LLVM and other compiler backends, despite not being designed for this. In spite of DWARF not being compatible with Wasm in any current execution engines, Chrome and Firefox can still make use of debugging information. They achieve this by linking a source map to executing Wasm.
This is important for ensuring security as many people can be wary of using JavaScript on browsers like Chrome or Firefox. According to Brisbane-based software developer Will Ellis of Privacy Australia, when running certain browsers such as Chrome, “some people opt to turn JavaScript off completely and only allow it to function on websites that they truly trust.” Thankfully, source maps can define a format for mapping between the original input files and the resulting JavaScript instructions, sort of like a translator. This way, browser debugging can be performed against a view of the original input files.
The DWARF debugging standard embeds sections within the DWARF data in an executable file. This embedding is made possible by the fact that WebAssembly is an extensible object format. Wasm consists of two primary formats: WAST, a text-based format, and a binary format.
Because of this, some Wasm tools don’t recognize byte offsets, among other things. Unfortunately, these two versions are not compatible when working with debug info.
There are some serious problems with embedding debug info in the Wasm file. This enables anyone to potentially decompile coding and view the file descriptions from their metadata or build machines. Another problem is that DWARF data for Wasm can only be stored on a very large file, much bigger than the main Wasm file.
DWARF splitting is the answer here, in which DWARF data is segregated from the main executable. Once split, the debugging data can be stored in a non-executable and non-functional file solely for debugging.
How do you link these back together once they’ve been split? Embed a reference to a file that can be downloaded for debugging in the primary Wasm file for a special section, like with a source map.
It’s very important to connect the debug data with the correct Wasm file. A debug ID is necessary for this purpose. The Wasm toolchain can take these files and put them on a symbol server for debug data and binaries.
Challenges with source maps are also rampant in Wasm. This is because it’s difficult to scope info, access or map function names, and find variables in debugging mode, and can only be used with the text-based version of Wasm.
Just keep in mind that it’s important to consider the way WebAssembly interacts with JavaScript and other Wasm modules when debugging. Stack traces in Wasm come with a file name encoding the location information, yet it is still difficult to find the function index, as two different modules can have the same function index. Wasm modules are held in isolated containers, but can still export and import functions.
WebAssembly is a great tool for developers in spite of the challenges it has with debugging. Although there are a few more steps and a little creativity is required to get around the stack-based design, it is still possible to use DWARF to successfully debug Wasm.
Install LogRocket via npm or script tag. LogRocket.init()
must be called client-side, not
server-side
$ npm i --save logrocket // Code: import LogRocket from 'logrocket'; LogRocket.init('app/id');
// Add to your HTML: <script src="https://cdn.lr-ingest.com/LogRocket.min.js"></script> <script>window.LogRocket && window.LogRocket.init('app/id');</script>
Hey there, want to help make our blog better?
Join LogRocket’s Content Advisory Board. You’ll help inform the type of content we create and get access to exclusive meetups, social accreditation, and swag.
Sign up nowThe useReducer React Hook is a good alternative to tools like Redux, Recoil, or MobX.
Node.js v22.5.0 introduced a native SQLite module, which is is similar to what other JavaScript runtimes like Deno and Bun already have.
Understanding and supporting pinch, text, and browser zoom significantly enhances the user experience. Let’s explore a few ways to do so.
Playwright is a popular framework for automating and testing web applications across multiple browsers in JavaScript, Python, Java, and C#. […]