2010-11-11 19 views
19

Vì vậy, tôi đang viết một ứng dụng sniffing gói. Về cơ bản tôi muốn nó để sniff cho phiên tcp, và sau đó phân tích chúng để xem nếu họ là http, và nếu họ đang có, và nếu họ có loại nội dung đúng, vv, lưu chúng như là một tập tin trên ổ cứng của tôi.Attoparsec phân bổ một tấn bộ nhớ trên 'take' lớn gọi

Vì vậy, để kết thúc, tôi muốn nó có hiệu quả. Vì thư viện http hiện tại là chuỗi dựa trên, và tôi sẽ xử lý các tệp lớn, và tôi chỉ thực sự cần phân tích các câu trả lời http, tôi đã quyết định cuộn thư của mình trong attoparsec.

Khi tôi hoàn thành chương trình, tôi thấy rằng khi tôi phân tích cú pháp phản hồi 9 meg http với tệp wav trong đó, khi tôi định dạng nó, nó đã phân bổ một bộ nhớ khi nó cố phân tích cơ thể của phản hồi http. Khi tôi nhìn vào HTTP.prof tôi thấy một số dòng:

 
httpBody    Main             362   1 0.0 0.0 93.8 99.3 

take     Data.Attoparsec.Internal        366  1201 0.0 0.0 93.8 99.3 
    takeWith   Data.Attoparsec.Internal        367  3603 0.0 0.0 93.8 99.3 
     demandInput  Data.Attoparsec.Internal        375   293 0.0 0.0 93.8 99.2 
     prompt   Data.Attoparsec.Internal        378   293 0.0 0.0 93.8 99.2 
     +++    Data.Attoparsec.Internal        380   586 93.8 99.2 93.8 99.2 

Như bạn có thể thấy, ở đâu đó trong vòng httpbody, lấy được gọi là 1201 lần, khiến 500 + (+++) concatenations của bytestrings, gây một lượng cấp phát bộ nhớ vô lý.

Đây là mã. N chỉ là độ dài nội dung của câu trả lời http, nếu có. Nếu không có nó chỉ là cố gắng để có tất cả mọi thứ.

Tôi muốn nó trả về giá trị 1000 hoặc nhiều ký tự, nhưng ngay cả khi tôi thay đổi nó để chỉ lấy n và trả về một cách nghiêm ngặt, nó vẫn có những phân bổ trong đó (và nó sử dụng 14 gig bộ nhớ).


httpBody n = do 
    x <- if n > 0 
    then AC.take n 
    else AC.takeWhile (\_ -> True) 
    if B.length x == 0 
    then return Nothing 
    else return (Just x) 

Tôi đã đọc blog của anh chàng đã phối hợp và anh ấy gặp vấn đề tương tự nhưng tôi chưa bao giờ nghe nói về giải pháp. Có ai từng gặp vấn đề này trước hoặc tìm ra giải pháp không?

Chỉnh sửa: Được rồi, tôi đã bỏ qua toàn bộ ngày này và không nhận được gì. Sau khi nghiên cứu vấn đề này tôi không nghĩ rằng có một cách để làm điều đó mà không cần thêm một người lười biếng bytestring accessor để attoparsec. Tôi cũng đã xem xét tất cả các thư viện khác và họ thiếu những thứ khác hoặc những thứ khác.

Vì vậy, tôi đã tìm thấy giải pháp thay thế. Nếu bạn nghĩ về một yêu cầu http, nó sẽ đi tiêu đề, dòng mới, dòng mới, nội dung. Vì cơ thể là cuối cùng, và phân tích trả về một tuple với cả những gì bạn phân tích và những gì còn lại của bytestring, tôi có thể bỏ qua phân tích cơ thể bên trong attoparsec và thay vì nhổ cơ thể thẳng offtestring đó là trái.


parseHTTPs bs = if P.length results == 0 
    then Nothing 
    else Just results 
    where results = foldParse(bs, []) 

