Công Cụ Kiểm Tra Rò Rỉ Bộ Nhớ Lua - nói dối e blog

Công Cụ Kiểm Tra Rò Rỉ Bộ Nhớ Lua

Hôm qua, chúng tôi phát hiện một vấn đề nghiêm trọng: lượng RAM của máy chủ nightly build tăng đột biến đến 8GB chỉ sau một đêm. Rõ ràng đây là một trường hợp rò rỉ bộ nhớ điển hình.

Trước đây, chúng tôi đã xây dựng sẵn nhiều giao thức gỡ lỗi trong hệ thống skynet, nhờ đó có thể nhanh chóng xác định được dịch vụ gặp lỗi - chính là trạng thái Lua của một bản đồ cụ thể. Bằng chứng cho thấy trong quá trình triển khai logic bản đồ bằng Lua, có những đối tượng đang được tạo ra liên tục. Dù tốc độ tạo không cao, nhưng do không có cơ chế giải phóng tham chiếu, lượng bộ nhớ cứ tăng dần theo thời gian.

Trên thực tế, đã có nhiều người phát triển công cụ phân tích bộ nhớ Lua trước đây. Tuy nhiên, vì tính lười biếng (cười), tôi quyết định tự xây dựng một giải pháp trong vòng nửa ngày (hiện đã open-source trên GitHub).

Nguyên lý hoạt động của công cụ này như sau:
Thư viện có tên “snapshot” này cung cấp duy nhất một hàm để chụp toàn bộ trạng thái hiện hành của Lua. Tuy nhiên, khác với việc thực hiện serialize toàn bộ trạng thái (công việc phức tạp hơn nhiều), để tối ưu dữ liệu phân tích, tôi chỉ tập trung vào mối quan hệ tham chiếu giữa các đối tượng phức tạp. Cụ thể, công cụ sẽ ghi lại mạng lưới tham chiếu giữa các table, thread, userdata và function.

Khi thực hiện chụp hai snapshot tại hai thời điểm khác nhau, việc so sánh sẽ giúp xác định chính xác các đối tượng mới được thêm vào, từ đó phát hiện nguyên nhân gây rò rỉ.

Ban đầu tôi dự định viết công cụ này bằng chính Lua - điều hoàn toàn khả thi. Tuy nhiên, việc thực thi mã Lua sẽ ảnh hưởng trực tiếp đến trạng thái hệ thống (hiệu ứng “người quan sát làm thay đổi đối tượng quan sát”). Do đó, tôi quyết định dùng C để trực tiếp gọi các API của Lua, phương pháp có ảnh hưởng tối thiểu đến trạng thái đang được theo dõi.

Dữ liệu quan hệ tham chiếu thu được có độ chi tiết cao, nhưng để giảm thiểu tác động đến trạng thái gốc, tôi đã tối ưu bằng cách gộp thông tin thành chuỗi ký tự lưu trữ trong bảng kết quả. Bảng trả về từ snapshot chứa các khóa là địa chỉ con trỏ (lightuserdata) của từng đối tượng Lua, giá trị tương ứng là chuỗi mô tả chi tiết sơ đồ tham chiếu của đối tượng đó.

Ví dụ, khi chạy đoạn mã dump.lua, kết quả sẽ có dạng:

1
2
3
4
userdata: 0052E760   table  
00521810 : tmp : dump.lua:7  
userdata: 0052EB98   table  
00521810 : S1 : dump.lua:7  

Thông tin này cho thấy giữa hai lần chụp snapshot, hai bảng mới đã được tạo ra. Chúng bị giữ tham chiếu bởi hai biến tmp và S1 trong hàm chạy tại dòng 7 của file dump.lua.

Báo cáo từ snapshot có cấu trúc chưa tối ưu cho việc đọc bằng mắt thường, nhưng hoàn toàn có thể phát triển thêm phân tích sâu hơn bằng mã nguồn. Ví dụ, xây dựng sơ đồ phân cấp tham chiếu giữa các bảng để trình bày trực quan hơn. Tuy nhiên, vì đây chỉ là công cụ tạm thời, nó đã giúp chúng tôi nhanh chóng xác định được nguyên nhân rò rỉ bộ nhớ từ hôm qua, nên tôi chưa có kế hoạch cải tiến sâu hơn trong thời gian tới.

0%