走近 WebAssembly 之调试大法

前言

WebAssembly是什么?

下面是来自官方的定义:

WebAssembly or wasm is a new portable, size- and load-time-efficient format suitable for compilation to the web.

关键词:”format”,WebAssembly 是一种编码格式,适合编译到web上运行。

事实上,WebAssembly可以看做是对JavaScript的加强,弥补JavaScript在执行效率上的缺陷。

  • 它是一个新的语言,它定义了一种AST,并可以用字节码的格式表示。
  • 它是对浏览器的加强,浏览器能够直接理解WebAssembly并将其转化为机器码。
  • 它是一种目标语言,任何其他语言都可以编译成WebAssembly在浏览器上运行。

想象一下,在计算机视觉,游戏动画,视频编解码,数据加密等需要需要高计算量的领域,如果想在浏览器上实现,并跨浏览器支持,唯一能做的就是用JavaScript来运行,这是一件吃力不讨好的事情。而WebAssembly可以将现有的用C,C++编写的库直接编译成WebAssembly运行到浏览器上, 并且可以作为库被JavaScript引用。那就意味着我们可以将很多后端的工作转移到前端,减轻服务器的压力。这是WebAssembly最为吸引人的特性。并且WebAssembly是运行于沙箱中,保证了其安全性。

更多关于WebAssembly基础入门, 可以看下这篇文章:https://segmentfault.com/a/1190000012110615, 写得很详细。

(后文将主要使用wasm 名称表示 WebAssembly)

怎么调试?

稍微了解javascript 的人应该知道,在chrome或者firefox的开发者面板中可以很方便对js代码加断点、查看变量、单步执行等等,非常方便!

既然wasm主要是运行在web浏览器上的(当然也可以在非web环境中运行,参见官方文档描述:http://webassembly.org/docs/non-web/),主要的调试方式无非是使用开发者控制台了!

但问题在于上文中说了 wasm 是一种二进制格式,虽然有可读的文本格式wast,但是调试起来依然比较费劲,最主要的问题是:你是不是更想能够调试被编译之前的c/c++ 源代码?

调试探索

搜了很多资料,走了不少弯路,总算是摸索出一条可行的调试之路!(当然如果你有更好的调试方法,请告诉我哦!)

环境&工具准备

说明:如果你想定制自己的wasm编译环境docker镜像,强烈建议在ubuntu中参考官方文档步骤搭建: http://webassembly.org/getting-started/developers-guide/

实验代码准备

github地址:https://github.com/zhouzhipeng/wasm-debug-test

  1. 编写一个简单的c程序,求两个数的平方和

debug.c

  1. 编译debug.c —> debug.wasm

使用上节中的docker镜像: zhouzhipeng/wasm-build

说明:关于emcc 命令细节,可以参考:http://kripken.github.io/emscripten-site/docs/compiling/WebAssembly.html

  1. 编写测试页面

说下大致逻辑:页面加载时会加载debug.wasm 文件并初始化,给页面上的按钮绑定click事件,点击时调用上面debug.c中的 sumOfSquare 函数。

debug.html

  1. 运行查看效果

为了简单起见,直接用python在当前目录临时启动一个http服务:

打开Firefox开发者版浏览器访问:http://localhost:8081/debug.html

《走近WebAssembly之调试大法》

点击click按钮:

《走近WebAssembly之调试大法》

很好,一切运行正常。接下来,尝试使用断点调试,并查看局部变量等。

基本调试

进入debugger面板,找到如下文件(wasm的可视化文本格式,是不是跟汇编指令很像?!所以名字带有assembly,哈哈)

并在对应代码行处打个断点:

《走近WebAssembly之调试大法》

好的,我们继续,再次点一下“click” 按钮,断点会进来:

《走近WebAssembly之调试大法》

注意上图红框中的局部变量var0,var1 就是我们的input1和input2,

可以接着用单步执行(注意不是旁边的step over 按钮,是箭头所示的step in !! 可能是bug):

《走近WebAssembly之调试大法》

对函数栈稍有了解的应该知道:上述指令如 get_local , i32.mul 等等会进行系列入栈、出栈操作,所以你看不到我们当时定义的临时变量 t1,t2, 它操作的直接是栈顶的元素.

firefox看不到stack栈中的元素,下文进阶调试中会用chrome浏览器演示下,感兴趣的客官请继续往下看!!

进阶调试

尼玛,说好的调试c/c++源代码呢!!!!

《走近WebAssembly之调试大法》

需要调试c源码,前面的emcc编译命令需要加点参数,关联一下 source map:

如你看到的,上面一共三成了三个文件:debug.wasm , debug.wasm.map , debug.wast

处理debug.wasm (二进制) 无法查看,其他的都可以看下:

是不是有种恍然大明白的感觉! 跟调试混淆的js 的方式很像。

刷新浏览器,看一下:

《走近WebAssembly之调试大法》

多了一个debug.c ! 是的,说明我们的sourcemap 生效了, 顺手在第二行打个断点。

点击click按钮,瞅一瞅:

《走近WebAssembly之调试大法》

有点尴尬,我明明打的第二行,断点却进入了第三行。。。(开发版。)

更加美中不足的是,如上图右上角红框,变量a 居然也无法查看!! 有点忧伤,不过好在右下角的局部变量还能看个大概。

所以我的建议是: 在debug.c 中打个断点,然后进入debug.html:xxxx 中单步调试, 如下,此时是可以双击进入的,两边断点状态是同步的:

《走近WebAssembly之调试大法》

填坑

在【基本调试】章节留了一坑,说可以用chrome 看下运行时的stack 栈状态,以下放一张截图证明我没有说谎 :

《走近WebAssembly之调试大法》

看到红色箭头处,有没有想起来 get_local 0 ,其实就是把我们的input1 (3) 压入栈了。可以单步一步步执行看下出栈/入栈效果.

另:chromd虽然也能调试wast,但是它把内容拆分成了好多小部分,不太方便调试。但是优势在于比firefox开发版多了个stack 查看功能。 很实用!!

总结

  1. 运行wasm编译环境的镜像: zhouzhipeng/wasm-build
  2. 编译命令: emcc debug.c -O1 -s WASM=1 -s SIDE_MODULE=1 -o debug.wasm -g4 --source-map-base http://localhost:8081/
  3. 本文演示源码地址:https://github.com/zhouzhipeng/wasm-debug-test

官方工具推荐:

  • https://wasdk.github.io/WasmFiddle/
  • https://webassembly.studio/

参考文献

  • http://webassemblycode.com/using-browsers-debug-webassembly/
  • https://segmentfault.com/a/1190000012110615
  • http://kripken.github.io/emscripten-site/docs/compiling/WebAssembly.html
1 收藏 评论

相关文章

可能感兴趣的话题



直接登录
跳到底部
返回顶部