Gỡ lỗi WebAssembly bằng các công cụ hiện đại

Ingvar Stepanyan
Ingvar Stepanyan

Quãng đường cho đến thời điểm này

Một năm trước, Chrome đã công bố hỗ trợ ban đầu để gỡ lỗi WebAssembly gốc trong Công cụ của Chrome cho nhà phát triển.

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
  • Kiểu in đẹp
  • Đánh giá biểu thức trong ngôn ngữ nguồn
  • ...và nhiều điểm cải tiến 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 trong thực tế và tiến trình của nhóm Emscripten và Công cụ của Chrome cho nhà phát triển đã đạt được 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 thử nghiệm 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ụ bạn tự chịu rủi ro và nếu bạn gặp phải bất kỳ vấn đề nào, vui lòng báo cáo 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 tệp này, chúng ta sử dụng Emscripten mới nhất và chuyển một cờ -g, giống như trong bài đăng gốc, để bao gồm gỡ lỗi của bạn:

emcc -g temp.c -o temp.html

Bây giờ chúng ta có thể phân phát trang được tạo từ một máy chủ HTTP localhost (cho ví dụ: với serve (phân phát) và mở tệp đó trong Chrome Canary mới nhất.

Lần này, chúng ta cũng sẽ cần một tiện ích trợ giúp tích hợp với Chrome Công cụ cho nhà phát triển và giúp công cụ này hiểu tất cả thông tin gỡ lỗi được mã hoá trong tệp WebAssembly. Vui lòng cài đặt bằng cách truy cập vào đường liên kết: goo.gle/wasm-debugging-extension

Bạn cũng cần bật tính năng gỡ lỗi WebAssembly trong Công cụ cho nhà phát triển Thử nghiệm. 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 () trong góc trên cùng bên phải của ngăn Công cụ cho nhà phát triển, hãy chuyển đến bảng điều khiển Thử nghiệm và đánh dấu Gỡ lỗi WebAssembly: Bật tính năng hỗ trợ DWARF.

Ngăn thử nghiệm của chế độ cài đặt Công cụ cho nhà phát triển

Khi bạn đóng phần Cài đặt, Công cụ cho nhà phát triển sẽ đề xuất tự tải lại để áp dụng các chế độ cài đặt, hãy làm việc đó. Vậy là hết rồi thiết lập.

Bây giờ, chúng ta có thể quay lại bảng điều khiển Nguồn, hãy bật tuỳ chọn Tạm dừng và bật ngoại lệ (biểu tượng ⏸), sau đó đánh dấu vào Tạm dừng khi phát hiện các trường hợp ngoại lệ và tải lại trang. Bạn sẽ thấy Công cụ cho nhà phát triển bị tạm dừng ở một trường hợp ngoại lệ:

Ảnh chụp màn hình bảng điều khiển Nguồn cho thấy cách bật chế độ &quot;Tạm dừng khi phát hiện ngoại lệ&quot;

Theo mặc định, công cụ này dừng trên mã keo do Emscripten tạo, nhưng trên Bạn có thể thấy chế độ xem Call Stack (Ngăn xếp lệnh gọi) biểu thị dấu vết ngăn xếp của lỗi và có thể điều hướng đến dòng C ban đầu đã gọi abort:

Công cụ cho nhà phát triển đã bị tạm dừng trong hàm &quot;assert_less&quot; và đang hiển thị các giá trị của &quot;x&quot; và &quot;y&quot; trong chế độ xem Phạm vi

Bây giờ, nếu xem trong chế độ xem Scope (Phạm vi), bạn có thể thấy các tên ban đầu 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 những tên bị hỏng như $localN và chúng liên quan như thế nào đến mã nguồn 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ợ nhiều định dạng

Hãy cùng xem một ví dụ phức tạp hơn để minh hoạ các chỉ số đó. Chiến dịch này chúng ta sẽ vẽ hình fractal Mandelbrot với mã C++ sau đây:

#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 ứng dụng tệp 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, như thư viện SDL cho hình ảnh cũng như số phức từ Thư viện chuẩn C++.

Tôi sẽ biên dịch mã này với cùng một cờ -g như trên để đưa vào thông tin gỡ lỗi, đồng thời tôi cũng sẽ yêu cầu Emscripten cung cấp SDL2 thư viện 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:

Trang minh hoạ

Khi mở Công cụ cho nhà phát triển, một lần nữa, tôi có thể thấy tệp C++ gốc. Chiến dịch này thời gian, tuy nhiên, chúng ta không có lỗi trong mã (phù hợp!), vì vậy hãy đặt một số điểm ngắt ở đầu mã để thay thế.

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++:

Công cụ cho nhà phát triển đã bị tạm dừng trên lệnh gọi &quot;SDL_Init&quot;

Chúng ta có thể thấy tất cả các biến ở bên phải, nhưng chỉ có widthheight hiện được khởi tạo nên không cần 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 tiếp một chút.

Công cụ cho nhà phát triển bị tạm dừng bên trong các vòng lặp lồng nhau

Lúc này, palette của chúng ta đã được tô bằng một số màu ngẫu nhiên, và chúng ta có thể mở rộng cả mảng, cũng như từng cá nhân SDL_Color cấu trúc 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 đến độ mờ hoàn toàn). Tương tự, chúng ta có thể mở rộng và kiểm tra các biến thực phần ảo của số phức được lưu trữ trong biến center.

