目前的路線
一年前,Chrome 宣布初步支援 Chrome 開發人員工具中的原生 WebAssembly 偵錯功能。
我們示範了基本逐步執行支援功能,並討論了未來使用 DWARF 資訊而非原始對應圖的機會:
- 解析變數名稱
- 美化排版類型
- 評估原始語言中的運算式
- ...還有更多!
今天,我們很高興展示承諾的功能已實現,以及 Emscripten 和 Chrome 開發人員工具團隊今年以來的進展,特別是針對 C 和 C++ 應用程式。
在我們開始之前,請注意這仍是新體驗的 Beta 版,您必須自行承擔使用所有工具最新版本的風險,如果遇到任何問題,請前往 https://issues.chromium.org/issues/new?noWizard=true&template=0&component=1456350 回報。
我們先從上次相同的簡易 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
我們現在可以透過本機 HTTP 伺服器 (例如使用 serve) 提供產生的網頁,並在最新的 Chrome Canary 中開啟該網頁。
這次我們也需要一個輔助擴充功能,整合 Chrome 開發人員工具,並協助解讀 WebAssembly 檔案中編碼的所有偵錯資訊。請按一下這個連結安裝:goo.gle/wasm-debugging-extension
建議您一併在開發人員工具的「實驗」中啟用 WebAssembly 偵錯功能。開啟 Chrome 開發人員工具,按一下開發人員工具窗格右上角的齒輪 (⚙) 圖示,前往「Experiments」面板,勾選「WebAssembly Debugging: Enable DWARF support」。
關閉「設定」後,開發人員工具會建議重新載入,以便套用設定,因此我們就這麼做吧。以上就是一次性設定的內容。
接下來,我們可以返回「來源」面板,啟用「遇到例外狀況時暫停」 (⏸ 圖示),然後勾選「遇到已偵測到的例外狀況時暫停」,並重新載入網頁。您應該會看到開發人員工具在例外狀況時暫停:
根據預設,它會在 Emscripten 產生的黏合程式碼上停止,但您可以在右側看到代表錯誤堆疊追蹤的「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
已完成初始化,因此沒有太多內容可檢查。
讓我們在主要曼德勃羅圖迴圈中設定另一個中斷點,並繼續執行,略過一點點。
此時,我們的 palette
已填入一些隨機顏色,我們可以展開陣列本身和個別 SDL_Color
結構,並檢查其元件,確認一切正常 (例如,「alpha」管道一律設為完全不透明)。同樣地,我們可以展開並檢查儲存在 center
變數中的複數的實部和虛部。
如果您想存取深層巢狀資源,但透過範圍檢視畫面很難導覽,也可以使用控制台評估功能!不過,請注意,系統目前不支援較複雜的 C++ 運算式。
我們可以暫停執行幾次,看看內部 x
的變化情形,方法是再次查看「範圍」檢視畫面、將變數名稱加入監控清單、在控制台中評估,或是將滑鼠游標懸停在原始程式碼中的變數上:
從這裡,我們可以逐步執行或略過 C++ 陳述式,並觀察其他變數的變化情形:
好,如果有偵錯資訊,這一切都會運作良好,但如果我們想偵錯未使用偵錯選項建構的程式碼,該怎麼辦呢?
原始 WebAssembly 偵錯
舉例來說,我們要求 Emscripten 為我們提供預先建構的 SDL 程式庫,而不是自行從來源編譯,因此目前至少無法讓偵錯工具找到相關聯的來源。讓我們再次介紹 SDL_RenderDrawColor
:
我們將回到原始的 WebAssembly 偵錯體驗。
雖然這看起來有點嚇人,而且大多數網頁開發人員都不會遇到這種情況,但有時您可能需要對沒有偵錯資訊的建構程式庫進行偵錯,可能是因為這是您無法控制的 3rd 方程式庫,或是您遇到只會在實際環境中發生的錯誤。
為協助處理這些情況,我們也對基本偵錯體驗進行了一些改善。
首先,如果您之前曾使用原始 WebAssembly 偵錯功能,您可能會發現整個反組譯作業現在會顯示在單一檔案中,不再需要猜測 Sources 項目 wasm-53834e3e/
wasm-53834e3e-7
可能對應的函式。
新建名稱產生配置
我們也改善了反組譯檢視畫面中的名稱。先前您只會看到數字索引,或是在函式中完全沒有名稱。
我們現在會使用 WebAssembly 名稱部分的提示、匯入/匯出路徑,以及在所有其他方法都失敗時,根據 $func123
等項目的類型和索引來產生名稱,以便產生名稱,這與其他解組工具類似。您可以看到,在上述螢幕截圖中,這項功能已可協助您取得較易讀的堆疊追蹤和反組譯。
當沒有可用的類型資訊時,您可能很難檢查基本類型以外的任何值。舉例來說,指標會顯示為一般整數,而您無法得知記憶體中儲存了什麼。
記憶體檢查
先前您只能展開 WebAssembly 記憶體物件 (在「Scope」檢視畫面中以 env.memory
表示) 來查詢個別位元組。這在一些小情況下可以使用,但擴充不是特別方便,而且不允許以位元組值以外的格式重新解譯資料。我們也新增一項新功能,協助您進行線性記憶體檢查器。
如果您在 env.memory
上按一下滑鼠右鍵,現在應該會看到名為「Inspect memory」的新選項:
點選後,系統會顯示記憶體檢查器,您可以在其中以十六進制和 ASCII 檢視方式檢查 WebAssembly 記憶體、前往特定位址,以及解讀不同格式的資料:
進階情況和注意事項
剖析 WebAssembly 程式碼
開啟開發人員工具時,WebAssembly 程式碼會「降級」至未最佳化的版本,以便進行偵錯。這個版本的執行速度慢很多,也就是說,在開發人員工具開啟時,您無法仰賴 console.time
、performance.now
及其他評估程式碼速度的方法,因為您取得的數字完全並不代表實際的效能。
請改用開發人員工具效能面板,以便完整速度執行程式碼,並提供不同函式所花費時間的詳細說明:
或者,您也可以在關閉開發人員工具的情況下執行應用程式,並在完成後開啟開發人員工具,以便檢查控制台。
我們會在日後改善剖析情境,但目前這項功能仍有待改進。如要進一步瞭解 WebAssembly 分層情境,請參閱 WebAssembly 編譯管道的說明文件。
在不同機器 (包括 Docker / 主機) 上建構及偵錯
在 Docker、虛擬機器或遠端建構伺服器中建構時,您可能會遇到以下情況:建構期間使用的來源檔案路徑,與 Chrome 開發人員工具執行時所用的檔案系統路徑不符。在這種情況下,檔案會顯示在「Sources」面板中,但無法載入。
為修正這個問題,我們已在 C/C++ 擴充功能選項中實作路徑對應功能。您可以使用它重新對應任意路徑,並協助開發人員工具找出來源。
舉例來說,如果主機電腦上的專案位於路徑 C:\src\my_project
下方,但在 Docker 容器中建構時,該路徑會以 /mnt/c/src/my_project
表示,您可以在偵錯期間將其重新對應,方法是將這些路徑指定為前置字串:
第一個相符的前置字元「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
,而當您開啟開發人員工具時,輔助擴充功能將可找到並載入該檔案。
搭配上述最佳化功能使用時,這項功能甚至可用於發布幾乎已最佳化的應用程式正式版本,並在日後使用本機端檔案進行偵錯。在這種情況下,我們還需要覆寫儲存的網址,以協助擴充功能尋找側邊檔案,例如:
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://issues.chromium.org/issues/new?noWizard=true&template=0&component=1456350。
下載預覽管道
建議您將 Chrome Canary、開發人員版或Beta 版設為預設開發人員版瀏覽器。這些預覽管道可讓您存取最新的 DevTools 功能,測試最新的網路平台 API,並在使用者發現問題前,協助您找出網站的問題!
與 Chrome 開發人員工具團隊聯絡
請使用下列選項討論新功能、更新或任何與開發人員工具相關的內容。
- 請透過 crbug.com 提交意見回饋和功能要求。
- 在開發人員工具中,依序點選 和 [更多選項] > [說明] > [回報開發人員工具問題] 回報開發人員工具問題。
- 在 Twitter 上傳送訊息給 @ChromeDevTools。
- 在 YouTube 影片「What's new in DevTools」或「DevTools 提示」YouTube 影片中留言。