Nahla Davies Technical Copywriter

How to debug WASM and achieve a reliable stack trace

4 min read 1129

WASM Logo

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?

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% 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.

Stack unwinding

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.

We made a custom demo for .
No really. Click here to check it out.

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.

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.

DWARF Linking to WASM With Source Map
Source

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.

Conclusion

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.

: Full visibility into your web apps

LogRocket is a frontend application monitoring solution that lets you replay problems as if they happened in your own browser. Instead of guessing why errors happen, or asking users for screenshots and log dumps, LogRocket lets you replay the session to quickly understand what went wrong. It works perfectly with any app, regardless of framework, and has plugins to log additional context from Redux, Vuex, and @ngrx/store.

In addition to logging Redux actions and state, LogRocket records console logs, JavaScript errors, stacktraces, network requests/responses with headers + bodies, browser metadata, and custom logs. It also instruments the DOM to record the HTML and CSS on the page, recreating pixel-perfect videos of even the most complex single-page apps.

.
Nahla Davies Technical Copywriter

Leave a Reply