Làm thế nào để gỡ lỗi lỗi tham nhũng heap?


165

Tôi đang gỡ lỗi một ứng dụng C ++ đa luồng (bản địa) trong Visual Studio 2008. Trong những dịp dường như ngẫu nhiên, tôi nhận được một lỗi "Windows đã gây ra một điểm dừng ..." với một lưu ý rằng điều này có thể là do tham nhũng trong đống. Những lỗi này sẽ không luôn làm sập ứng dụng ngay lập tức, mặc dù nó có khả năng bị sập ngay sau đó.

Vấn đề lớn với những lỗi này là chúng chỉ bật lên sau khi tham nhũng thực sự xảy ra, điều này khiến chúng rất khó theo dõi và gỡ lỗi, đặc biệt là trên một ứng dụng đa luồng.

  • Những thứ gì có thể gây ra những lỗi này?

  • Làm thế nào để tôi gỡ lỗi chúng?

Mẹo, công cụ, phương pháp, giác ngộ ... đều được chào đón.

Câu trả lời:


128

Trình xác minh ứng dụng kết hợp với Công cụ gỡ lỗi cho Windows là một thiết lập tuyệt vời. Bạn có thể lấy cả hai như một phần của Bộ điều khiển Windows hoặc SDK Windows nhẹ hơn . (Tìm thấy hiểu về Application Verifier khi nghiên cứu một câu hỏi trước đó về một vấn đề đống tham nhũng .) Tôi đã sử dụng BoundsChecker và Đảm bảo ++ (đề cập trong câu trả lời khác) trong quá khứ cũng vậy, mặc dù tôi đã rất ngạc nhiên bao nhiêu chức năng là trong Application Verifier.

Hàng rào điện (còn gọi là "efence"), dmalloc , valgrind , v.v ... đều đáng được đề cập, nhưng hầu hết trong số này dễ dàng hơn để chạy dưới * nix so với Windows. Valgrind rất linh hoạt: Tôi đã gỡ lỗi phần mềm máy chủ lớn với nhiều vấn đề heap khi sử dụng nó.

Khi thất bại, bạn có thể cung cấp cho nhà điều hành toàn cầu mới / xóa và quá tải malloc / calloc / realloc - cách thực hiện sẽ thay đổi một chút tùy thuộc vào trình biên dịch và nền tảng - và đây sẽ là một khoản đầu tư - nhưng nó có thể trả hết trong thời gian dài. Danh sách tính năng mong muốn sẽ trông quen thuộc từ dmalloc và điện, và cuốn sách xuất sắc đáng ngạc nhiên Writing Solid Code :

  • giá trị sentry : cho phép thêm một chút không gian trước và sau mỗi lần phân bổ, tôn trọng yêu cầu căn chỉnh tối đa; điền vào các số ma thuật (giúp bắt bộ đệm tràn và tràn, và con trỏ "hoang dã" thỉnh thoảng)
  • alloc fill : điền vào các phân bổ mới với giá trị 0 khác - Visual C ++ sẽ thực hiện điều này cho bạn trong các bản dựng Debug (giúp bắt sử dụng các vars chưa được khởi tạo)
  • điền miễn phí : điền vào bộ nhớ đã giải phóng với giá trị ma thuật bằng 0, được thiết kế để kích hoạt một segfault nếu nó bị hủy đăng ký trong hầu hết các trường hợp (giúp bắt con trỏ lơ lửng)
  • bị trì hoãn miễn phí : không trả lại bộ nhớ đã giải phóng cho heap trong một thời gian, giữ cho nó được lấp đầy nhưng không có sẵn (giúp bắt được nhiều con trỏ lơ lửng hơn, bắt được hai lần tự do)
  • theo dõi : có thể ghi lại nơi phân bổ được thực hiện đôi khi có thể hữu ích

Lưu ý rằng trong hệ thống homebrew cục bộ của chúng tôi (đối với mục tiêu được nhúng), chúng tôi giữ việc theo dõi tách biệt với hầu hết các nội dung khác, vì chi phí hoạt động trong thời gian chạy cao hơn nhiều.


Nếu bạn quan tâm đến nhiều lý do hơn để quá tải các hàm / toán tử phân bổ này, hãy xem câu trả lời của tôi về "Bất kỳ lý do nào để quá tải toán tử toàn cầu mới và xóa?" ; không biết xấu hổ tự quảng cáo sang một bên, nó liệt kê các kỹ thuật khác hữu ích trong việc theo dõi các lỗi tham nhũng heap, cũng như các công cụ áp dụng khác.


Bởi vì tôi tiếp tục tìm câu trả lời của riêng mình ở đây khi tìm kiếm các giá trị phân bổ / miễn phí / hàng rào mà MS sử dụng, đây là một câu trả lời khác bao gồm các giá trị điền vào Microsoft dbgheap .


