- Trình giả lập terminal Ghostty được phát hiện có lỗi rò rỉ nghiêm trọng khiến nó chiếm dụng hàng chục GB bộ nhớ khi chạy trong thời gian dài
- Nguyên nhân là do trong logic tái sử dụng các trang bộ nhớ không theo chuẩn của cấu trúc
PageList, munmap không được gọi nên bộ nhớ không được giải phóng bị tích lũy dần
- Claude Code CLI thường xuyên tạo đầu ra đồ thị đa code point, làm tăng tần suất sử dụng các trang không theo chuẩn, khiến lỗi rò rỉ bộc lộ ở quy mô lớn
- Bản sửa đổi đã thay đổi để không tái sử dụng các trang không theo chuẩn mà giải phóng chúng ngay lập tức, đồng thời tận dụng tính năng VM tag của macOS để theo dõi và xác minh lỗi rò rỉ
- Bản sửa này được đánh giá là đã giải quyết vấn đề rò rỉ lớn nhất của Ghostty và dự kiến sẽ được đưa vào bản phát hành sắp tới (1.3)
Tổng quan về lỗi rò rỉ bộ nhớ của Ghostty
- Một số người dùng đã báo cáo trường hợp Ghostty sử dụng hơn 37GB bộ nhớ sau khi chạy trong thời gian dài
- Lỗi rò rỉ này tồn tại ít nhất từ phiên bản 1.0, và gần đây các ứng dụng CLI đã thỏa mãn một số điều kiện nhất định để làm lộ vấn đề
- Bản sửa đã được gộp vào GitHub và dự kiến sẽ có trong bản dựng nightly cũng như bản phát hành chính thức 1.3
Cấu trúc PageList và cách quản lý bộ nhớ
- Ghostty sử dụng một cấu trúc danh sách liên kết đôi tên là PageList để lưu nội dung terminal
- Mỗi trang chứa dữ liệu như ký tự, kiểu dáng, siêu liên kết, v.v.
- Các trang được cấp phát bằng
mmap và được tái sử dụng thông qua pool các trang kích thước chuẩn
- Các trang có kích thước không vượt quá chuẩn sẽ được trả về pool
- Các trang có kích thước không chuẩn phải được giải phóng trực tiếp bằng
munmap
- Bản thân cấu trúc này là bình thường, nhưng lỗi trong logic tối ưu hóa đã gây ra rò rỉ
Tối ưu hóa scrollback và nguyên nhân phát sinh lỗi
- Khi vượt quá
scrollback-limit, Ghostty thực hiện tối ưu hóa tái sử dụng trang cũ nhất
- Chỉ điều chỉnh con trỏ thay vì cấp phát trang mới để cải thiện hiệu năng
- Vấn đề là trong quá trình này, nó chỉ đổi metadata của trang không chuẩn thành kích thước chuẩn trong khi vùng nhớ thực tế vẫn giữ nguyên
- Sau đó khi giải phóng, trang bị nhầm là trang chuẩn nên
munmap không được gọi
- Kết quả là các trang không chuẩn không được giải phóng mà tiếp tục tích lũy, gây ra rò rỉ bộ nhớ lớn khi chạy lâu dài
Claude Code và việc lỗi rò rỉ bộc lộ trên diện rộng
- Claude Code CLI thường xuyên tạo đầu ra đồ thị đa code point, làm tăng tần suất sử dụng các trang không chuẩn
- Ngoài ra, lượng đầu ra scrollback lớn khiến lỗi rò rỉ tích lũy nhanh hơn
- Theo thiết kế của Ghostty, các trang không chuẩn lẽ ra phải hiếm khi xuất hiện, nhưng do đặc tính của Claude Code nên lỗi rò rỉ được tái hiện với số lượng lớn
- Nhà phát triển nhấn mạnh rằng đây không phải lỗi của Claude Code mà là khiếm khuyết trong logic nội bộ của Ghostty
Nội dung bản sửa
Theo dõi lỗi rò rỉ bằng VM tag
- Sử dụng tính năng VM tag của nhân Mach trên macOS để gắn thẻ cụ thể cho phần cấp phát bộ nhớ của PageList
- Khi debug có thể nhận diện rõ vùng bộ nhớ của Ghostty
- Rất hữu ích trong việc lần theo nguyên nhân rò rỉ và xác minh bản sửa
- Nhờ tính năng này có thể xác nhận trực quan việc bộ nhớ liên quan đến PageList có được giải phóng hay không
Cơ chế phòng chống rò rỉ bộ nhớ của Ghostty
- Ghostty phát hiện và ngăn ngừa rò rỉ theo nhiều cách khác nhau
- Dùng allocator phát hiện rò rỉ của Zig trong bản dựng debug và unit test
- Chạy toàn bộ bài kiểm thử bằng valgrind trong CI
- Kiểm tra rò rỉ của mã Swift bằng macOS Instruments
- Các PR liên quan đến GTK được xác minh bằng Valgrind GUI test
- Lỗi rò rỉ lần này chỉ xảy ra trong những điều kiện cụ thể nên các bài kiểm thử hiện có không tái hiện được
- Một test case mới đã được thêm vào để ngăn ngừa hồi quy
Kết luận
- Đây được xác nhận là trường hợp rò rỉ bộ nhớ lớn nhất từng được ghi nhận trong Ghostty
- Sau khi sửa, vấn đề vẫn sẽ tiếp tục được theo dõi thông qua báo cáo từ người dùng và các bài kiểm thử tái hiện
- Dữ liệu chẩn đoán và các trường hợp tái hiện do cộng đồng cung cấp đã đóng vai trò quyết định trong việc giải quyết vấn đề
- Bài học được nhấn mạnh là việc có một môi trường có thể tái hiện được là chìa khóa để xử lý lỗi rò rỉ bộ nhớ
1 bình luận
Ý kiến trên Hacker News
Thật sự là một tin rất đáng mừng. Xin gửi lời tán dương tới tất cả những người đã tham gia giải quyết vấn đề này
Đây là lỗi đã được nhắc đến từ tuần trước trong chuỗi này
Có vẻ Claude Code là chất xúc tác khiến lỗi này lộ ra với nhiều người dùng hơn, nhưng cũng có những người như tôi chưa từng dùng Claude Code mà vẫn gặp đúng vấn đề đó
Tiêu chí để một trang bị phân loại là “không tiêu chuẩn (non-standard)” hóa ra không hề kiểu trắng đen rõ ràng như tôi nghĩ
Tôi cũng nghĩ rằng với những người dùng cấu hình như
scrollback-limit = 0, tình trạng rò rỉ có thể đã xảy ra thường xuyên hơnCách sửa hiện tại có thể sẽ xóa rồi tạo lại các trang không tiêu chuẩn một cách không cần thiết, nên tôi vẫn hơi tiếc là liệu có thể tái sử dụng các trang cũ vốn đã không tiêu chuẩn hay không
Cách PageList hoạt động từ trước đến nay vẫn vậy, và ngay cả khi có lỗi thì nó cũng chỉ nhìn nhầm kích thước trong lúc điều chỉnh dung lượng thôi
Sẽ không có thay đổi nào có thể cảm nhận được về hiệu năng
Phương án thay thế bạn đề xuất cũng đã được cân nhắc, nhưng cách tiếp cận hiện tại đã được dữ liệu benchmark hậu thuẫn đủ mạnh
Tôi vẫn có thể đổi ý, nhưng lần này tôi tập trung vào sửa rò rỉ hơn là thay đổi cả thế giới quan
Đây thực sự là một lỗi có thể tái hiện và gây ra segfault
Nó khiến CLI trông mới mẻ hơn bất cứ thứ gì tôi thấy trong 20 năm qua
Bài viết rất tuyệt. Cảm ơn mitchellh vì đã tạo ra Ghostty
Tôi đã chuyển sang dùng từ năm ngoái và chưa từng hối hận
Chỉ hơi bất ngờ là bản sửa sẽ được đưa vào một bản phát hành tính năng sau vài tháng
Tôi cứ nghĩ nó sẽ nằm trong một bản phát hành sửa lỗi
Ngay khi thấy nhắc đến các trang, tôi đã nghĩ “à, chắc là memory pooling”, rồi lại nghĩ “chắc là ring buffer”, và đúng là chuyện tái sử dụng scrollback
Tôi cũng đoán ngay được lỗi nằm ở đâu — phần không giải phóng bộ nhớ của trang đúng cách
Sơ đồ căn chỉnh bộ nhớ cũng rất đẹp
Điều này lại nhắc tôi rằng mỗi lần thử một cách làm mới thì khả năng rò rỉ lại xuất hiện
Tuần này tôi mới chuyển sang Ghostty, và đã gặp OOM crash khi phát triển một ứng dụng UI cho terminal
Cấu trúc của tôi dùng biểu tượng UTF8 trong thanh tab, và ứng dụng bị crash ngay khi thay đổi kích thước terminal
Việc tái hiện rất dễ nên tôi đang chuẩn bị báo cáo lỗi, nhưng nó trông rất giống với vấn đề được giải thích trong bài blog
Hy vọng chuyện này sẽ được giải quyết
Tôi đã hỏi @mitchellh — anh ấy dùng công cụ gì để làm phần trực quan hóa bộ nhớ, và website hoạt động tốt cả trên di động nên tôi tò mò về stack được dùng
Mã cho phần trực quan hóa chỉ là đồ dùng một lần nên tôi chỉ kiểm tra độ chính xác chứ không đặt nặng chất lượng
Tôi tách namespace theo từng bài blog và không tái sử dụng
Tôi chỉ xác nhận là phần triển khai không làm trò kỳ quặc nào (ví dụ: đào Bitcoin, rò rỉ bí mật, v.v.)
Điều cốt lõi là truyền đạt thông tin, và những sơ đồ như thế này giúp nội dung dễ hiểu hơn nhiều
Tôi vẫn đang tiếp tục theo dõi quá trình phát triển của Ghostty
Có hơi cảm giác overengineering một chút, nhưng những bài hậu kiểm lỗi như thế này là tư liệu cực kỳ giá trị với những ai yêu sự thủ công tinh xảo
Tôi tò mò nếu là terminal viết bằng Rust thì người ta sẽ xử lý kiểu triển khai này thế nào mà không bị tổn thất hiệu năng
Đây là một bài viết dễ hiểu ngay cả với người không rành về Ghostty hay terminal emulator
Tôi rất ấn tượng với cách giải thích thân thiện và dễ tiếp cận
Điều này lại làm tôi cảm nhận rõ tầm quan trọng của một báo cáo lỗi có thể tái hiện được
Tôi đang chờ ai đó nói rằng “nếu dùng Rust thì đã không xảy ra chuyện này”
Rust không bảo đảm “an toàn trước rò rỉ bộ nhớ (leak safety)” ở cấp độ ngôn ngữ
Ngay cả mã Rust an toàn cũng có thể làm rò rỉ bộ nhớ — chỉ là đó không phải vấn đề về an toàn
Ngay trong API chuẩn cũng có những thứ cho phép rò rỉ một cách tường minh như Box::leak
Rust chỉ khiến việc tạo ra rò rỉ ngoài ý muốn trở nên khó hơn, chứ không thể ngăn chặn hoàn toàn