2010-05-13 23 views
5

Tôi đã viết trình gỡ lỗi heap sau đây để chứng minh rò rỉ bộ nhớ, xóa kép và sai hình thức xóa (tức là cố xóa một mảng với delete p thay vì delete[] p) để bắt đầu lập trình viên.Phê bình trình gỡ lỗi heap của tôi

Tôi rất muốn nhận được một số phản hồi về điều đó từ các lập trình viên mạnh mẽ của C++ bởi vì tôi chưa bao giờ làm điều này trước đây và tôi chắc chắn rằng tôi đã thực hiện một số sai lầm ngu ngốc. Cảm ơn!

#include <cstdlib> 
#include <iostream> 
#include <new> 

namespace 
{ 
    const int ALIGNMENT = 16; 
    const char* const ERR = "*** ERROR: "; 
    int counter = 0; 

    struct heap_debugger 
    { 
     heap_debugger() 
     { 
      std::cerr << "*** heap debugger started\n"; 
     } 

     ~heap_debugger() 
     { 
      std::cerr << "*** heap debugger shutting down\n"; 
      if (counter > 0) 
      { 
       std::cerr << ERR << "failed to release memory " << counter << " times\n"; 
      } 
      else if (counter < 0) 
      { 
       std::cerr << ERR << (-counter) << " double deletes detected\n"; 
      } 
     } 
    } instance; 

    void* allocate(size_t size, const char* kind_of_memory, size_t token) throw (std::bad_alloc) 
    { 
     void* raw = malloc(size + ALIGNMENT); 
     if (raw == 0) throw std::bad_alloc(); 

     *static_cast<size_t*>(raw) = token; 
     void* payload = static_cast<char*>(raw) + ALIGNMENT; 

     ++counter; 
     std::cerr << "*** allocated " << kind_of_memory << " at " << payload << " (" << size << " bytes)\n"; 
     return payload; 
    } 

    void release(void* payload, const char* kind_of_memory, size_t correct_token, size_t wrong_token) throw() 
    { 
     if (payload == 0) return; 

     std::cerr << "*** releasing " << kind_of_memory << " at " << payload << '\n'; 
     --counter; 

     void* raw = static_cast<char*>(payload) - ALIGNMENT; 
     size_t* token = static_cast<size_t*>(raw); 

     if (*token == correct_token) 
     { 
      *token = 0xDEADBEEF; 
      free(raw); 
     } 
     else if (*token == wrong_token) 
     { 
      *token = 0x177E6A7; 
      std::cerr << ERR << "wrong form of delete\n"; 
     } 
     else 
     { 
      std::cerr << ERR << "double delete\n"; 
     } 
    } 
} 

void* operator new(size_t size) throw (std::bad_alloc) 
{ 
    return allocate(size, "non-array memory", 0x5AFE6A8D); 
} 

void* operator new[](size_t size) throw (std::bad_alloc) 
{ 
    return allocate(size, " array memory", 0x5AFE6A8E); 
} 

void operator delete(void* payload) throw() 
{ 
    release(payload, "non-array memory", 0x5AFE6A8D, 0x5AFE6A8E); 
} 

void operator delete[](void* payload) throw() 
{ 
    release(payload, " array memory", 0x5AFE6A8E, 0x5AFE6A8D); 
} 

Trả lời

4

Thay vì thực hiện ghi chú xâm nhập, bạn có thể giữ danh sách tất cả các phân bổ được thực hiện. Sau đó, bạn có thể giải phóng bộ nhớ mà không phá hủy dữ liệu của chính mình và theo dõi số lần một địa chỉ cụ thể bị "xóa" và cũng tìm thấy những nơi chương trình cố xóa địa chỉ không phù hợp (nghĩa là không có trong danh sách).

+0

Làm cách nào để đảm bảo rằng danh sách lưu ý không theo dõi bộ nhớ của chính nó? Viết một cấp phát? Viết danh sách của riêng tôi thực hiện dựa trên malloc và miễn phí? – fredoverflow

+0