Nếu bạn muốn truy cập vào một thuộc tính được lồng sâu mà khó có thể chuyển đến thông qua chế độ xem Phạm vi, bạn có thể sử dụng Bảng điều khiển ! Tuy nhiên, lưu ý rằng các biểu thức C++ phức tạp hơn thì không chưa được hỗ trợ.

Bảng điều khiển cho thấy kết quả của &quot;bảng điều khiển[10].r&quot;

Hãy tiếp tục thực thi một vài lần để biết x bên trong đang hoạt động như thế nào 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 xem, đánh giá tên 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:

Chú thích trên biến &quot;x&quot; trong nguồn hiển thị giá trị của biến &quot;3&quot;

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:

Chú giải công cụ và chế độ xem Phạm vi cho thấy giá trị của các biến &quot;color&quot;, &quot;point&quot; và các biến khác

Được rồi, tất cả tính năng này đề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 tính năng gỡ lỗi thì sao không?

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 cho chúng tôi, thay vì tự biên dịch từ nguồn, ít nhất hiện không có cách nào để trình gỡ lỗi tìm các nguồn có liên quan. Hãy cùng khám phá thêm một lần nữa về SDL_RenderDrawColor:

Công cụ cho nhà phát triển cho thấy khung hiển thị tháo rời của &quot;mandelbrot.wasm&quot;

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 đã thực hiện một số cải tiến đối với trải nghiệm gỡ lỗi của bạn.

Trước hết, nếu trước đó bạn đã sử dụng gỡ lỗi WebAssembly thô, bạn có thể lưu ý rằng toàn bộ quá trình tháo rời hiện được hiển thị trong một tệp-no dự đoán hàm nào mà mục Nguồn wasm-53834e3e/ wasm-53834e3e-7 có thể tương ứng với hàm nào.

Lược đồ tạo tên mới

Chúng tôi cũng đã cải thiện tên trong khung hiển thị phân tách. 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ể trong ảnh chụp màn hình ở trên, cách này đã giúp bạn dấu vết ngăn xếp và tháo rời dễ đọc hơn.

Khi không có thông tin về loại, có thể bạn sẽ gặp khó khăn khi kiểm tra bất kỳ giá trị nào ngoài dữ liệu nguyên gốc-ví dụ: con trỏ sẽ hiển thị như số nguyên thông thường mà không có cách nào biết được dữ liệu nào được lưu trữ phía sau 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 khung hiển thị Scope (Phạm vi) để tra cứu byte riêng lẻ. Cách này hiệu quả trong một số trường hợp đơn giản, nhưng không đặc biệt thuận tiện trong việc mở rộng và không cho phép diễn giải lại dữ liệu ở định dạng khác ngoài giá trị byte. Chúng tôi đã thêm một tính năng mới để trợ giúp bằng công cụ 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 có tên Kiểm tra bộ nhớ:

Trình đơn theo bối cảnh trên &quot;env.memory&quot; trong ngăn Scope hiển thị thông tin &quot;Inspect Memory&quot; (Kiểm tra bộ nhớ) mục

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:

Ngăn Trình kiểm tra bộ nhớ trong Công cụ cho nhà phát triển cho thấy khung hiển thị hex và ASCII của bộ nhớ

Tình huống nâng cao và cảnh báo

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 "giảm cấp" sang 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 rất nhiều, đồng nghĩa với việc bạn không thể sử dụng console.time, performance.now và các phương pháp khác để đo tốc độ của mã trong khi Công cụ cho nhà phát triển vì các số liệu bạn nhận được sẽ không thể hiện hiệu suất thực tế nào.

Thay vào đó, bạn nên sử dụng bảng điều khiển Hiệu suất cho Công cụ cho nhà phát triển Thao tác này sẽ chạy mã ở tốc độ tối đa và cung cấp cho bạn bảng phân tích chi tiết thời gian sử dụng các hàm khác nhau:

Bảng điều khiển phân tích tài nguyên cho thấy nhiều hàm Wasm

Ngoài ra, bạn có thể chạy ứng dụng bằng Công cụ cho nhà phát triển đã đóng và hãy mở chúng 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 lập hồ sơ trong tương lai, nhưng hiện tại, cần lưu ý. 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.

Xây dựng và gỡ lỗi trên nhiều máy (bao gồm Docker / máy chủ lưu trữ)