foldParse (bs,rs) = case ACL.parse httpResponse bs of 
    ACL.Done rest r -> addBody (rest,rs) r 
    otherwise -> rs 

addBody (rest,rs) http = foldParse (rest', rs') 
    where 
    contentlength = ((read . BU.toString) (maybe "0" id (hdrContentLength (rspHeaders http)))) 
    rest' = BL.drop contentlength rest 
    rs' = rs ++ [http { rspBody = body' }] 
    body' 
     | contentlength == 0 = Just rest 
     | BL.length rest == 0 = Nothing 
     | otherwise   = Just (BL.take contentlength rest) 
httpResponse = do 
    (code, desc) <- statusLine 
    hdrs <- many header 
    endOfLine 
-- body <- httpBody ((read . BU.toString) (maybe "0" id (hdrContentLength parsedHeaders))) 

    return Response { rspCode = code, rspReason = desc, rspHeaders = parseHeaders hdrs, rspBody = undefined } 

Đó là một chút lộn xộn, nhưng cuối cùng nó hoạt động nhanh và phân bổ không có gì nhiều hơn tôi muốn. Vì vậy, về cơ bản bạn gấp qua việc thu thập các cấu trúc dữ liệu http, sau đó ở giữa các bộ sưu tập, tôi kiểm tra độ dài nội dung của cấu trúc mà tôi vừa nhận được, kéo một số tiền thích hợp từ phần còn lại, và sau đó tiếp tục nếu có bất kỳ dấu nào bên trái.

Chỉnh sửa: Tôi thực sự đã hoàn thành dự án này. Làm việc như một say mê. Tôi không được cabalized đúng cách nhưng nếu ai đó muốn xem toàn bộ nguồn, bạn có thể tìm thấy nó tại https://github.com/onmach/Audio-Sniffer.

+0

AC là gì? (filler) –

+0

Có sử dụng [hoạt động chống phân mảnh của nhà xây dựng blaze] (http://lambda-view.blogspot.com/2010/11/defragmenting-lazy-bytestrings.html) khắc phục vấn đề này không? –

+0

AC đủ điều kiện để nhập dữ liệu.Attoparsec.Char8 –

Trả lời

5

combinatorrent anh chàng ở đây :)

Nếu bộ nhớ phục vụ, các vấn đề với attoparsec là đòi hỏi đầu vào một chút tại một thời điểm, xây dựng một bytestring lười biếng mà là cuối cùng nối vào nhau. "Giải pháp" của tôi là tự cuộn chức năng đầu vào. Đó là, tôi nhận được luồng đầu vào cho attoparsec từ một ổ cắm mạng và tôi biết có bao nhiêu byte để mong đợi trong một tin nhắn.Về cơ bản, tôi chia thành hai trường hợp:

  • Thông điệp nhỏ: Đọc lên đến 4k từ các ổ cắm và ăn mà Bytestring một chút tại một thời điểm (lát bytestrings tác dụng rất nhanh và chúng tôi vứt bỏ các 4k sau nó đã cạn kiệt).

  • Thông điệp là "lớn" (lớn ở đây có nghĩa là khoảng 16 Kilobyte trong bittorrent nói): Chúng tôi tính toán bao nhiêu phần 4k chúng tôi có thể thực hiện, và sau đó chúng tôi chỉ yêu cầu ổ cắm mạng bên dưới để lấp đầy mọi thứ. bây giờ có hai bytestrings, phần còn lại của đoạn 4k và phần lớn. Họ có tất cả dữ liệu, vì vậy việc ghép nối chúng và phân tích cú pháp chúng là những gì chúng tôi làm.

    Bạn có thể tối ưu hóa bước ghép nối.

TL; DR phiên bản: Tôi xử lý nó bên ngoài attoparsec và điều khiển vòng lặp để tránh sự cố.

Các combinatorrent liên quan cam kết là fc131fe24, xem

https://github.com/jlouis/combinatorrent/commit/fc131fe24207909dd980c674aae6aaba27b966d4

cho các chi tiết.

Các vấn đề liên quan