3
Một điều nhỏ đáng chú ý về Trình xác minh ứng dụng: bạn phải đăng ký các ký hiệu của Trình xác minh ứng dụng trước các ký hiệu máy chủ biểu tượng microsoft trong đường dẫn tìm kiếm biểu tượng của bạn, nếu bạn sử dụng ... Tôi đã tìm một chút để tìm hiểu tại sao! tìm kiếm các biểu tượng cần thiết
leander

Trình xác minh ứng dụng là một sự trợ giúp tuyệt vời và kết hợp với một số phỏng đoán, tôi đã có thể giải quyết vấn đề! Cảm ơn rất nhiều, và cho những người khác nữa, vì đã đưa ra những điểm hữu ích.

Trình xác minh ứng dụng có phải được sử dụng với WinDbg không, hay nó có nên hoạt động với trình gỡ lỗi Visual Studio không? Tôi đã cố gắng sử dụng nó, nhưng nó không gây ra bất kỳ lỗi nào hoặc dường như làm bất cứ điều gì khi tôi gỡ lỗi trong VS2012.
Nathan Reed

@NathanReed: Tôi tin rằng nó cũng hoạt động với VS - xem msdn.microsoft.com/en-us/l Library / ms220944 (v = vs.90) .aspx - mặc dù lưu ý liên kết này là dành cho VS2008, nhưng tôi không chắc chắn về các phiên bản sau. Bộ nhớ hơi mờ, nhưng tôi tin rằng khi gặp sự cố trong liên kết "câu hỏi trước đó", tôi vừa chạy Trình xác minh ứng dụng và lưu các tùy chọn, chạy chương trình và khi nó bị lỗi, tôi đã chọn VS để gỡ lỗi. AV chỉ làm cho nó sụp đổ / khẳng định trước đó. Tuy nhiên, lệnh! Avrf dành riêng cho WinDbg theo như tôi biết. Hy vọng những người khác có thể cung cấp thêm thông tin!
leander

Cảm ơn. Tôi thực sự đã giải quyết vấn đề ban đầu của mình và hóa ra cuối cùng không phải là tham nhũng, mà là một thứ khác, vì vậy có lẽ giải thích tại sao Trình xác minh ứng dụng không tìm thấy gì. :)
Nathan Reed

35

Bạn có thể phát hiện rất nhiều vấn đề tham nhũng heap bằng cách kích hoạt Page Heap cho ứng dụng của bạn. Để làm điều này, bạn cần sử dụng gflags.exe như một phần của Công cụ gỡ lỗi cho Windows

Chạy Gflags.exe và trong các tùy chọn tệp Ảnh cho tệp thực thi của bạn, kiểm tra tùy chọn "Bật trang Heap".

Bây giờ khởi động lại exe của bạn và đính kèm với trình gỡ lỗi. Khi Page Heap được kích hoạt, ứng dụng sẽ đột nhập vào trình gỡ lỗi bất cứ khi nào có bất kỳ tham nhũng heap nào xảy ra.


có nhưng một khi tôi nhận được chức năng này trong cuộc gọi callstack của tôi (sau sự cố hỏng bộ nhớ): wow64! Wow64NotifyDebugger, tôi có thể làm gì? Tôi vẫn không biết điều gì đang xảy ra trong ứng dụng của mình
Guillaume07

Chỉ cần thử các gflags để gỡ lỗi tham nhũng heap ở đây, RẤT công cụ nhỏ hữu ích, rất khuyến khích. Hóa ra tôi đang truy cập vào bộ nhớ đã được giải phóng, mà khi được kết hợp với các gflags sẽ ngay lập tức xâm nhập vào trình gỡ lỗi ... Tiện dụng!
Dave F

Công cụ tuyệt vời! Chỉ cần tìm thấy một lỗi, mà tôi đã săn lùng trong nhiều ngày, bởi vì Windows không nói địa chỉ của tham nhũng, chỉ có điều "cái gì đó" là sai, điều này không thực sự hữu ích.
Devolus

Đến bữa tiệc muộn một chút, nhưng tôi nhận thấy việc sử dụng bộ nhớ tăng đáng kể ứng dụng của tôi, tôi đang gỡ lỗi khi bật Page Heap. Thật không may, đến thời điểm ứng dụng (32 bit) hết bộ nhớ trước khi phát hiện tham nhũng heap được kích hoạt. Bất kỳ ý tưởng làm thế nào để giải quyết vấn đề đó?
uceumern

13

Để thực sự làm chậm mọi thứ và thực hiện nhiều kiểm tra thời gian chạy, hãy thử thêm phần sau vào đầu main()hoặc tương đương của bạn trong Microsoft Visual Studio C ++

_CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF | _CRTDBG_CHECK_ALWAYS_DF );


8

Những thứ gì có thể gây ra những lỗi này?