Khi tạo trong Docker, máy ảo hoặc trên máy chủ xây dựng từ xa, bạn có thể sẽ gặp phải trường hợp đường dẫn đến các tệp nguồn được sử dụng trong quá trình tạo không khớp với các đường dẫn trên hệ thống tệp của riêng bạn trong đó Công cụ của Chrome cho nhà phát triển đang chạy. Trong trường hợp này, 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ể dùng công cụ này để gán lại các đường dẫn tuỳ ý và giúp Công cụ cho nhà phát triển 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 một đường dẫn C:\src\my_project, nhưng được xây dựng bên trong một vùng chứa Docker nơi đường dẫn đó được biểu thị dưới dạng /mnt/c/src/my_project, nên bạn có thể gán lại nó trở lại trong quá trình gỡ lỗi bằng cách chỉ định các đường dẫn đó làm tiền tố:

Trang tuỳ chọn của tiện ích gỡ lỗi C/C++

Tiền tố khớp đầu tiên là "wins" (thắng). Nếu bạn đã quen với C++ khác các trình gỡ lỗi, 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á. Tối ưu hoá có thể cùng dòng hàm này với hàm khác, sắp xếp lại thứ tự hoặc xóa hoàn toàn các phần của mã-và tất cả thao tác này đều có cơ hội gây nhầm lẫn cho trình gỡ lỗi và do đó bạn với tư cách là người dùng.

Nếu bạn không muốn có trải nghiệm gỡ lỗi hạn chế hơn và vẫn muốn gỡ lỗi 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 dự kiến, ngoại trừ chức năng cùng dòng. Chúng tôi dự định sẽ giải quyết các trong tương lai, nhưng hiện tại, vui lòng sử dụng -fno-inline để hãy tắt chế độ đó khi biên dịch bằng 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 giữ lại nhiều chi tiết về mã của bạn, được định nghĩa loại, biến, hàm, phạm vi và vị trí – bất cứ thứ gì có thể rất hữu ích cho trình gỡ lỗi. Kết quả là, mã này thường có thể lớn hơn mã.

Để tăng tốc độ tải và biên dịch mô-đun WebAssembly, bạn có thể muốn chia nhỏ thông tin gỡ lỗi này thành một WebAssembly riêng biệt . Để thực hiện việc đó trong Emscripten, hãy truyền cờ -gseparate-dwarf=… bằng 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ể xác định và tải công cụ đó khi bạn mở Công cụ cho nhà phát triển.

Khi được kết hợp với các biện pháp tối ưu hoá như mô tả ở trên, tính năng này có thể được dùng để gửi các phiên bản sản xuất gần như được tối ưu hoá của ứng dụng của bạn và sau đó gỡ lỗi bằng tệp cục bộ. Trong trường hợp này, chúng tôi sẽ cần ghi đè URL đã lưu trữ để giúp tiện ích tìm tệp bên, 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...

Ồ, có rất nhiều tính năng mới!

Với tất cả những công cụ tích hợp mới đó, Công cụ của Chrome cho nhà phát triển trở nên khả thi, trình gỡ lỗi mạnh mẽ không chỉ dành cho JavaScript mà còn cho các ứng dụng C và C++, giúp họ sử dụng ứng dụng dễ dàng hơn bao giờ hết, được tích hợp sẵn nhiều công nghệ và đưa chúng đến với Web chia sẻ, đa nền tảng.

Tuy nhiên, hành trình của chúng ta vẫn chưa dừng lại. Một số việc chúng tôi sẽ thực hiện làm việc từ đây về:

  • Vệ sinh các cạnh thô 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.
  • Chúng tôi đang nỗ lực cải tiến phân tích tài nguyên cho ứng dụng WebAssembly.
  • Thêm tính năng hỗ trợ cho mức độ sử dụng mã để giúp người dùng dễ dàng tìm thấy mã hơn mã không sử dụng.
  • Cải thiện khả năng hỗ trợ biểu thức trong việc đánh giá bảng điều khiển.
  • Đang thêm tính năng hỗ trợ cho nhiều ngôn ngữ hơn.
  • …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 bất kỳ lỗi nào tìm được vấn đề đến https://issues.chromium.org/issues/new?noWizard=true&amp;template=0&amp;component=1456350.

Tải kênh xem trước xuống

Hãy cân nhắc việc sử dụng Chrome Canary, Nhà phát triển hoặc Beta làm trình duyệt phát triển mặc định của bạn. Các kênh xem trước này cho phép bạ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, kiểm thử các API nền tảng web tiên tiến và tìm ra các vấn đề trên trang web của bạn trước khi người dùng 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 để thảo luận về các tính năng và thay đổi mới trong bài đăng, hoặc bất cứ nội dung nào khác liên quan đến Công cụ cho nhà phát triển.

  • 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 biểu tượng Tuỳ chọn khác   Thêm   > 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.
  • Tweet tại @ChromeDevTools.
  • Để 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 Mẹo video trên YouTube trong Công cụ cho nhà phát triển.