Trình báo cáo crash mới của Bun
(bun.sh)- Bun v1.1.5 bổ sung trình báo cáo crash
bun.report, cho phép truyền thông tin stack Zig/C++ chỉ bằng một URL khoảng 150 byte, không chứa thông tin cá nhân ngay cả khi xảy ra crash hoặc panic - Các trình báo cáo crash của OS và core dump hiện có gây gánh nặng lớn về debug symbol, hiệu năng, quyền riêng tư và kích thước file thực thi, nên khó áp dụng cho một công cụ CLI như Bun
- Cách tiếp cận mới chuyển các địa chỉ bị ASLR làm mất ý nghĩa thành địa chỉ tương đối theo module, rồi server khôi phục tên hàm bằng debug symbol phù hợp với commit SHA và nền tảng
- URL chứa nền tảng, subcommand, commit SHA, feature flag, địa chỉ stack, loại crash và thông điệp; địa chỉ stack được mã hóa ngắn bằng base64 VLQ
- Không gửi mã nguồn JavaScript/TypeScript hay biến môi trường; Bun team chỉ truyền thông tin stack Zig/C++ và một số metadata cần thiết cho chẩn đoán
Vì sao Bun tự xây dựng trình báo cáo crash
- Tại thời điểm viết, Bun có hơn 2.600 issue GitHub đang mở, trong đó một số issue đặc biệt khó tái hiện và debug
- Các dịch vụ báo cáo crash như Sentry phù hợp với ứng dụng và sản phẩm SaaS, nhưng nếu một công cụ CLI như Bun upload core dump thì vấn đề về quyền riêng tư, hiệu năng và kích thước file thực thi sẽ trở nên nghiêm trọng
- Bun v1.1.5 giới thiệu một định dạng nhỏ mới cho báo cáo crash Zig và C++
- Báo cáo crash nằm trong một URL khoảng 150 byte
- Không chứa thông tin cá nhân
Những điểm chưa đủ nếu chỉ dùng trình báo cáo crash của OS
- Một số hệ điều hành như macOS có trình báo cáo crash tích hợp, nhưng để tận dụng đúng cách thường phải phân phối debug symbol cùng với ứng dụng
- Debug symbol làm tăng đáng kể kích thước bản phân phối của Bun
- Debug symbol trên Linux khoảng 30MB
- Debug symbol trên macOS khoảng 9MB
- File
.pdbtrên Windows hơn 250MB
- Ví dụ file thực thi Bun giảm từ
60Mxuống51Msau khi chạyllvm-strip - Nếu crash xảy ra khi không có debug symbol, stack trace chỉ còn
???và địa chỉ, nên ít hữu ích - Do ASLR(Address space layout randomization), địa chỉ hàm bị trộn thêm offset ngẫu nhiên, nên không thể khôi phục tên hàm trực tiếp từ đó
Cách bun.report hoạt động
- Khi xảy ra crash hoặc panic trong Bun v1.1.5, Bun in ra liên kết
bun.reportcùng với phiên bản, nền tảng, tham số chạy, mức sử dụng bộ nhớ và thông điệp crash - Khi người dùng mở liên kết, họ được chuyển hướng đến biểu mẫu GitHub issue đã được điền sẵn
- Bên trong URL có mã hóa stack trace đã được remap
- Server khôi phục địa chỉ stack dựa trên thông tin trong URL và chuyển đổi thành báo cáo crash mà Bun team có thể đọc được
Quy trình chuyển địa chỉ thành stack trace có thể đọc được
- Địa chỉ hàm là con trỏ tới vị trí mã ứng dụng được nạp vào bộ nhớ, và vì lý do bảo mật nên có offset ngẫu nhiên
- Ý tưởng cơ bản là lấy địa chỉ thô trừ đi địa chỉ cơ sở (base address) của binary để thu được địa chỉ tương đối
- Triển khai thực tế phức tạp hơn do khác biệt API giữa các nền tảng
- Windows dùng flag
GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESSvớiGetModuleHandleExW, rồi lấy con trỏ module làm địa chỉ cơ sở - Linux duyệt các module đã nạp bằng
dl_iterate_phdr, và dùngdl_phdr_info.dlpi_addrcủa module chứa địa chỉ làm địa chỉ cơ sở - macOS duyệt module bằng
_dyld_image_count,_dyld_get_image_header, rồi lấy ASLR slide bằng_dyld_get_image_vmaddr_slide- Địa chỉ kết quả trên macOS vẫn còn offset image; với Bun là
0x100000000 - Để rút ngắn URL, offset này được loại bỏ, nhưng phải cộng lại trước khi remap bằng
llvm-symbolizer
- Địa chỉ kết quả trên macOS vẫn còn offset image; với Bun là
- Windows dùng flag
- Trên Linux và macOS, module đầu tiên trỏ tới binary ứng dụng chính
- Trên Windows, có thể xác định binary chính bằng cách so sánh tên module với
peb.ProcessParameters.ImagePathName - Bun không tải và phân tích debug symbol cục bộ, mà giao việc demangle cho server
- Server có thể cache debug symbol
- Có thể demangle stack trace trong vài giây
- Đồng thời đóng vai trò liên kết mở GitHub issue mới
Cấu trúc URL bun.report
- URL
bun.reportmã hóa các thông tin sau- Platform: một ký tự biểu thị nền tảng. Ví dụ
wlà x86_64 Windows,Mlà aarch64 macOS - Subcommand: một ký tự biểu thị subcommand như
bun test,bun install,bun run - Commit SHA: commit SHA của phiên bản Bun hiện tại, dùng để lấy debug symbol sau này
- Feature Flags: các dấu hiệu cho biết API và tính năng đã được dùng trước khi crash
- Stack Trace Addresses: các địa chỉ đã tính ở bước trước
- Crash Type: một ký tự biểu thị loại crash
- Crash Message: thông điệp có định dạng khác nhau tùy theo loại crash
- Platform: một ký tự biểu thị nền tảng. Ví dụ
- Số phiên bản trong URL chủ yếu là để con người đọc, hơn là để xử lý thực tế
- Chỉ riêng các thông tin này cũng có thể giúp nhận diện thủ công một số đặc điểm của crash
- Nhìn định danh
wcó thể nhanh chóng biết đây là crash trên Windows - Nhìn
A2ở cuối chuỗi có thể nhận diện lỗi segmentation fault
- Nhìn định danh
Mã hóa VLQ để có URL ngắn
- Địa chỉ stack trace được mã hóa thành số base64 Variable Length Quantity(VLQ) để giữ URL ngắn
- VLQ biểu diễn số nhỏ bằng ít ký tự hơn, đồng thời vẫn có thể mã hóa số lớn
- Kỹ thuật tương tự cũng được dùng khi lưu số dòng trong source map JavaScript
- Server giải mã các giá trị VLQ trở lại địa chỉ tương đối, dùng commit hash và nền tảng để tải debug symbol, rồi demangle tên hàm bằng
llvm-symbolizer - Trong ví dụ crash, có thể thấy một assertion đã thất bại trong
dirInfoCachedMaybeLog, một phần của mã resolver module trên Windows
Mã hóa feature flag
- URL cũng mã hóa số nguyên 64-bit, trong đó mỗi bit tương ứng với việc một tính năng cụ thể của Bun có được sử dụng hay không
- Các flag này đưa ra gợi ý về những API và hệ thống có thể đã ảnh hưởng đến crash
- Nếu file
.envđược tự động nạp, tính năngdotenvđược bật - Nếu dùng
fetch(), tính năngfetchđược bật
- Nếu file
- Bun theo dõi việc sử dụng tính năng bằng một container biến toàn cục, và bên trong mỗi API sẽ tăng con số tương ứng để đánh dấu việc sử dụng
- Bằng metaprogramming tại compile time của Zig, Bun duyệt danh sách tính năng và tự động tạo một packed struct dùng 1 bit cho mỗi tính năng
- Dùng
inline forcho phép duyệt danh sách tính năng tại compile time, trong khi thao tác đặt bit thực tế được thực hiện ở runtime - Khi thêm tính năng mới vào struct
Featureshiện có, trình báo cáo crash cũng xử lý được mà không cần viết lặp lại - Cách tương tự cũng có thể thực hiện bằng macro C hoặc Rust, nhưng trong triển khai của Bun,
comptimecủa Zig được dùng theo cách đơn giản và dễ đọc hơn
Khác biệt so với core dump
- Core dump chứa nhiều thông tin hơn rất nhiều, nhưng có kích thước lớn, chỉ hữu ích khi có debug symbol, và có thể chứa nhiều thông tin nhạy cảm hoặc bí mật
- Cách báo cáo mới của Bun tránh tình huống gửi mã nguồn JavaScript/TypeScript, biến môi trường hoặc các thông tin nhạy cảm khác
- Thay vì mặc định gửi mọi thứ, nó chỉ gửi stack trace Zig/C++ và một vài chi tiết có khả năng cao cần thiết cho việc chẩn đoán
- Nếu cần thêm thông tin, có thể yêu cầu riêng từ người dùng
- So với tình huống trước đây chỉ còn các địa chỉ chưa được mapping, Bun team sẽ dễ chẩn đoán crash hơn
Demo
- Một web app nhỏ để kiểm thử trình báo cáo crash được cung cấp tại bun.report
- Với bất kỳ URL báo cáo crash nào, chỉ cần thêm
/viewvào cuối là sẽ chuyển đến màn hình web app đó
1 bình luận
Ý kiến trên Hacker News
Nếu lý do dùng cách này thay cho stack trace thông thường là để tránh phân phối debug symbol nặng vài MB, thì có vẻ họ đã bỏ qua một lựa chọn tốt hơn: chỉ đưa tên hàm vào debug table
Đây là cách tốt hơn nhiều so với việc phải dùng một dịch vụ web để xem stack trace, và không chỉ là lý thuyết mà đã được triển khai trong LLVM: https://clang.llvm.org/docs/UsersManual.html#cmdoption-gline...
Nếu chỉ cần một URL để gần như tự động điền các nội dung cần thiết thì việc đó sẽ đủ dễ, và như vậy các nhà phát triển mới thực sự gửi báo cáo crash. Kích thước cũng quan trọng vì họ muốn tránh tạo bất lợi cho người dùng, nhưng trọng tâm là làm cho toàn bộ quy trình thật dễ dàng
Trong kịch bản sử dụng này, việc phải xem stack trace bằng dịch vụ web không phải là nhược điểm lớn. Nó gần giống với việc làm rối/rút gọn bundle JavaScript frontend, tải source map lên Sentry, rồi dùng Sentry để khôi phục stack trace gửi từ trình duyệt của người dùng. Người dùng dù sao cũng chẳng xem stack trace đó, còn tôi thì không thấy bất tiện khi xem qua Sentry. Nếu không có nó thì thậm chí tôi đã không xem được gì
Có nhiều cách để đề xuất phương án thay thế
Trên các nền tảng này, đó là cách symbolication tìm được tên hàm của các thư viện hệ thống mà không cần toàn bộ debug symbol
Xuất sắc và rất sáng tạo. Sẽ rất hay nếu nhiều dự án làm theo cách này. Điểm cốt lõi là lưu stack trace bằng program counter tương đối theo executable/shared object
Theo tôi biết thì Bun được liên kết tĩnh, nhưng nếu là hệ thống liên kết động thì có lẽ cần thêm một ID shared object dạng số nhỏ trước mỗi program counter đã chuẩn hóa
Ví dụ Unreal Engine crash reporter đã có thể gửi định dạng đơn giản như thế này từ nhiều năm trước, và có thể khôi phục tên hàm/số dòng khá chính xác cho từng stack frame. Tuy nhiên thường người ta vẫn thích minidump hơn, vì nếu có cả biến trên stack thì có thể có thêm gợi ý về chuyện đã xảy ra
Microsoft làm những thứ như vậy thật sự rất tốt. Trong SQL Server, họ dùng minidump đã loại bỏ thông tin cá nhân; chúng rất nhỏ và cực kỳ hữu ích
Ngay cả hồi đó, cách đây 15 năm, full dump của SQL Server production cũng đã là những tệp khổng lồ đến mức khó di chuyển
Tôi đã theo dõi Bun vài năm kể từ khi thấy tweet đầu tiên liên quan đến Zig, và gần đây mới bắt đầu dùng; nó cứ thế hoạt động tốt mà không có rắc rối đáng kể nào
Bun khá hấp dẫn. Tôi đã thử trong vài dự án ví dụ nhỏ, tốc độ tốt, và tôi thích việc nó kết hợp quản lý package với JavaScript runtime
Tuy nhiên tôi đang dùng Dependabot trong hầu hết các dự án nghiêm túc. Theo tôi biết, hỗ trợ Bun của Dependabot đang được làm, hoặc ít nhất đang được thảo luận trong một số issue của repo, nên tôi đang hoãn dùng cho đến khi hỗ trợ đó được phát hành
Hoàn toàn không hối tiếc. Những phần nhanh hơn tích lũy lại tạo ra mức tiết kiệm đáng kể, và cải thiện lớn về trải nghiệm lập trình viên đúng là xứng đáng như kỳ vọng
Sẽ không nhiều người nhận ra họ đã đặt bao nhiêu tâm sức vào những thứ như thế này. Tôi thích vì nó cho thấy đội Bun quan tâm đến craft của mình đến mức nào
Bun rất ấn tượng, nhưng gần đây khi tôi thử tạo HTTP/2 server bằng Fastify thì không được
Lỗi là
node:http2 createServer is not yet implemented in Bun, và issue mà thông báo trỏ tới thực ra nói về hỗ trợ HTTP/2 client. Hỗ trợ client đã được phát hành từ v1.0.13: https://bun.sh/blog/bun-v1.0.13#http2-client-supportThông báo
NotImplementedErrornên được đổi để trỏ đến issue phía server: https://github.com/oven-sh/bun/issues/8823Hỗ trợ HTTP/2 server là một trong những yêu cầu tính năng được quan tâm hàng đầu: https://github.com/oven-sh/bun/issues?q=is%3Aissue+is%3Aopen...
Nếu tính năng này ra mắt, có vẻ sẽ có nhiều người hơn chuyển sang Bun
Bun vẫn còn ở giai đoạn quá sớm trong vòng đời. Dù vậy tôi rất kỳ vọng vào dự án này
Tôi tò mò không biết có ai thực sự dùng Bun không. Nó có tốt như kỳ vọng không?
Việc thiết lập ts-node, ts-jest, hỗ trợ ESM, top-level await, v.v. trong môi trường TypeScript Node phiền phức hơn mức cần thiết. Các bản phát hành Node gần đây đã giảm bớt một phần bất tiện, nhưng vẫn không đơn giản bằng
bun init. Tôi cũng đang dùng bun shell API khá thích thú: https://bun.sh/blog/the-bun-shellThông báo lỗi cũng tệ hơn Node khá nhiều. Tôi đã dùng một thời gian, nhưng dạo này chỉ cần gắn
—loader tsxvào Node là nó làm được mọi thứ tôi muốn và không có nhược điểm. Với server đơn giản, ví dụ dùng WebSocket và chắc chắn không cần native module, thì Bun đáng để cân nhắc. Tôi thực sự đang vận hành vài dịch vụ như vậyTốc độ khởi động tức thì vẫn khiến tôi ngạc nhiên
Vẫn còn vài thứ thiếu, nhưng với tôi nó đã tốt hơn Node
Bài viết này cũng có cảm giác là một case study về Zig rất hay. Thú vị
Bun phải tải 37 package trước khi dùng được REPL. Không có Internet thì cũng không dùng được REPL
Khi chạy
bun repl, nó báo lỗi không tải được manifest của packagebun-repl. Không phải vấn đề lớn, nhưng tôi đã kỳ vọng rằng chỉ cần đặt một executable duy nhất vào PATH là nó chạy ngay không cần cài đặt, nên cũng khá háo hứcBên trong,
bun repllàm cùng việc vớibunx bun-repl