Việc triển khai đầu tiên của tôi về trình gỡ lỗi heap không xâm nhập có thể được tìm thấy [ở đây] (http://stackoverflow.com/questions/2835416/critique-my-non-intrusive-heap-debugger). – fredoverflow

1
void* raw = static_cast<char*>(payload) - ALIGNMENT; 

Nếu payload đã bị xoá, sẽ không làm cho hành vi undefined này?

+0

Thành thật mà nói, tôi không biết. Tôi hy vọng rằng khi một con trỏ đã được đưa trở lại bởi 'mới' tại một số điểm trong quá khứ, sử dụng nó một lần nữa sẽ không gây ra quá nhiều ma quỷ bay ra khỏi mũi của tôi. Nhưng tôi không chắc chắn. – fredoverflow

+0

Ý tôi là, tôi không thấy làm thế nào bạn có thể hy vọng kiểm tra xem liệu bạn có được một con trỏ hợp lệ không nếu bạn hoàn toàn dựa vào con trỏ hợp lệ để bắt đầu (hoặc những con quỷ khác * sẽ bay). Tôi nghĩ rằng bạn có thể muốn xem xét lại câu trả lời của bitc ... – UncleBens

1

Tôi không theo dõi việc sử dụng các hằng số được mã hóa cứng/các chuỗi liên tục rất tốt - đặt chúng trong một môi trường không? Và tôi thực sự không nhận được ý tưởng về mã thông báo khá tốt. Cần thêm nhận xét

+0

Ah, bạn có nghĩa là các thẻ và công cụ "mảng/không mảng". Vâng, điều đó có ý nghĩa. Cảm ơn. – fredoverflow

2

Đó thực sự là một khởi đầu tuyệt vời. Đây là 2 xu của tôi khi bạn đã yêu cầu phản hồi:

  1. Mã ghi thông tin theo dõi vào cerr, thực sự là do lỗi. Sử dụng cout cho các bản ghi thông tin.
  2. Số lượng ALIGNMENT là tùy ý. Nếu mã đã cố gắng phân bổ 4090 byte, bạn sẽ phân bổ 4106, sẽ tràn vào khối 4k tiếp theo, đó là kích thước của một trang bộ nhớ. Giá trị căn chỉnh được tính toán sẽ tốt hơn ... hoặc đổi tên ALIGNMENT thành HEADER_SIZE hoặc tương tự.
  3. Với tiêu đề bạn đang tạo, bạn có thể lưu trữ kích thước và cờ cho 'loại bộ nhớ' tại thời điểm phân bổ và so sánh nó ở thời điểm phát hành.
  4. Mã thông báo có thể được gọi là 'sentinel' hoặc 'canary value'.
  5. Tại sao Token là size_t? Tại sao không chỉ là một khoảng trống *?
  6. Kiểm tra của bạn cho null trong phát hành có lẽ nên ném một ngoại lệ thay vì - nó sẽ không là một lỗi nếu mã đã xóa một con trỏ null?
  7. Giá trị 'correct_token' và 'wrong_token' của bạn quá giống nhau. Tôi phải đọc lại mã để chắc chắn.
  8. Cho điểm (3), bạn có thể tăng gấp đôi số tiền bạn phân bổ bổ sung và có trước và sau khối gửi/bảo vệ. Điều này sẽ phát hiện bộ nhớ bị hỏng và tràn ngập.
+0

Thật an toàn để xóa một con trỏ rỗng. – bitc

+1

2.Làm cách nào để tính giá trị hoạt động cho mọi loại? | 5. Tôi không hiểu tại sao giá trị tích phân cố định phải là 'void *', vui lòng giải thích. | 6. Không, xóa 0 phải là một noop. Việc ném một ngoại lệ sẽ vi phạm hợp đồng xóa. | 7. Có, cảm ơn cho quan sát đó. | 8. Mặc dù không phải mục tiêu ban đầu của tôi, đây có lẽ vẫn là một ý tưởng tốt để thực hiện. Cảm ơn! – fredoverflow

+1

1. Hoặc có thể là 'làm tắc nghẽn'? Tôi nghĩ rằng điểm là để có thể chuyển hướng đầu ra đăng nhập. – UncleBens

2

Giải thích lý do bạn chọn "ALIGNMENT" làm số nhận dạng. Giải thích lý do tại sao bạn chọn 16. Tranh luận cách thuật toán của bạn bắt được các lỗi phổ biến nhất, chẳng hạn như làm tràn phần cuối của khối được phân bổ heap hoặc quên phát hành bộ nhớ.

+0

Bạn có nghĩa là "giải thích cho bạn" hoặc "thêm nhận xét vào mã" không? Tôi đã chọn "ALIGNMENT" vì phân bổ bộ nhớ cho các đối tượng tùy ý phải được căn chỉnh bởi một số số hoạt động cho mọi loại. Tôi đã chọn 16 vì tôi không biết một cách di động để xác định giá trị thực, vì vậy đề xuất tốt hơn được chào đón :) tràn bộ đệm không được trình gỡ lỗi heap của tôi bao phủ. Quên để giải phóng bộ nhớ được bao phủ bằng cách kiểm tra một bộ đếm được tăng lên với mỗi lần thực hiện 'mới' và giảm đi với mỗi lần thực hiện' xóa', xem mã trong '~ heap_debugger'. – fredoverflow

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