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.
What is WebAssembly?
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,
DWARF debugging standard and Wasm
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.
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.
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.
Get setup with LogRocket's modern error tracking in minutes:
- Visit https://logrocket.com/signup/ to get an app ID.
- Install LogRocket via NPM or script tag.
LogRocket.init()must be called client-side, not server-side.
- (Optional) Install plugins for deeper integrations with your stack:
- Redux middleware
- ngrx middleware
- Vuex plugin
$ npm i --save logrocket
import LogRocket from 'logrocket';
Add to your HTML:
<script>window.LogRocket && window.LogRocket.init('app/id');</script>