快 11K Star 的 WebAssembly,你应该这样学
本文是学习 WebAssembly 系列的第三篇文章,也是想探究一下 Chrome 开发者工具对 WebAssembly 的调试支持度如何,通过这个探究的过程,我们会了解到 Chrome 调试工具各种方面的使用方法以及作用,发掘你可能不知道的一些知识点。
所以本文既可以当做学习使用 Chrome Devtools 调试工具的一篇比较全面的文章,也可以当做是介绍现阶段我们如何在浏览器中对 WebAssembly 相关的代码进行调试,帮助你成为一个合格的调试工程师 :)。
WebAssembly 的原始调试方式
Chrome 开发者工具目前已经支持 WebAssembly 的调试,虽然存在一些限制,但是针对 WebAssembly 的文本格式的文件能进行单个指令的分析以及查看原始的堆栈追踪,具体见如下图:
上述的方法对于一些无其他依赖函数的 WebAssembly 模块来说可以很好的运行,因为这些模块只涉及到很小的调试范围。但是对于复杂的应用来说,如 C/C++ 编写的复杂应用,一个模块依赖其他很多模块,且源代码与编译后的 WebAssembly 的文本格式的映射有较大的区别时,上述的调试方式就不太直观了,只能靠猜的方式才能理解其中的代码运行方式,且大多数人很难以看懂复杂的汇编代码。
更加直观的调试方式
现代的 JavaScript 项目在开发时通常也会存在编译的过程,使用 ES6 进行开发,编译到 ES5 及以下的版本进行运行,这个时候如果需要调试代码,就涉及到 Source Map 的概念,source map 用于映射编译后的对应代码在源代码中的位置,source map 使得客户端的代码更具可读性、更方便调试,但是又不会对性能造成很大的影响。
而 C/C++ 到 WebAssembly 代码的编译器 Emscripten 则支持在编译时,为代码注入相关的调试信息,生成对应的 source map,然后安装 Chrome 团队编写的
C/C++ Devtools Support
浏览器扩展,就可以使用 Chrome 开发者工具调试 C/C++ 代码了。
这里的原理其实就是,Emscripten 在编译时,会生成一种 DWARF 格式的调试文件,这是一种被大多数编译器使用的通用调试文件格式,而
C/C++ Devtools Support
则会解析 DWARF 文件,为 Chrome Devtools 在调试时提供 source map 相关的信息,使得开发者可以在 89+ 版本以上的 Chrome Devtools 上调试 C/C++ 代码。
调试简单的 C 应用
因为 DWARF 格式的调试文件可以提供处理变量名、格式化类型打印消化、在源代码中执行表达式等等,现在就让我们实际来编写一个简单的 C 程序,然后编译到 WebAssembly 并在浏览器中运行,查看实际的调试效果吧。
首先让我们进入到之前创建的 WebAssembly 目录下,激活 emcc 相关的命令,然后查看激活效果:
cd emsdk && source emsdk_env.sh
emcc --version # emcc (Emscripten gcc/clang-like replacement) 1.39.18 (a3beeb0d6c9825bd1757d03677e817d819949a77)
接着在 WebAssembly 创建一个
temp
文件夹,然后创建
temp.c
文件,填充如下内容并保存:
#include <stdlib.h>
void assert_less(int x, int y) {
if (x >= y) {
abort();
int main() {
assert_less(10, 20);
assert_less(30, 20);
上述代码在执行 asset_less
时,如果遇到 x >= y
的情况会抛出异常,终止程序执行。
在终端切换目录到 temp
目录下执行 emcc
命令进行编译:
emcc -g temp.c -o temp.html
上述命令在普通的编译形式上,加入了 -g
参数,告诉 Emscripten 在编译时为代码注入 DWARF 调试信息。
现在可以开启一个 HTTP 服务器,可以使用 npx serve .
,然后访问 localhost:5000/temp.html
查看运行效果。
需要确保已经安装了 Chrome 扩展:https://chrome.google.com/web...,以及 Chrome Devtools 升级到 89+ 版本。
为了查看调试效果,需要设置一些内容。
打开 Chrome Devtools 里面的 WebAssembly 调试选项
设置完之后,在工具栏顶部会出现一个 Reload 的蓝色按钮,需要重新加载配置,点击一下就好。
设置调试选项,在遇到异常的地方暂停
刷新浏览器,然后你会发现断点停在了 temp.js
,由 Emscripten 编译生成的 JS 胶水代码,然后顺着调用栈去找,可以查看到 temp.c
并定位到抛出异常的位置:
可以看到,我们成功在 Chrome Devtools 里面查看了 C 代码,并且代码停在了 abort()
处,同时还可以类似我们调试 JS 时一样,查看当前 scope 下的值:
如上述可以查看 x
、y
值,将鼠标浮动到 x
上还可以显示此时的值。
查看复杂类型值
实际上 Chrome Devtools 不仅可以查看原 C/C++ 代码中一些变量的普通类型值,如数字、字符串,还可以查看更加复杂的结构,如结构体、数组、类等内容,我们拿另外一个例子来展现这个效果。
我们通过一个在 C++ 里面绘制 曼德博图形 的例子来展示上述的效果,同样在 WebAssembly 目录下创建 mandelbrot
文件夹,然后添加 `mandelbrot.cc
文件,并填入如下内容:
#include <SDL2/SDL.h>
#include <complex>
int main() {