2014-07-24 19 views
37

Tôi đang làm việc trên trình nền Haskell để nhận và xử lý các yêu cầu JSON. Trong khi các hoạt động của daemon phức tạp, cấu trúc chính được cố tình giữ đơn giản: trạng thái bên trong của nó chỉ là một cấu trúc dữ liệu và tất cả các luồng thực hiện các hoạt động nguyên tử trên IORef này. Sau đó, có một vài chủ đề mà trên một kích hoạt có giá trị làm một cái gì đó với nó.Gỡ lỗi rò rỉ bộ nhớ không hiển thị trên hồ sơ heap

Vấn đề là daemon đang rò rỉ bộ nhớ và tôi không thể tìm ra lý do. Nó chắc chắn liên quan đến các yêu cầu: khi daemon nhận được một số yêu cầu mỗi giây, nó rò rỉ một cái gì đó như 1MB/s (theo báo cáo của các công cụ Linux). Tiêu thụ bộ nhớ tăng đều đặn. Không có yêu cầu, mức tiêu thụ bộ nhớ vẫn không thay đổi.

Điều gì khiến tôi khó hiểu rằng không điều nào trong số này hiển thị trong hồ sơ GHC. Hoặc là tôi đang thiếu một cái gì đó trong các thông số profiling, hoặc bộ nhớ được tiêu thụ bởi cái gì khác:

Run với +RTS -hc -xt -p:

screenshot of profiler output

Run với +RTS -hr -xt -p:

screenshot of profiler output

Trong quá trình chạy thử nghiệm này, daemon sau đó tiêu thụ trên 1GB. Vì vậy, dữ liệu lược tả rõ ràng không tương ứng với bộ nhớ tiêu thụ thực tế theo thứ tự độ lớn. (Tôi hiểu rằng RTS, GC và bản thân lược tả thêm vào mức tiêu thụ bộ nhớ thực, nhưng sự khác biệt này quá lớn và không tương ứng với mức tiêu thụ ngày càng tăng.)

Tôi đã cố gắng rnf tất cả dữ liệu trạng thái của daemon bên trong IORef, cũng như các yêu cầu JSON được phân tích cú pháp (để tránh các phần của chuỗi JSON được giữ lại ở đâu đó), nhưng không thành công nhiều.

Bất kỳ ý tưởng hoặc đề xuất nào được hoan nghênh.

Cập nhật: Daemon đang chạy mà không cần -threaded, vì vậy không có chủ đề cấp hệ điều hành.

Số liệu thống kê GC là gần gũi hơn với các hồ sơ đống hơn là những con số báo cáo của Linux:

Alloc Copied  Live GC GC  TOT  TOT Page Flts 
    bytes  bytes  bytes user elap user elap 
[...] 
    5476616  44504 2505736 0.00 0.00 23.21 410.03 0 0 (Gen: 0) 
35499296  41624 2603032 0.00 0.00 23.26 410.25 0 0 (Gen: 0) 
51841800  46848 2701592 0.00 0.00 23.32 410.49 0 0 (Gen: 0) 
31259144  36416 2612088 0.00 0.00 23.40 410.61 0 0 (Gen: 0) 
53433632  51976 2742664 0.00 0.00 23.49 412.05 0 0 (Gen: 0) 
48142768  50928 2784744 0.00 0.00 23.54 412.49 0 0 (Gen: 0) 
[...] 

Cập nhật 2: Tôi tìm thấy nguồn gốc của vấn đề, rò rỉ bộ nhớ là do bởi handleToFd (xem this issue cho thư viện unix). Tôi chỉ tự hỏi làm thế nào nó có thể xác định hiệu quả hơn một rò rỉ như vậy (có lẽ xảy ra trong một đoạn nước ngoài của mã).

+6

Tôi sẽ đoán bộ nhớ bị rò rỉ nằm trong các byte được phân bổ bên ngoài vùng GHC. Hồ sơ heap chỉ hiển thị những gì GHC biết về, sau khi tất cả. – Carl

+0

@Carl Và có cách nào để nắm được cách phân bổ 'ByteString' không? –

+0

Không có gì tôi biết, ngoài việc hiểu những gì đang xảy ra trong giao diện FFI của bạn. Hãy nghi ngờ bất kỳ thư viện nào bạn đang sử dụng để thực hiện kết nối mạng. – Carl

Trả lời

1

Trong khi tôi không quen thuộc với Haskell daemon bản thân, trả lời câu hỏi của bạn "làm thế nào nó muốn được có thể xác định một cách hiệu quả hơn như một sự rò rỉ", nó có thể là có thể sử dụng

valgrind --leak-check=yes haskelldaemon (tốt hơn nếu bạn biên dịch nó với thông tin debug),

OR, nếu rò rỉ xảy ra trong thư viện chia sẻ, hãy thử

LD_PRELOAD="yourlibrary.so" valgrind your-executable.