Hành trình đến nay
Một năm trước, Chrome đã thông báo về tính năng hỗ trợ ban đầu cho tính năng gỡ lỗi WebAssembly gốc trong Chrome DevTools.
Chúng tôi đã minh hoạ tính năng hỗ trợ bước cơ bản và thảo luận về các cơ hội sử dụng thông tin DWARF thay vì bản đồ nguồn trong tương lai:
- Giải quyết tên biến
- Các loại in đẹp
- Đánh giá biểu thức bằng ngôn ngữ nguồn
- …và nhiều tính năng khác!
Hôm nay, chúng tôi rất vui được giới thiệu các tính năng đã hứa hẹn và tiến trình mà nhóm Emscripten và Công cụ cho nhà phát triển Chrome đã đạt được trong năm nay, đặc biệt là đối với các ứng dụng C và C++.
Trước khi bắt đầu, xin lưu ý rằng đây vẫn là phiên bản beta của trải nghiệm mới. Bạn cần sử dụng phiên bản mới nhất của tất cả các công cụ theo rủi ro của riêng mình. Nếu gặp bất kỳ vấn đề nào, vui lòng báo cáo vấn đề đó tại https://issues.chromium.org/issues/new?noWizard=true&template=0&component=1456350.
Hãy bắt đầu với cùng một ví dụ đơn giản về C như lần trướ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);
}
Để biên dịch, chúng ta sử dụng Emscripten mới nhất và truyền cờ -g
, giống như trong bài đăng gốc, để đưa thông tin gỡ lỗi vào:
emcc -g temp.c -o temp.html
Bây giờ, chúng ta có thể phân phát trang đã tạo từ máy chủ HTTP cục bộ (ví dụ: bằng serve) và mở trang đó trong Chrome Canary mới nhất.
Lần này, chúng ta cũng cần một tiện ích trợ giúp tích hợp với Chrome DevTools và giúp tiện ích này hiểu được tất cả thông tin gỡ lỗi được mã hoá trong tệp WebAssembly. Vui lòng cài đặt tiện ích này bằng cách truy cập vào đường liên kết này: goo.gle/wasm-debugging-extension
Bạn cũng nên bật tính năng gỡ lỗi WebAssembly trong Thử nghiệm của Công cụ dành cho nhà phát triển. Mở Công cụ của Chrome cho nhà phát triển, nhấp vào biểu tượng bánh răng (⚙) ở góc trên cùng bên phải của ngăn Công cụ của Chrome cho nhà phát triển, chuyển đến bảng điều khiển Thử nghiệm rồi đánh dấu vào Gỡ lỗi WebAssembly: Bật tính năng hỗ trợ DWARF.
Khi bạn đóng Settings (Cài đặt), DevTools sẽ đề xuất tự tải lại để áp dụng các chế độ cài đặt. Hãy làm như vậy. Đó là toàn bộ quy trình thiết lập một lần.
Bây giờ, chúng ta có thể quay lại bảng điều khiển Sources (Nguồn), bật tuỳ chọn Pause on exceptions (Tạm dừng khi có ngoại lệ) (biểu tượng ⏸), sau đó đánh dấu vào Pause on caught exceptions (Tạm dừng khi phát hiện ngoại lệ) rồi tải lại trang. Bạn sẽ thấy Công cụ cho nhà phát triển tạm dừng khi gặp trường hợp ngoại lệ:
Theo mặc định, trình gỡ lỗi sẽ dừng ở mã keo do Emscripten tạo, nhưng ở bên phải, bạn có thể thấy chế độ xem Call Stack (Ngăn xếp lệnh gọi) đại diện cho dấu vết ngăn xếp của lỗi và có thể chuyển đến dòng C ban đầu đã gọi abort
:
Bây giờ, nếu xem trong chế độ xem Phạm vi, bạn có thể thấy tên gốc và giá trị của các biến trong mã C/C++ và không còn phải tìm hiểu ý nghĩa của các tên bị xáo trộn như $localN
cũng như mối quan hệ của các tên đó với mã nguồn mà bạn đã viết.
Điều này không chỉ áp dụng cho các giá trị gốc như số nguyên, mà còn áp dụng cho các loại kết hợp như cấu trúc, lớp, mảng, v.v.!
Hỗ trợ loại nội dung đa dạng thức
Hãy xem một ví dụ phức tạp hơn để minh hoạ những điều đó. Lần này, chúng ta sẽ vẽ một hình ảnh Mandelbrot fractal bằng mã C++ sau:
#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();
}
Bạn có thể thấy rằng ứng dụng này vẫn khá nhỏ – đó là một tệp duy nhất chứa 50 dòng mã – nhưng lần này tôi cũng sử dụng một số API bên ngoài, chẳng hạn như thư viện SDL cho đồ hoạ cũng như số phức từ thư viện chuẩn C++.
Tôi sẽ biên dịch ứng dụng này bằng cùng một cờ -g
như trên để đưa vào thông tin gỡ lỗi, đồng thời yêu cầu Emscripten cung cấp thư viện SDL2 và cho phép bộ nhớ có kích thước tuỳ ý:
emcc -g mandelbrot.cc -o mandelbrot.html \ -s USE_SDL=2 \ -s ALLOW_MEMORY_GROWTH=1
Khi truy cập vào trang được tạo trong trình duyệt, tôi có thể thấy hình dạng fractal tuyệt đẹp với một số màu ngẫu nhiên:
Khi mở lại DevTools, tôi có thể thấy tệp C++ ban đầu. Tuy nhiên, lần này chúng ta không gặp lỗi trong mã (phew!). Vì vậy, hãy đặt một số điểm ngắt ở đầu mã.
Khi chúng ta tải lại trang, trình gỡ lỗi sẽ tạm dừng ngay bên trong nguồn C++:
Chúng ta có thể thấy tất cả các biến ở bên phải, nhưng hiện tại chỉ có width
và height
được khởi tạo, vì vậy, không có nhiều điều để kiểm tra.
Hãy đặt một điểm ngắt khác bên trong vòng lặp Mandelbrot chính và tiếp tục thực thi để chuyển sang một chút.
Tại thời điểm này, palette
của chúng ta đã được lấp đầy bằng một số màu ngẫu nhiên, và chúng ta có thể mở rộng cả mảng này cũng như các cấu trúc SDL_Color
riêng lẻ và kiểm tra các thành phần của chúng để xác minh rằng mọi thứ đều ổn (ví dụ: kênh "alpha" đó luôn được đặt thành độ mờ đầy đủ). Tương tự, chúng ta có thể mở rộng và kiểm tra phần thực và phần ảo của số phức được lưu trữ trong biến center
.
Nếu muốn truy cập vào một thuộc tính lồng sâu mà khó điều hướng qua chế độ xem Phạm vi, bạn cũng có thể sử dụng tính năng đánh giá Bảng điều khiển! Tuy nhiên, xin lưu ý rằng các biểu thức C++ phức tạp hơn chưa được hỗ trợ.
Hãy tiếp tục thực thi một vài lần và chúng ta có thể thấy x
bên trong cũng đang thay đổi – bằng cách xem lại chế độ xem Phạm vi, thêm tên biến vào danh sách theo dõi, đánh giá biến đó trong bảng điều khiển hoặc bằng cách di chuột qua biến trong mã nguồn:
Từ đây, chúng ta có thể bước vào hoặc bước qua các câu lệnh C++ và quan sát cách các biến khác cũng thay đổi:
Ok, tất cả đều hoạt động tốt khi có thông tin gỡ lỗi, nhưng nếu chúng ta muốn gỡ lỗi một mã không được tạo bằng các tuỳ chọn gỡ lỗi thì sao?
Gỡ lỗi WebAssembly thô
Ví dụ: chúng tôi đã yêu cầu Emscripten cung cấp một thư viện SDL tạo sẵn cho chúng tôi, thay vì tự biên dịch thư viện đó từ nguồn, vì vậy, ít nhất hiện tại, trình gỡ lỗi không có cách nào tìm thấy các nguồn liên kết.
Hãy cùng tìm hiểu lại SDL_RenderDrawColor
:
Chúng ta quay lại trải nghiệm gỡ lỗi WebAssembly thô.
Hiện tại, việc này có vẻ hơi đáng sợ và không phải là điều mà hầu hết các nhà phát triển Web sẽ cần phải xử lý, nhưng đôi khi bạn có thể muốn gỡ lỗi một thư viện được tạo mà không có thông tin gỡ lỗi, cho dù đó là thư viện của bên thứ 3 mà bạn không có quyền kiểm soát hay vì bạn đang gặp phải một trong những lỗi chỉ xảy ra trong quá trình phát hành chính thức.
Để hỗ trợ trong những trường hợp đó, chúng tôi cũng đã cải thiện một số trải nghiệm gỡ lỗi cơ bản.
Trước hết, nếu đã từng sử dụng tính năng gỡ lỗi WebAssembly thô, bạn có thể nhận thấy rằng toàn bộ quá trình tháo rời hiện hiển thị trong một tệp duy nhất – không còn phải đoán xem mục nhập Sources (Nguồn) wasm-53834e3e/
wasm-53834e3e-7
có thể tương ứng với hàm nào nữa.
Lược đồ tạo tên mới
Chúng tôi cũng đã cải thiện tên trong chế độ xem tháo rời. Trước đây, bạn chỉ thấy các chỉ mục dạng số hoặc trong trường hợp hàm, hoàn toàn không có tên.
Bây giờ, chúng ta sẽ tạo tên tương tự như các công cụ tháo rời khác, bằng cách sử dụng gợi ý từ mục tên WebAssembly, đường dẫn nhập/xuất và cuối cùng, nếu mọi thứ đều không thành công, hãy tạo tên dựa trên loại và chỉ mục của mục như $func123
. Bạn có thể thấy trong ảnh chụp màn hình ở trên, việc này đã giúp bạn đọc được một chút dấu vết ngăn xếp và tháo rời.
Khi không có thông tin về loại, bạn có thể khó kiểm tra bất kỳ giá trị nào ngoài các giá trị gốc – ví dụ: con trỏ sẽ xuất hiện dưới dạng số nguyên thông thường, không có cách nào để biết nội dung được lưu trữ đằng sau các con trỏ đó trong bộ nhớ.
Kiểm tra bộ nhớ
Trước đây, bạn chỉ có thể mở rộng đối tượng bộ nhớ WebAssembly, được biểu thị bằng env.memory
trong chế độ xem Phạm vi để tra cứu từng byte. Phương thức này hoạt động trong một số trường hợp không quan trọng, nhưng không đặc biệt thuận tiện để mở rộng và không cho phép diễn giải lại dữ liệu ở định dạng khác với giá trị byte. Chúng tôi cũng đã thêm một tính năng mới để giúp giải quyết vấn đề này: trình kiểm tra bộ nhớ tuyến tính.
Nếu nhấp chuột phải vào env.memory
, bạn sẽ thấy một tuỳ chọn mới có tên là Inspect memory (Kiểm tra bộ nhớ):
Sau khi nhấp vào, một Trình kiểm tra bộ nhớ sẽ xuất hiện, trong đó bạn có thể kiểm tra bộ nhớ WebAssembly ở chế độ xem thập lục phân và ASCII, chuyển đến các địa chỉ cụ thể cũng như diễn giải dữ liệu ở nhiều định dạng:
Các trường hợp nâng cao và lưu ý
Phân tích tài nguyên mã WebAssembly
Khi bạn mở Công cụ cho nhà phát triển, mã WebAssembly sẽ được "hạ cấp" xuống phiên bản chưa được tối ưu hoá để bật tính năng gỡ lỗi. Phiên bản này chậm hơn nhiều, nghĩa là bạn không thể dựa vào console.time
, performance.now
và các phương thức khác để đo tốc độ mã của mình khi DevTools đang mở, vì các con số bạn nhận được sẽ không thể hiện hiệu suất thực tế.
Thay vào đó, bạn nên sử dụng Bảng điều khiển hiệu suất của Công cụ cho nhà phát triển. Bảng điều khiển này sẽ chạy mã ở tốc độ tối đa và cung cấp cho bạn thông tin chi tiết về thời gian dành cho các hàm khác nhau:
Ngoài ra, bạn có thể chạy ứng dụng khi DevTools đang đóng và mở các công cụ đó sau khi hoàn tất để kiểm tra Bảng điều khiển.
Chúng tôi sẽ cải thiện các tình huống phân tích tài nguyên trong tương lai, nhưng hiện tại, bạn cần lưu ý điều này. Nếu bạn muốn tìm hiểu thêm về các tình huống phân cấp WebAssembly, hãy xem tài liệu của chúng tôi về quy trình biên dịch WebAssembly.
Tạo bản dựng và gỡ lỗi trên nhiều máy (bao gồm cả Docker / máy chủ)
Khi tạo bản dựng trong Docker, máy ảo hoặc trên máy chủ bản dựng từ xa, bạn có thể gặp phải trường hợp đường dẫn đến tệp nguồn được sử dụng trong bản dựng không khớp với đường dẫn trên hệ thống tệp của riêng bạn nơi Chrome DevTools đang chạy. Trong trường hợp này, các tệp sẽ xuất hiện trong bảng điều khiển Sources (Nguồn) nhưng không tải được.
Để khắc phục vấn đề này, chúng tôi đã triển khai chức năng liên kết đường dẫn trong các tuỳ chọn tiện ích C/C++. Bạn có thể sử dụng công cụ này để ánh xạ lại các đường dẫn tuỳ ý và giúp DevTools xác định vị trí nguồn.
Ví dụ: nếu dự án trên máy chủ lưu trữ của bạn nằm trong đường dẫn C:\src\my_project
, nhưng được tạo bên trong vùng chứa Docker, trong đó đường dẫn đó được biểu thị là /mnt/c/src/my_project
, thì bạn có thể ánh xạ lại đường dẫn đó trong quá trình gỡ lỗi bằng cách chỉ định các đường dẫn đó làm tiền tố:
Tiền tố khớp đầu tiên "wins". Nếu bạn đã quen thuộc với các trình gỡ lỗi C++ khác, tuỳ chọn này tương tự như lệnh set substitute-path
trong GDB hoặc chế độ cài đặt target.source-map
trong LLDB.
Gỡ lỗi bản dựng được tối ưu hoá
Giống như mọi ngôn ngữ khác, việc gỡ lỗi sẽ hoạt động hiệu quả nhất nếu bạn tắt tính năng tối ưu hoá. Các hoạt động tối ưu hoá có thể đưa các hàm vào cùng một dòng, sắp xếp lại mã hoặc xoá hoàn toàn một số phần của mã. Tất cả những điều này có thể gây nhầm lẫn cho trình gỡ lỗi và do đó, gây khó khăn cho bạn khi sử dụng.
Nếu bạn không ngại trải nghiệm gỡ lỗi bị hạn chế hơn và vẫn muốn gỡ lỗi một bản dựng được tối ưu hoá, thì hầu hết các tính năng tối ưu hoá sẽ hoạt động như dự kiến, ngoại trừ tính năng nội tuyến hàm. Chúng tôi dự định giải quyết các vấn đề còn lại trong tương lai, nhưng hiện tại, vui lòng sử dụng -fno-inline
để tắt tính năng này khi biên dịch với bất kỳ tính năng tối ưu hoá cấp -O
nào, ví dụ:
emcc -g temp.c -o temp.html \ -O3 -fno-inline
Tách thông tin gỡ lỗi
Thông tin gỡ lỗi lưu giữ nhiều thông tin chi tiết về mã, các loại, biến, hàm, phạm vi và vị trí đã xác định – mọi thứ có thể hữu ích cho trình gỡ lỗi. Do đó, tệp này thường có thể lớn hơn bản thân mã.
Để tăng tốc độ tải và biên dịch mô-đun WebAssembly, bạn nên tách thông tin gỡ lỗi này thành một tệp WebAssembly riêng. Để thực hiện việc đó trong Emscripten, hãy truyền cờ -gseparate-dwarf=…
với tên tệp mong muốn:
emcc -g temp.c -o temp.html \ -gseparate-dwarf=temp.debug.wasm
Trong trường hợp này, ứng dụng chính sẽ chỉ lưu trữ tên tệp temp.debug.wasm
và tiện ích trợ giúp sẽ có thể định vị và tải tên tệp đó khi bạn mở DevTools.
Khi kết hợp với các tính năng tối ưu hoá như mô tả ở trên, tính năng này thậm chí có thể được dùng để phân phối các bản dựng sản xuất gần như được tối ưu hoá của ứng dụng, sau đó gỡ lỗi các bản dựng đó bằng tệp phía máy. Trong trường hợp này, chúng ta cũng cần ghi đè URL đã lưu để giúp tiện ích tìm thấy tệp phụ, ví dụ:
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]
Tiếp tục…
Ôi, có rất nhiều tính năng mới!
Với tất cả các tính năng tích hợp mới đó, Công cụ của Chrome cho nhà phát triển trở thành một trình gỡ lỗi hiệu quả, mạnh mẽ không chỉ cho JavaScript mà còn cho các ứng dụng C và C++, giúp bạn dễ dàng hơn bao giờ hết trong việc đưa các ứng dụng được xây dựng bằng nhiều công nghệ lên một Web dùng chung, đa nền tảng.
Tuy nhiên, hành trình của chúng ta vẫn chưa kết thúc. Sau đây là một số việc chúng tôi sẽ làm từ giờ trở đi:
- Sửa các lỗi còn tồn tại trong trải nghiệm gỡ lỗi.
- Thêm tính năng hỗ trợ cho các trình định dạng loại tuỳ chỉnh.
- Đang cải thiện tính năng phân tích tài nguyên cho các ứng dụng WebAssembly.
- Thêm tính năng hỗ trợ mức độ sử dụng mã để dễ dàng tìm thấy mã không dùng đến.
- Cải thiện khả năng hỗ trợ biểu thức trong quá trình đánh giá bảng điều khiển.
- Hỗ trợ thêm nhiều ngôn ngữ.
- …và nhiều hình thức khác!
Trong thời gian chờ đợi, vui lòng giúp chúng tôi bằng cách dùng thử phiên bản beta hiện tại trên mã của riêng bạn và báo cáo mọi vấn đề phát hiện được cho https://issues.chromium.org/issues/new?noWizard=true&template=0&component=1456350.
Tải các 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 cho phép bạn sử dụng các tính năng mới nhất của DevTools, kiểm thử các API nền tảng web tiên tiến và giúp bạn tìm thấy vấn đề trên trang web của mình trước khi người dùng phát hiện ra!
Liên hệ với nhóm Công cụ của Chrome cho nhà phát triển
Hãy sử dụng các lựa chọn sau để thảo luận về các tính năng, bản cập nhật mới hoặc bất kỳ nội dung nào khác liên quan đến Công cụ cho nhà phát triển.
- Gửi ý kiến phản hồi và yêu cầu về tính năng cho chúng tôi tại 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 biểu tượng Tuỳ chọn khác > Trợ giúp > Báo cáo sự cố của Công cụ cho nhà phát triển trong Công cụ cho nhà phát triển.
- Gửi tweet đến @ChromeDevTools.
- Để lại bình luận trên video YouTube về tính năng mới trong DevTools hoặc video YouTube về mẹo sử dụng DevTools.