目前為止的道路
一年前,Chrome 宣布在 Chrome 開發人員工具中支援原生 WebAssembly 偵錯。
我們示範了基本步驟支援服務,並談到未來是否會開放使用來源地圖,而是使用 DWARF 資訊 (而非來源地圖):
- 解析變數名稱
- 美化排版類型
- 評估原文語言的運算式
- ...還有更多!
我們今天很高興向大家展示幾項預期的功能,以及 Emscripten 和 Chrome 開發人員工具團隊今年的進展,特別是 C 和 C++ 應用程式的進展。
在我們開始之前,提醒您,此新版本仍是新版服務的 Beta 版,請自行斟酌使用最新版所有工具,並自行承擔風險。如果遇到任何問題,請向 https://bugs.chromium.org/p/chromium/issues/entry?template=DevTools+issue 回報問題。
讓我們先從上次的相同 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);
}
如要編譯,請使用最新版 Emscripten 並傳遞 -g
旗標 (與原始貼文相同),以納入偵錯資訊:
emcc -g temp.c -o temp.html
現在我們可以透過 localhost HTTP 伺服器提供產生的頁面 (例如,使用 serve),並在最新的 Chrome Canary 中開啟網頁。
這次我們也會需要與 Chrome 開發人員工具整合的輔助擴充功能,可協助您瞭解 WebAssembly 檔案編碼的所有偵錯資訊。請前往以下連結進行安裝:goo.gle/wasm-debugging-extension
此外,建議您也在開發人員工具的實驗功能中啟用 WebAssembly 偵錯功能。開啟 Chrome 開發人員工具,按一下開發人員工具窗格右上角的齒輪 (⚙) 圖示,前往「Experiments」面板,然後勾選「WebAssembly Debugging: Enable DWARF support」。
關閉「Settings」後,開發人員工具就會建議自行重新載入以套用設定,讓我們開始吧!以上就是一次性設定
現在我們可以返回「Sources」面板,啟用「Pause onexceptions」 (⏸圖示),勾選「Pause on saughtException」,然後重新載入頁面。開發人員工具應在例外狀況時暫停:
根據預設,此項目會在 Emscripten 產生的 glue 程式碼上停止,但畫面右側會顯示代表錯誤的堆疊追蹤的「Call Stack」檢視畫面,且可以前往叫用 abort
的原始 C 行:
現在,如果查看「Scope」(範圍) 檢視畫面,就能看到 C/C++ 程式碼中變數的原始名稱和值,而不必再瞭解類似 $localN
這類形狀名稱的意義,以及這些名稱與您編寫的原始碼之間的關係。
這不僅適用於原始值 (例如整數),也適用於結構、類別、陣列等複合類型!
支援多種類型
以下讓我們來看看一個較複雜的範例,以顯示那些結果。這次我們會使用以下 C++ 程式碼繪製 Mandelbrot 碎段:
#include <SDL2/SDL.h>
#include <complex>
int main() {
// Init SDL.
int width = 600, height = 600;
SDL_Init(SDL_INIT_VIDEO);
SDL_Window* window;
SDL_Renderer* renderer;
SDL_CreateWindowAndRenderer(width, height, SDL_WINDOW_OPENGL, &window,
&renderer);
// Generate a palette with random colors.
enum { MAX_ITER_COUNT = 256 };
SDL_Color palette[MAX_ITER_COUNT];
srand(time(0));
for (int i = 0; i < MAX_ITER_COUNT; ++i) {
palette[i] = {
.r = (uint8_t)rand(),
.g = (uint8_t)rand(),
.b = (uint8_t)rand(),
.a = 255,
};
}
// Calculate and draw the Mandelbrot set.
std::complex<double> center(0.5, 0.5);
double scale = 4.0;
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) {
std::complex<double> point((double)x / width, (double)y / height);
std::complex<double> c = (point - center) * scale;
std::complex<double> z(0, 0);
int i = 0;
for (; i < MAX_ITER_COUNT - 1; i++) {
z = z * z + c;
if (abs(z) > 2.0)
break;
}
SDL_Color color = palette[i];
SDL_SetRenderDrawColor(renderer, color.r, color.g, color.b, color.a);
SDL_RenderDrawPoint(renderer, x, y);
}
}
// Render everything we've drawn to the canvas.
SDL_RenderPresent(renderer);
// SDL_Quit();
}
您可以看到這個應用程式仍然很小,只有包含 50 行程式碼的單一檔案,但這次也會使用一些外部 API,例如用於圖像的 SDL 程式庫以及 C++ 標準程式庫的複雜數字。
我要使用與上述相同的 -g
旗標編譯程式碼,以便納入偵錯資訊,並且要求 Emscripten 提供 SDL2 程式庫,並允許任意大小的記憶體:
emcc -g mandelbrot.cc -o mandelbrot.html \ -s USE_SDL=2 \ -s ALLOW_MEMORY_GROWTH=1
在瀏覽器中造訪產生的頁面時,我能夠看到美觀的碎片形狀,並且隨機挑選一些顏色:
開啟開發人員工具後,再次顯示原始 C++ 檔案。但這次的程式碼裡沒有任何錯誤,所以我們改為在程式碼開頭設定某個中斷點。
再次重新載入頁面時,偵錯工具會直接在 C++ 來源中暫停:
右側已能看到所有變數,但目前只有 width
和 height
初始化,所以沒有太多需要檢查。
讓我們在主要的 Mandelbrot 迴圈中設定另一個中斷點,然後繼續執行以跳過一點。
此時,palette
已填滿一些隨機顏色,我們可以展開陣列本身以及個別的 SDL_Color
結構,並檢查其元件,確認一切看起來都沒問題 (例如「alpha」管道一律設為完全不透明度)。同樣地,我們也可以展開及檢查 center
變數中儲存的複數真實和虛部分部分。
如要存取難以透過「範圍」檢視畫面存取的深巢狀屬性,您也可以使用主控台評估!不過請注意,目前不支援較複雜的 C++ 運算式。
讓我們恢復執行數次,觀察內部 x
的變化,方法如下:再次查看「Scope」(範圍) 檢視畫面、將變數名稱新增至觀察清單、在主控台中評估,或是將滑鼠遊標懸停在原始碼中的變數上:
我們可以從這裡逐步或逐步改善 C++ 陳述式,並觀察其他變數的變化情形:
好的,當有偵錯資訊可用時,這一切就非常實用,但如果要對非使用偵錯選項建構的程式碼進行偵錯,該怎麼辦?
原始 WebAssembly 偵錯
舉例來說,我們要求 Emscripten 提供預先建構的 SDL 程式庫供我們使用,而非自行從來源編譯,因此偵錯工具目前至少無法找到相關來源。讓我們再次登入來進入 SDL_RenderDrawColor
:
我們即將返回原始的 WebAssembly 偵錯體驗。
現在,看起來有點可怕,而且大多數網頁開發人員都不需要處理,但有時您可能要對建構的程式庫進行偵錯,無論該程式庫是您無法控管的「第三方」程式庫,或是您正在執行僅在實際工作環境會發生的其中一個錯誤。
為了因應這類情況,我們也對基本偵錯體驗做了一些改善。
首先,如果您之前使用過原始 WebAssembly 偵錯,可能會注意到整個反組譯現在會顯示在單一檔案中,再猜測 Sources 項目 wasm-53834e3e/
wasm-53834e3e-7
可能對應的函式。
全新名稱產生配置
另外,我們也改善了反組譯碼檢視畫面中的名稱。您之前看到的是數字索引;如果使用的是函式,則不會顯示名稱。
我們現在使用 WebAssembly 名稱區段、匯入/匯出路徑等提示,產生與其他反組工具的名稱,最後如果一切失敗,則根據項目的類型和索引 (如 $func123
) 產生這些名稱。您可以從上方的螢幕截圖瞭解,這種做法已有助於取得更易讀的堆疊追蹤及拆解。
如果沒有可用的類型資訊,可能就很難檢查原始值以外的任何值,例如指標會顯示為一般整數,因為無法得知這些值儲存在記憶體背後的內容。
記憶體檢查
先前,您只能展開 Scope 檢視畫面中的 env.memory
表示 WebAssembly 記憶體物件,以查詢個別位元組。這種做法可以在一些小的情況下發揮,但不太方便展開,也不允許以位元組值以外的格式重新解讀資料。我們還新增了一些新功能:線性記憶體檢查器。
在 env.memory
上按一下滑鼠右鍵,應該會看到名為「Inspect memory」(檢查記憶體) 的新選項:
系統點選後即會開啟記憶體檢查器,您可以在其中以十六進位和 ASCII 檢視區塊查看 WebAssembly 記憶體、前往特定位址,以及解讀不同格式的資料:
進階情境和注意事項
剖析 WebAssembly 程式碼
開啟 DevTools 後,WebAssembly 程式碼會「分層」為未最佳化的版本,以便啟用偵錯功能。這個版本的速度明顯變慢,也就是說,您無法在開發人員工具開啟時,仰賴 console.time
、performance.now
和其他測量程式碼速度的方法,因為您取得的數據並不代表實際效能。
請改用開發人員工具的效能面板,以完整速度執行程式碼,並提供不同功能使用時間的詳細資料:
您也可以在 DevTools 關閉的情況下執行應用程式,並在完成後開啟應用程式來檢查控制台。
我們會在日後改善剖析情境,但目前請多加留意。如要進一步瞭解 WebAssembly 分層情境,請參閱 WebAssembly 編譯管道的說明文件。
在不同機器 (包括 Docker / 主機) 上建構及偵錯
在 Docker、虛擬機器或遠端建構伺服器上建構時,您可能會遇到建構期間所用來源檔案的路徑,與執行 Chrome 開發人員工具的檔案系統路徑不一致的情況。在此範例中,檔案會顯示在「Sources」面板中,但無法載入。
為修正這個問題,我們已在 C/C++ 擴充功能選項中實作路徑對應功能。您可以使用此元件重新對應任意路徑,並協助開發人員工具找出來源。
舉例來說,如果主體機器上的專案位於路徑 C:\src\my_project
下,但建立在 Docker 容器內該路徑顯示為 /mnt/c/src/my_project
的 Docker 容器內,則您可以在偵錯期間將這些路徑指定為前置字串,以重新對應其:
第一個相符的前置字串「wins」。如果您熟悉其他 C++ 偵錯工具,這個選項類似於 GDB 中的 set substitute-path
指令或 LLDB 中的 target.source-map
設定。
對最佳化版本進行偵錯
與任何其他程式語言一樣,在最佳化功能停用的情況下,偵錯功能可發揮最佳效果。最佳化作業可能會以內嵌函式的形式內嵌函式、重新排序程式碼,或是移除程式碼的某些部分。因此,這麼做可能會混淆偵錯工具,進而讓您身為使用者。
如果您不介意對偵錯功能設限,但仍想對最佳化版本進行偵錯,那麼大多數最佳化功能都能正常運作,但內嵌函式除外。我們預計日後會解決其餘問題,但目前請在針對任何 -O
層級最佳化項目進行編譯時,使用 -fno-inline
停用此功能,例如:
emcc -g temp.c -o temp.html \ -O3 -fno-inline
分離偵錯資訊
偵錯資訊會保留許多與程式碼、定義的類型、變數、函式、範圍和位置相關的詳細資料,這些細節可能對偵錯工具有幫助。因此,通常比程式碼本身大。
如要加快 WebAssembly 模組的載入和編譯速度,建議您將這項偵錯資訊分割為獨立的 WebAssembly 檔案。如要在 Emscripten 中執行這項操作,請傳遞包含所需檔案名稱的 -gseparate-dwarf=…
旗標:
emcc -g temp.c -o temp.html \ -gseparate-dwarf=temp.debug.wasm
在此情況下,主要應用程式只會儲存檔案名稱 temp.debug.wasm
,而當您開啟 DevTools 時,輔助程式擴充功能將可找到並載入該檔案。
與上述最佳化功能搭配使用時,這項功能甚至可用於發布幾乎最佳化的應用程式正式版本,之後再使用本機端檔案對這些版本進行偵錯。在此情況下,我們還需要覆寫儲存的網址,協助擴充功能尋找側邊檔案,例如:
emcc -g temp.c -o temp.html \ -O3 -fno-inline \ -gseparate-dwarf=temp.debug.wasm \ -s SEPARATE_DWARF_URL=file://[local path to temp.debug.wasm]
未完待付費...
哇,這些新功能真是好玩!
隨著所有新的整合項目,Chrome 開發人員工具也能成為強大、功能強大的偵錯工具,不僅適用於 JavaScript,也能針對 C 和 C++ 應用程式提供所需工具,讓您更輕鬆取用以各種技術建構的應用程式,並導入共用的跨平台網路。
然而,我們的旅程尚未結束。後續工作包括:
- 清除偵錯體驗中的粗糙邊緣。
- 新增對自訂類型格式設定工具的支援。
- 努力改善 WebAssembly 應用程式的剖析作業。
- 新增程式碼涵蓋率支援功能,以便更輕鬆地找出未使用的程式碼。
- 改善對主控台評估作業中的運算式支援。
- 新增支援的語言。
- …還有更多!
同時,請在您自己的程式碼中試用目前的 Beta 版,並將發現的任何問題回報給 https://bugs.chromium.org/p/chromium/issues/entry?template=DevTools+issue。
下載預覽管道
考慮使用 Chrome Canary 版、開發人員版或 Beta 版做為預設開發瀏覽器。這些預覽管道可讓您存取開發人員工具的最新功能、測試最先進的網路平台 API,並在使用者使用之前就在網站上發現問題!
與 Chrome 開發人員工具團隊聯絡
使用下列選項,討論文章的新功能和異動,以及其他與開發人員工具相關的事項。
- 請透過 crbug.com 提交建議或意見回饋。
- 如要回報開發人員工具的問題,請在開發人員工具中依序點選「更多選項」圖示 >「說明」 >「回報開發人員工具的問題」。
- 在 @ChromeDevTools 張貼推文。
- 歡迎對開發人員工具的 YouTube 影片或開發人員工具秘訣 (YouTube 影片) 提供意見。