Tại Hội nghị Nhà phát triển Chrome 2020, lần đầu tiên, chúng tôi đã minh hoạ tính năng hỗ trợ gỡ lỗi của Chrome dành cho các ứng dụng WebAssembly trên web. Kể từ đó, đội ngũ của họ đã đầu tư rất nhiều công sức vào việc xây dựng trải nghiệm dành cho nhà phát triển trên các ứng dụng lớn và thậm chí rất lớn. Trong bài đăng này, chúng tôi sẽ chỉ cho bạn các nút mà chúng tôi đã thêm (hoặc đã hoàn thiện) trong các công cụ và cách sử dụng chúng!
Gỡ lỗi có thể mở rộng
Hãy tiếp tục từ nơi chúng ta đã dừng lại trong bài đăng năm 2020. Sau đây là ví dụ mà chúng ta đã xem trước đó:
#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();
}
Đây vẫn là một ví dụ khá nhỏ và có thể bạn sẽ không thấy bất kỳ vấn đề thực sự nào bạn sẽ thấy trong một ứng dụng thực sự lớn, nhưng chúng tôi vẫn có thể cho bạn thấy các tính năng mới là gì. Quá trình thiết lập nhanh chóng, dễ dàng và bạn có thể tự mình thử nghiệm!
Trong bài đăng trước, chúng ta đã thảo luận cách biên dịch và gỡ lỗi ví dụ này. Hãy cùng thực hiện lại các thao tác đó, nhưng cũng hãy xem nhanh //performance//:
$ emcc -sUSE_SDL=2 -g -O0 -o mandelbrot.html mandelbrot.cc -sALLOW_MEMORY_GROWTH
Lệnh này tạo ra một tệp nhị phân wasm có kích thước 3 MB. Và phần lớn trong số đó, như bạn có thể mong đợi, là thông tin gỡ lỗi. Bạn có thể xác minh điều này bằng công cụ llvm-objdump
[1]. Ví dụ:
$ llvm-objdump -h mandelbrot.wasm
mandelbrot.wasm: file format wasm
Sections:
Idx Name Size VMA Type
0 TYPE 0000026f 00000000
1 IMPORT 00001f03 00000000
2 FUNCTION 0000043e 00000000
3 TABLE 00000007 00000000
4 MEMORY 00000007 00000000
5 GLOBAL 00000021 00000000
6 EXPORT 0000014a 00000000
7 ELEM 00000457 00000000
8 CODE 0009308a 00000000 TEXT
9 DATA 0000e4cc 00000000 DATA
10 name 00007e58 00000000
11 .debug_info 000bb1c9 00000000
12 .debug_loc 0009b407 00000000
13 .debug_ranges 0000ad90 00000000
14 .debug_abbrev 000136e8 00000000
15 .debug_line 000bb3ab 00000000
16 .debug_str 000209bd 00000000
Kết quả này cho chúng ta thấy tất cả các phần trong tệp wasm đã tạo, hầu hết trong số đó là các mục WebAssembly tiêu chuẩn, nhưng cũng có một số mục tuỳ chỉnh có tên bắt đầu bằng .debug_
. Đó là nơi tệp nhị phân chứa thông tin gỡ lỗi của chúng ta! Nếu cộng tất cả các kích thước, chúng ta sẽ thấy thông tin gỡ lỗi chiếm khoảng 2,3 MB trong tệp 3 MB. Nếu cũng time
lệnh emcc
, chúng ta sẽ thấy rằng trên máy của mình, mất khoảng 1,5 giây để chạy. Những con số này tạo ra một cơ sở nhỏ, nhưng chúng quá nhỏ nên có lẽ không ai để ý đến chúng. Mặc dù vậy, trong các ứng dụng thực tế, tệp nhị phân gỡ lỗi có thể dễ dàng đạt đến kích thước tính bằng GB và mất vài phút để xây dựng!
Bỏ qua Binaryen
Khi tạo ứng dụng wasm bằng Emscripten, một trong những bước tạo bản dựng cuối cùng là chạy trình tối ưu hoá Binaryen. Binaryen là một bộ công cụ biên dịch giúp tối ưu hoá và hợp pháp hoá các tệp nhị phân WebAssembly (tương tự như). Việc chạy Binaryen trong bản dựng khá tốn kém nhưng chỉ bắt buộc phải đáp ứng một số điều kiện nhất định. Đối với các bản gỡ lỗi, chúng ta có thể đẩy nhanh đáng kể thời gian xây dựng nếu không cần truyền tệp Binaryen. Thẻ Binaryen yêu cầu phổ biến nhất là để hợp pháp hoá các chữ ký hàm liên quan đến các giá trị số nguyên 64 bit. Bằng cách chọn tích hợp WebAssembly BigInt bằng -sWASM_BIGINT
, chúng ta có thể tránh được điều này.
$ emcc -sUSE_SDL=2 -g -O0 -o mandelbrot.html mandelbrot.cc -sALLOW_MEMORY_GROWTH -sWASM_BIGINT -sERROR_ON_WASM_CHANGES_AFTER_LINK
Chúng ta đã gửi cờ -sERROR_ON_WASM_CHANGES_AFTER_LINK
để đo lường thích hợp. Công cụ này giúp phát hiện thời điểm Binaryen đang chạy và đột ngột viết lại tệp nhị phân. Bằng cách này, chúng ta có thể đảm bảo mình duy trì được lộ trình nhanh chóng.
Mặc dù ví dụ của chúng ta khá nhỏ, nhưng chúng ta vẫn có thể thấy tác động của việc bỏ qua Binaryen! Theo time
, lệnh này chỉ chạy chưa đến 1 giây, vì vậy, nhanh hơn nửa giây so với trước đây!
Chỉnh sửa nâng cao
Bỏ qua bước quét tệp nhập
Thông thường, khi bạn liên kết một dự án Emscripten, emcc
sẽ quét mọi thư viện và tệp đối tượng đầu vào. Công cụ này thực hiện việc này nhằm triển khai các phần phụ thuộc chính xác giữa các hàm thư viện JavaScript và ký hiệu gốc trong chương trình của bạn. Đối với các dự án lớn, việc quét thêm các tệp đầu vào (sử dụng llvm-nm
) có thể làm tăng đáng kể thời gian liên kết.
Thay vào đó, bạn có thể chạy bằng -sREVERSE_DEPS=all
để yêu cầu emcc
bao gồm tất cả phần phụ thuộc gốc có thể có của các hàm JavaScript. Cách này có mức hao tổn kích thước mã nhỏ nhưng có thể tăng tốc thời gian liên kết và có thể hữu ích cho các bản gỡ lỗi.
Đối với một dự án nhỏ như ví dụ của chúng tôi, điều này không tạo ra sự khác biệt thực sự nhưng nếu bạn có hàng trăm hoặc thậm chí hàng nghìn tệp đối tượng trong dự án của mình, thì điều này có thể giúp cải thiện đáng kể thời gian liên kết.
Xoá phần "name"
Trong các dự án lớn, đặc biệt là những dự án sử dụng nhiều mẫu C++, phần "tên" WebAssembly có thể rất lớn. Trong ví dụ của chúng tôi, đây chỉ là một phần nhỏ trong tổng kích thước tệp (xem kết quả của llvm-objdump
ở trên), nhưng trong một số trường hợp, con số này có thể rất quan trọng. Nếu phần "name" của ứng dụng rất lớn và thông tin gỡ lỗi ngắn là đủ cho nhu cầu gỡ lỗi của bạn, thì bạn nên bỏ phần "name":
$ emstrip --no-strip-all --remove-section=name mandelbrot.wasm
Thao tác này sẽ bỏ phần "tên" WebAssembly trong khi vẫn giữ nguyên các phần gỡ lỗi DWARF.
Gỡ lỗi phân hạch
Các tệp nhị phân có nhiều dữ liệu gỡ lỗi không chỉ gây áp lực về thời gian tạo bản dựng mà còn cả thời gian gỡ lỗi. Trình gỡ lỗi cần tải dữ liệu và cần tạo chỉ mục cho dữ liệu đó để có thể phản hồi nhanh các truy vấn, chẳng hạn như "Biến cục bộ x thuộc kiểu gì?".
Tính năng phân tách khi gỡ lỗi cho phép chúng ta chia thông tin gỡ lỗi của một tệp nhị phân thành hai phần: một phần vẫn ở trong tệp nhị phân và một phần nằm trong một tệp riêng biệt có tên là đối tượng DWARF (.dwo
). Bạn có thể bật tính năng này bằng cách truyền cờ -gsplit-dwarf
đến Emscripten:
$ emcc -sUSE_SDL=2 -g -gsplit-dwarf -gdwarf-5 -O0 -o mandelbrot.html mandelbrot.cc -sALLOW_MEMORY_GROWTH -sWASM_BIGINT -sERROR_ON_WASM_CHANGES_AFTER_LINK
Dưới đây là các lệnh và những tệp được tạo bằng cách biên dịch mà không cần dữ liệu gỡ lỗi, với dữ liệu gỡ lỗi và cuối cùng là với cả dữ liệu gỡ lỗi và phân tách gỡ lỗi.
Khi chia tách dữ liệu DWARF, một phần dữ liệu gỡ lỗi sẽ nằm cùng với tệp nhị phân, trong khi phần lớn được đưa vào tệp mandelbrot.dwo
(như minh hoạ ở trên).
Đối với mandelbrot
, chúng tôi chỉ có một tệp nguồn, nhưng thường thì các dự án sẽ lớn hơn và bao gồm nhiều tệp. Quá trình phân chia gỡ lỗi sẽ tạo một tệp .dwo
cho mỗi tệp đó. Để phiên bản beta hiện tại của trình gỡ lỗi (0.1.6.1615) có thể tải thông tin gỡ lỗi phần phân tách này, chúng ta cần gói tất cả những thông tin đó vào một gói DWARF (.dwp
) như sau:
$ emdwp -e mandelbrot.wasm -o mandelbrot.dwp
Xây dựng gói DWARF từ các đối tượng riêng lẻ có lợi thế là bạn chỉ cần phân phát thêm một tệp! Chúng tôi hiện đang cố gắng tải tất cả đối tượng riêng lẻ trong một bản phát hành sau này.
DWARF 5 có gì?
Bạn có thể nhận thấy, chúng tôi đã chèn một cờ khác vào lệnh emcc
ở trên, -gdwarf-5
. Bật phiên bản 5 của biểu tượng DWARF (hiện không phải là chế độ mặc định) là một thủ thuật khác giúp chúng ta bắt đầu gỡ lỗi nhanh hơn. Cùng với nó, một số thông tin nhất định được lưu trữ trong tệp nhị phân chính mà phiên bản mặc định 4 đã bỏ qua. Cụ thể, chúng ta có thể xác định tập hợp đầy đủ các tệp nguồn chỉ từ tệp nhị phân chính. Nhờ vậy, trình gỡ lỗi có thể thực hiện các thao tác cơ bản như hiển thị toàn bộ cây nguồn và đặt các điểm ngắt mà không cần tải và phân tích cú pháp toàn bộ dữ liệu biểu tượng. Điều này giúp gỡ lỗi bằng biểu tượng phân tách nhanh hơn rất nhiều. Vì vậy, chúng ta luôn sử dụng kết hợp cờ dòng lệnh -gsplit-dwarf
và -gdwarf-5
!
Với định dạng gỡ lỗi DWARF5, chúng ta cũng có quyền truy cập vào một tính năng hữu ích khác. Thao tác này giới thiệu một chỉ mục tên trong dữ liệu gỡ lỗi sẽ được tạo khi chuyển cờ -gpubnames
:
$ emcc -sUSE_SDL=2 -g -gdwarf-5 -gsplit-dwarf -gpubnames -O0 -o mandelbrot.html mandelbrot.cc -sALLOW_MEMORY_GROWTH -sWASM_BIGINT -sERROR_ON_WASM_CHANGES_AFTER_LINK
Trong một phiên gỡ lỗi, hoạt động tra cứu biểu tượng thường diễn ra bằng cách tìm kiếm một thực thể theo tên. Ví dụ: khi tìm kiếm biến hoặc loại. Chỉ mục tên giúp tăng tốc quá trình tìm kiếm này bằng cách trỏ trực tiếp đến đơn vị biên dịch xác định tên đó. Nếu không có chỉ mục tên, chúng ta sẽ phải tìm kiếm toàn bộ dữ liệu gỡ lỗi để tìm đúng đơn vị biên dịch xác định thực thể có tên mà chúng ta đang tìm kiếm.
Dành cho người muốn tò mò: Xem xét dữ liệu gỡ lỗi
Bạn có thể sử dụng llvm-dwarfdump
để xem nhanh dữ liệu DWARF. Hãy thử làm như sau:
llvm-dwarfdump mandelbrot.wasm
Việc này mang đến cho chúng ta thông tin tổng quan về "Đơn vị biên dịch" (nói gần đúng là tệp nguồn) mà chúng tôi có thông tin gỡ lỗi. Trong ví dụ này, chúng ta chỉ có thông tin gỡ lỗi cho mandelbrot.cc
. Thông tin chung sẽ cho chúng ta biết rằng chúng ta có một đơn vị khung, nghĩa là chúng ta chưa có dữ liệu trong tệp này, đồng thời có một tệp .dwo
riêng chứa thông tin gỡ lỗi còn lại:
Bạn cũng có thể xem các bảng khác trong tệp này, chẳng hạn như ở bảng dòng cho thấy cách liên kết mã byte wasm với các dòng C++ (thử sử dụng llvm-dwarfdump -debug-line
).
Chúng ta cũng có thể xem thông tin gỡ lỗi có trong tệp .dwo
riêng biệt:
llvm-dwarfdump mandelbrot.dwo
Tóm tắt: Lợi ích của việc sử dụng tính năng phân tách gỡ lỗi là gì?
Có một số lợi ích của việc chia nhỏ thông tin gỡ lỗi nếu một thông tin gỡ lỗi đang hoạt động với các ứng dụng lớn:
Liên kết nhanh hơn: Trình liên kết không cần phân tích cú pháp toàn bộ thông tin gỡ lỗi nữa. Trình liên kết thường cần phân tích cú pháp toàn bộ dữ liệu DWARF trong tệp nhị phân. Bằng cách loại bỏ các phần lớn của thông tin gỡ lỗi thành các tệp riêng biệt, trình liên kết xử lý các tệp nhị phân nhỏ hơn, dẫn đến thời gian liên kết nhanh hơn (đặc biệt đúng với các ứng dụng lớn).
Gỡ lỗi nhanh hơn: Trình gỡ lỗi có thể bỏ qua việc phân tích cú pháp các ký hiệu bổ sung trong tệp
.dwo
/.dwp
đối với một số hoạt động tra cứu biểu tượng. Đối với một số hoạt động tra cứu (chẳng hạn như yêu cầu liên kết giữa các tệp wasm-to-C++), chúng ta không cần xem xét dữ liệu gỡ lỗi bổ sung. Việc này giúp chúng tôi tiết kiệm thời gian do không cần phải tải và phân tích cú pháp dữ liệu gỡ lỗi bổ sung.
1: Nếu bạn không có phiên bản mới của llvm-objdump
trong hệ thống và bạn đang sử dụng emsdk
, bạn có thể tìm thấy phiên bản đó trong thư mục emsdk/upstream/bin
.
Tải kênh xem trước xuống
Hãy cân nhắc sử dụng Chrome Canary, Dev hoặc Beta làm trình duyệt phát triển mặc định. Các kênh xem trước này cung cấp cho bạn quyền truy cập vào các tính năng mới nhất của Công cụ cho nhà phát triển, thử nghiệm API nền tảng web tiên tiến và tìm ra sự cố trên trang web của bạn trước khi người dùng của bạn làm điều đó!
Liên hệ với nhóm Công cụ của Chrome cho nhà phát triển
Sử dụng các lựa chọn sau đây để thảo luận về các tính năng mới và thay đổi trong bài đăng hoặc bất cứ vấn đề nào khác liên quan đến Công cụ cho nhà phát triển.
- Hãy gửi đề xuất hoặc phản hồi cho chúng tôi qua crbug.com.
- Báo cáo sự cố của Công cụ cho nhà phát triển bằng cách sử dụng phần Tuỳ chọn khác > Trợ giúp > Báo cáo sự cố về Công cụ cho nhà phát triển trong Công cụ cho nhà phát triển.
- Tweet tại @ChromeDevTools.
- Hãy để lại bình luận về tính năng mới trong video trên YouTube của Công cụ cho nhà phát triển hoặc video trên YouTube.