Làm những việc nghịch ngợm với bộ nhớ, ví dụ viết sau khi kết thúc bộ đệm hoặc ghi vào bộ đệm sau khi nó được giải phóng trở lại đống.

Làm thế nào để tôi gỡ lỗi chúng?

Sử dụng một công cụ bổ sung kiểm tra giới hạn tự động để thực thi của bạn: ví dụ: valgrind trên Unix hoặc một công cụ như BoundChecker (Wikipedia cũng gợi ý Purify và Insure ++) trên Windows.

Coi chừng những thứ này sẽ làm chậm ứng dụng của bạn, vì vậy chúng có thể không sử dụng được nếu ứng dụng của bạn là ứng dụng thời gian thực mềm.

Một công cụ hỗ trợ / gỡ lỗi có thể khác có thể là HeapAgent của MicroQuill.


1
Xây dựng lại ứng dụng với thời gian chạy gỡ lỗi (cờ MDd hoặc / MTd) sẽ là bước đầu tiên của tôi. Chúng thực hiện kiểm tra bổ sung tại malloc và miễn phí, và thường thoát hiệu quả trong việc thu hẹp vị trí của (các) lỗi.
Sử dụng tiếng Nga

HeapAgent của MicroQuill: Không có nhiều văn bản hoặc nghe về nó, nhưng đối với tham nhũng heap, nó nên có trong danh sách của bạn.
Samrat Patil

1
BoundChecker hoạt động tốt như một thử nghiệm khói, nhưng thậm chí không nghĩ đến việc chạy một chương trình theo nó trong khi cố gắng chạy chương trình đó trong sản xuất. Làm chậm có thể là bất cứ nơi nào từ 60x đến 300x, tùy thuộc vào tùy chọn bạn đang sử dụng, và bạn có đang sử dụng tính năng thiết bị đo của trình biên dịch hay không. Tuyên bố miễn trừ trách nhiệm: Tôi là một trong những người duy trì sản phẩm cho Micro Focus.
Rick Papo

8

Một mẹo nhanh mà tôi nhận được từ Phát hiện quyền truy cập vào bộ nhớ đã giải phóng là:

Nếu bạn muốn xác định vị trí lỗi một cách nhanh chóng mà không cần kiểm tra mọi câu lệnh truy cập vào khối bộ nhớ, bạn có thể đặt con trỏ bộ nhớ thành giá trị không hợp lệ sau khi giải phóng khối:

#ifdef _DEBUG // detect the access to freed memory
#undef free
#define free(p) _free_dbg(p, _NORMAL_BLOCK); *(int*)&p = 0x666;
#endif

5

Công cụ tốt nhất tôi thấy hữu ích và hoạt động mọi lúc là đánh giá mã (với những người đánh giá mã tốt).

Khác với đánh giá mã, trước tiên tôi nên thử Page Heap . Page Heap mất vài giây để thiết lập và may mắn là nó có thể xác định chính xác vấn đề của bạn.

Nếu không may mắn với Page Heap, hãy tải xuống Công cụ gỡ lỗi cho Windows từ Microsoft và tìm hiểu cách sử dụng WinDbg. Xin lỗi không thể cung cấp cho bạn trợ giúp cụ thể hơn, nhưng gỡ lỗi tham nhũng heap đa luồng là một nghệ thuật hơn là khoa học. Google cho "WinDbg heap heap" và bạn sẽ tìm thấy nhiều bài viết về chủ đề này.


4

Bạn cũng có thể muốn kiểm tra xem liệu bạn đang liên kết với thư viện thời gian chạy C động hay tĩnh. Nếu các tệp DLL của bạn đang liên kết với thư viện thời gian chạy C tĩnh, thì các tệp DLL có các đống riêng biệt.

Do đó, nếu bạn tạo một đối tượng trong một DLL và cố gắng giải phóng nó trong một DLL khác, bạn sẽ nhận được thông điệp tương tự như bạn thấy ở trên. Vấn đề này được tham chiếu trong một câu hỏi Stack Overflow khác, Giải phóng bộ nhớ được phân bổ trong một DLL khác .


3

Bạn đang sử dụng loại chức năng phân bổ nào? Gần đây tôi đã gặp một lỗi tương tự khi sử dụng các hàm phân bổ kiểu Heap *.

Hóa ra là tôi đã tạo nhầm heap với HEAP_NO_SERIALIZEtùy chọn. Điều này về cơ bản làm cho các hàm Heap chạy mà không có sự an toàn của luồng. Đó là một cải tiến hiệu suất nếu được sử dụng đúng cách nhưng không bao giờ được sử dụng nếu bạn đang sử dụng HeapAlloc trong một chương trình đa luồng [1]. Tôi chỉ đề cập đến điều này bởi vì bài đăng của bạn đề cập đến bạn có một ứng dụng đa luồng. Nếu bạn đang sử dụng HEAP_NO_SERIALIZE ở bất cứ đâu, hãy xóa nó và nó có thể sẽ khắc phục vấn đề của bạn.

