當前位置:
首頁 > 最新 > 走近 WebAssembly 之調試大法

走近 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, 寫得很詳細。

(後文將主要使用名稱表示 WebAssembly)

怎麼調試?

稍微了解的人應該知道,在chrome或者firefox的開發者面板中可以很方便對js代碼加斷點、查看變數、單步執行等等,非常方便!

既然主要是運行在web瀏覽器上的(當然也可以在非web環境中運行,參見官方文檔描述:http://webassembly.org/docs/non-web/),主要的調試方式無非是使用開發者控制台了!

但問題在於上文中說了是一種二進位格式,雖然有可讀的文本格式,但是調試起來依然比較費勁,最主要的問題是:你是不是更想能夠調試被編譯之前的源代碼?

調試探索

搜了很多資料,走了不少彎路,總算是摸索出一條可行的調試之路!(當然如果你有更好的調試方法,請告訴我哦!)

環境&工具準備

wasm編譯環境 docker版 , 鏡像 zhouzhipeng/wasm-build

Firefox最新開發者版, 下載地址

文本編輯器

說明:如果你想定製自己的wasm編譯環境docker鏡像,強烈建議在ubuntu中參考官方文檔步驟搭建: http://webassembly.org/getting-started/developers-guide/

實驗代碼準備

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

編寫一個簡單的c程序,求兩個數的平方和

debug.c

intsumOfSquare(inta,intb){

intt1=a*a;

intt2=b*b;

returnt1+t2;

}

編譯debug.c —> debug.wasm

使用上節中的docker鏡像: zhouzhipeng/wasm-build

#1.先運行wasm編譯docker容器 (鏡像託管在docker官方hub,可能會比較慢,請耐心等待)

?wasm-debug-testgit:(master)?dockerrun-it--namewasm-test-v$(pwd):/data/zhouzhipeng/wasm-buildbash

#2.編譯debug.c為debug.wasm 文件

root@f4d3ee71bec8:/data# cd /data/

root@f4d3ee71bec8:/data# emcc debug.c -O1 -s WASM=1 -s SIDE_MODULE=1 -o debug.wasm

說明:關於命令細節,可以參考:http://kripken.github.io/emscripten-site/docs/compiling/WebAssembly.html

編寫測試頁面

說下大致邏輯:頁面載入時會載入文件並初始化,給頁面上的按鈕綁定click事件,點擊時調用上面中的函數。

debug.html

// 下面這些配置是作為wasm初始化用的,去掉某一個會報錯。

constimportObj={

env:{

memory:newWebAssembly.Memory({initial:256,maximum:256}),

memoryBase:,

tableBase:,

table:newWebAssembly.Table({initial:10,element:"anyfunc"}),

abort:function(){}

}

};

// 直接使用 WebAssembly.instantiateStream的方式會報錯,說是 debug.wasm 資源不是 application/wasm 格式s.

fetch("./debug.wasm").then(response=>

response.arrayBuffer()

).then(bytes=>WebAssembly.instantiate(bytes,importObj)).then(results=>{

instance=results.instance;

varsumOfSquare=instance.exports._sumOfSquare;//注意這裡導出的方法名前有下劃線!!

varbutton=document.getElementById("run");

button.addEventListener("click",function(){

varinput1=3;

varinput2=4;

alert("sumOfSquare("+input1+","+input2+")="+sumOfSquare(input1,input2));

},false);

});

運行查看效果

為了簡單起見,直接用在當前目錄臨時啟動一個http服務:

?wasm-debug-testgit:(master)?python-mSimpleHTTPServer8081

Serving HTTPon0.0.0.0port8081...

打開Firefox開發者版瀏覽器訪問:http://localhost:8081/debug.html

點擊click按鈕:

很好,一切運行正常。接下來,嘗試使用斷點調試,並查看局部變數等。

基本調試

進入debugger面板,找到如下文件(wasm的可視化文本格式,是不是跟彙編指令很像?!所以名字帶有assembly,哈哈)

並在對應代碼行處打個斷點:

好的,我們繼續,再次點一下「click」 按鈕,斷點會進來:

注意上圖紅框中的局部變數var0,var1 就是我們的input1和input2,

可以接著用單步執行(注意不是旁邊的step over 按鈕,是箭頭所示的step in !! 可能是bug):

對函數棧稍有了解的應該知道:上述指令如 get_local , i32.mul 等等會進行系列入棧、出棧操作,所以你看不到我們當時定義的臨時變數 t1,t2, 它操作的直接是棧頂的元素.

firefox看不到stack棧中的元素,下文進階調試中會用chrome瀏覽器演示下,感興趣的客官請繼續往下看!!

進階調試

尼瑪,說好的調試c/c++源代碼呢!!!!

需要調試c源碼,前面的emcc編譯命令需要加點參數,關聯一下 source map:

root@f4d3ee71bec8:/data# emcc debug.c -O1 -s WASM=1 -s SIDE_MODULE=1 -o debug.wasm -g4 --source-map-base http://localhost:8081/

root@f4d3ee71bec8:/data# ls

處理debug.wasm (二進位) 無法查看,其他的都可以看下:

root@f4d3ee71bec8:/data# cat debug.wast

(module

(type$FUNCSIG$vi(func(parami32)))

(import"env""table"(table2anyfunc))

(import"env""memoryBase"(global$memoryBasei32))

(import"env""tableBase"(global$tableBasei32))

(import"env""abort"(func$abort(parami32)))

(global$STACKTOP(muti32)(i32.const))

(global$STACK_MAX(muti32)(i32.const))

(global$fp$_sumOfSquare i32(i32.const1))

(elem(get_global$tableBase)$b0$_sumOfSquare)

(export"__post_instantiate"(func$__post_instantiate))

(export"_sumOfSquare"(func$_sumOfSquare))

(export"runPostSets"(func$runPostSets))

(export"fp$_sumOfSquare"(global$fp$_sumOfSquare))

(func$_sumOfSquare(;1;)(param$i32)(param$1i32)(resulti32)

;;@debug.c:2:

(set_local$

(i32.mul

(get_local$)

(get_local$)

)

)

....後面內容省略

{"version":3,"sources":["debug.c"],"names":[],"mappings":"mNACA,OACA,OACA"}

是不是有種恍然大明白的感覺! 跟調試混淆的js 的方式很像。

刷新瀏覽器,看一下:

多了一個debug.c ! 是的,說明我們的sourcemap 生效了, 順手在第二行打個斷點。

點擊click按鈕,瞅一瞅:

有點尷尬,我明明打的第二行,斷點卻進入了第三行。。。(開發版。)

更加美中不足的是,如上圖右上角紅框,變數居然也無法查看!! 有點憂傷,不過好在右下角的局部變數還能看個大概。

所以我的建議是:在debug.c 中打個斷點,然後進入debug.html:xxxx 中單步調試, 如下,此時是可以雙擊進入的,兩邊斷點狀態是同步的:

填坑

在【基本調試】章節留了一坑,說可以用chrome 看下運行時的stack 棧狀態,以下放一張截圖證明我沒有說謊 :

看到紅色箭頭處,有沒有想起來 get_local 0 ,其實就是把我們的input1 (3) 壓入棧了。可以單步一步步執行看下出棧/入棧效果.

另:chromd雖然也能調試wast,但是它把內容拆分成了好多小部分,不太方便調試。但是優勢在於比firefox開發版多了個stack 查看功能。 很實用!!

總結

運行wasm編譯環境的鏡像: zhouzhipeng/wasm-build

編譯命令:

本文演示源碼地址: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

覺得本文對你有幫助?請分享給更多人

關注「前端大全」,提升前端技能


喜歡這篇文章嗎?立刻分享出去讓更多人知道吧!

本站內容充實豐富,博大精深,小編精選每日熱門資訊,隨時更新,點擊「搶先收到最新資訊」瀏覽吧!


請您繼續閱讀更多來自 前端大全 的精彩文章:

主流瀏覽器圖片反防盜鏈方法總結
一篇文章教會你 Event loop——瀏覽器和 Node

TAG:前端大全 |