[1] Có một số tình huống trong đó điều này là hợp pháp, nhưng nó yêu cầu bạn phải tuần tự hóa các cuộc gọi đến Heap * và thường không phải là trường hợp cho các chương trình đa luồng.


Có: xem xét các tùy chọn trình biên dịch / xây dựng của ứng dụng và đảm bảo rằng nó được xây dựng để liên kết với phiên bản "đa luồng" của thư viện thời gian chạy C.
ChrisW

@ChrisW cho API kiểu HeapAlloc thì khác. Đó thực sự là một tham số có thể được thay đổi tại thời điểm tạo heap, không phải thời gian liên kết.
JaredPar

Oh. Điều đó không xảy ra với tôi rằng OP có thể đang nói về đống đó, và không phải về đống trong CRT.
ChrisW

@ChrisW, câu hỏi khá mơ hồ nhưng tôi chỉ nhấn vào vấn đề tôi đã nêu chi tiết ~ 1 tuần trước vì vậy nó mới mẻ trong tâm trí tôi.
JaredPar

3

Nếu những lỗi này xảy ra ngẫu nhiên, có khả năng cao là bạn gặp phải các cuộc đua dữ liệu. Vui lòng kiểm tra: bạn có sửa đổi các con trỏ bộ nhớ dùng chung từ các luồng khác nhau không? Intel Thread Checker có thể giúp phát hiện các vấn đề như vậy trong chương trình đa luồng.


1

Ngoài việc tìm kiếm các công cụ, hãy xem xét tìm kiếm một thủ phạm có khả năng. Có thành phần nào bạn đang sử dụng, có lẽ không phải do bạn viết, có thể chưa được thiết kế và thử nghiệm để chạy trong môi trường đa luồng? Hoặc đơn giản là một cái mà bạn không biết đã chạy trong một môi trường như vậy.

Lần cuối cùng nó xảy ra với tôi, đó là một gói bản địa đã được sử dụng thành công từ các công việc hàng loạt trong nhiều năm. Nhưng đây là lần đầu tiên tại công ty này, nó đã được sử dụng từ một dịch vụ web .NET (đa luồng). Đó là nó - họ đã nói dối về việc mã được an toàn.


1

Bạn có thể sử dụng các macro CRT Heap-Check của VC cho _CrtSetDbgFlag : _CRTDBG_CHECK_ALWAYS_DF hoặc _CRTDBG_CHECK_EVERY_16_DF .. _CRTDBG_CHECK_EVERY_1024 .


0

Tôi muốn thêm kinh nghiệm của tôi. Trong vài ngày qua, tôi đã giải quyết một trường hợp lỗi này trong ứng dụng của mình. Trong trường hợp cụ thể của tôi, các lỗi trong mã là:

  • Xóa các phần tử khỏi bộ sưu tập STL trong khi lặp qua nó (Tôi tin rằng có các cờ gỡ lỗi trong Visual Studio để bắt những thứ này; tôi đã bắt gặp nó trong khi xem xét mã)
  • Cái này phức tạp hơn, tôi sẽ chia nó theo các bước:
    • Từ một luồng C ++ gốc, gọi lại vào mã được quản lý
    • Trong đất được quản lý, gọi Control.Invoke và xử lý một đối tượng được quản lý bao bọc đối tượng gốc mà cuộc gọi lại thuộc về.
    • Vì đối tượng vẫn còn sống bên trong luồng gốc (nó sẽ vẫn bị chặn trong cuộc gọi lại cho đến khi Control.Invokekết thúc). Tôi nên làm rõ rằng tôi sử dụng boost::thread, vì vậy tôi sử dụng một hàm thành viên làm hàm luồng.
    • Giải pháp : Sử dụng Control.BeginInvoke(GUI của tôi được tạo bằng Winforms) thay vào đó để luồng gốc có thể kết thúc trước khi đối tượng bị hủy (mục đích của cuộc gọi lại là thông báo chính xác rằng luồng kết thúc và đối tượng có thể bị hủy).

0

Tôi đã có một vấn đề tương tự - và nó xuất hiện khá ngẫu nhiên. Có lẽ một cái gì đó đã bị hỏng trong các tập tin xây dựng, nhưng cuối cùng tôi đã sửa nó bằng cách làm sạch dự án trước sau đó xây dựng lại.

Vì vậy, ngoài các phản ứng khác được đưa ra:

Những thứ gì có thể gây ra những lỗi này? Một cái gì đó bị hỏng trong tập tin xây dựng.

Làm thế nào để tôi gỡ lỗi chúng? Vệ sinh dự án và xây dựng lại. Nếu nó được sửa, đây có thể là vấn đề.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.