programing tip

힙 손상 오류를 디버깅하는 방법?

itbloger 2020. 6. 1. 19:07
반응형

힙 손상 오류를 디버깅하는 방법?


Visual Studio 2008에서 (네이티브) 멀티 스레드 C ++ 응용 프로그램을 디버깅하고 있습니다. 임의의 경우에 "Windows가 중단 점을 트리거했습니다 ..."라는 오류가 발생합니다. 더미. 이 오류는 응용 프로그램이 즉시 충돌하지는 않지만 잠시 후에 충돌 할 가능성이 있습니다.

이러한 오류의 큰 문제는 실제로 손상이 발생한 후에 만 ​​오류가 발생하여 특히 다중 스레드 응용 프로그램에서 추적 및 디버깅이 매우 어렵다는 것입니다.

  • 어떤 종류의 일이 이러한 오류를 일으킬 수 있습니까?

  • 어떻게 디버깅합니까?

팁, 도구, 방법, 깨달음은 환영합니다.


Windows 용 디버깅 도구 와 결합 된 Application Verifier 는 놀라운 설정입니다. Windows 드라이버 키트 또는 더 가벼운 Windows SDK 의 일부로 둘 다 얻을 수 있습니다 . ( 힙 손상 문제에 대한 이전 질문을 조사 할 때 Application Verifier에 대해 알게 되었습니다 .) 과거에도 BoundsChecker 및 Insure ++ (다른 답변에서 언급)를 사용했지만 Application Verifier의 기능이 얼마나 많은지 놀랐습니다.

Electric Fence (일명 "efence"), dmalloc , valgrind 등은 언급 할 가치가 있지만, 대부분 Windows보다 * nix에서 실행하기가 훨씬 쉽습니다. Valgrind는 엄청나게 융통성이 있습니다. 대용량 서버 소프트웨어를 사용하여 많은 힙 문제를 디버깅했습니다.

다른 모든 방법이 실패하면 자체 글로벌 운영자에게 new / delete 및 malloc / calloc / realloc 과부하를 제공 할 수 있습니다. 그렇게하는 방법은 컴파일러와 플랫폼에 따라 조금씩 다를 수 있으며 이는 약간의 투자입니다. 그러나 장기적으로 보답 할 수 있습니다. 바람직한 기능 목록은 dmalloc과 electricfence 및 놀랍도록 뛰어난 책 Writing Solid Code :

  • 센트리 값 : 최대 정렬 요구 사항을 고려하여 각 할당 전후에 약간의 공간을 확보하십시오. 매직 숫자로 채움 (버퍼 오버플로 및 언더 플로 및 가끔 "와일드"포인터를 잡는 데 도움이 됨)
  • alloc fill : 0이 아닌 마법의 값으로 새로운 할당을 채 웁니다 .Visual C ++은 이미 디버그 빌드 에서이 작업을 수행합니다 (초기화되지 않은 var의 사용을 잡는 데 도움이 됨)
  • free fill : 대부분의 경우 역 참조되는 경우 segfault를 트리거하도록 설계된 0이 아닌 매직 값으로 해제 된 메모리를 채 웁니다 (매달려있는 포인터를 잡는 데 도움이 됨)
  • delayed free : 사용 가능한 메모리를 힙에 잠시 동안 반환하지 않고 여유 공간을 채우지 만 사용할 수는 없습니다 (더 많은 매달려있는 포인터를 잡는 데 도움이되고 근접한 이중 프리를 잡는 데 도움이 됨)
  • 추적 : 할당이 이루어진 위치를 기록 할 수 있으면 때로는 유용 할 수 있습니다

로컬 홈 브루 시스템 (내장 된 대상의 경우)에서는 런타임 오버 헤드가 훨씬 높기 때문에 추적을 다른 항목과 별도로 유지합니다.


이러한 할당 기능 / 오퍼레이터를 오버로드해야하는 더 많은 이유에 관심이있는 경우 "전역 오퍼레이터를 새로로드하여 삭제 해야하는 이유는 무엇입니까?" ; 뻔뻔한 자체 홍보 외에 힙 손상 오류를 추적하는 데 도움이되는 기타 기술과 기타 적용 가능한 도구가 나열되어 있습니다.


MS가 사용하는 alloc / free / fence 값을 검색 할 때 여기에서 내 자신의 대답을 계속 찾기 때문에 Microsoft dbgheap fill values에 대한 다른 대답이 있습니다 .


응용 프로그램에 대해 페이지 힙을 활성화하여 많은 힙 손상 문제를 감지 할 수 있습니다. 이렇게하려면 Windows 용 디버깅 도구 의 일부로 제공되는 gflags.exe를 사용해야 합니다.

Gflags.exe를 실행하고 실행 파일의 이미지 파일 옵션에서 "페이지 힙 사용"옵션을 선택하십시오.

이제 exe를 다시 시작하고 디버거에 연결하십시오. 페이지 힙을 사용하면 힙 손상이 발생할 때마다 응용 프로그램이 디버거로 침입합니다.


작업 속도를 늦추고 많은 런타임 검사를 수행하려면 main()Microsoft Visual Studio C ++에서 다음을 추가하십시오.

_CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF | _CRTDBG_CHECK_ALWAYS_DF );

매우 관련있는 기사는 Application Verifier 및 Debugdiag를 사용한 디버깅 힙 손상입니다 .


어떤 종류의 일이 이러한 오류를 일으킬 수 있습니까?

버퍼 종료 후 쓰기 또는 버퍼가 힙으로 다시 해제 된 후 버퍼에 쓰기와 같이 메모리로 나쁜 일을하는 것.

어떻게 디버깅합니까?

실행 파일에 자동화 된 경계 검사를 추가하는 도구를 사용하십시오 (예 : Unix의 valgrind) 또는 Windows의 BoundsChecker (Wikipedia는 Purify 및 Insure ++를 제안합니다)와 같은 도구를 사용하십시오.

이로 인해 응용 프로그램이 느려질 수 있으므로 응용 프로그램이 소프트 실시간 응용 프로그램 인 경우 사용하지 못할 수 있습니다.

또 다른 가능한 디버깅 보조 도구는 MicroQuill의 HeapAgent 일 수 있습니다.


사용 가능한 메모리에 대한 액세스 감지 에서 얻은 한 가지 빠른 팁 은 다음과 같습니다.

메모리 블록에 액세스하는 모든 명령문을 확인하지 않고 오류를 빠르게 찾으려면 블록을 해제 한 후 메모리 포인터를 유효하지 않은 값으로 설정할 수 있습니다.

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

내가 유용하게 사용할 수있는 최상의 도구는 코드 검토 (좋은 코드 검토 자 포함)입니다.

코드 검토 외에 먼저 Page Heap을 시도 합니다. 페이지 힙을 설정하는 데 몇 초가 걸리며 운이 좋으면 문제가 발생할 수 있습니다.

Page Heap이 마음에 들지 않으면 Microsoft에서 Windows 용 디버깅 도구를 다운로드 하고 WinDbg 사용 방법을 배우십시오. 더 구체적인 도움을 줄 수는 없지만 멀티 스레드 힙 손상을 디버깅하는 것은 과학보다 예술입니다. "WinDbg 힙 손상"에 대한 Google의 경우 주제에 대한 기사를 많이 찾을 수 있습니다.


동적 또는 정적 C 런타임 라이브러리에 링크되어 있는지 확인할 수도 있습니다. DLL 파일이 정적 C 런타임 라이브러리에 연결되어 있으면 DLL 파일에 별도의 힙이 있습니다.

따라서 한 DLL에서 개체를 만들고 다른 DLL에서 해제하려고하면 위에서 본 것과 같은 메시지가 나타납니다. 이 문제는 다른 DLL에 할당 된 메모리 해제 다른 스택 오버플로 질문에서 참조됩니다 .


어떤 유형의 할당 기능을 사용하고 있습니까? 최근에 Heap * 스타일 할당 함수를 사용하여 비슷한 오류가 발생했습니다.

It turned out that I was mistakenly creating the heap with the HEAP_NO_SERIALIZE option. This essentially makes the Heap functions run without thread safety. It's a performance improvement if used properly but shouldn't ever be used if you are using HeapAlloc in a multi-threaded program [1]. I only mention this because your post mentions you have a multi-threaded app. If you are using HEAP_NO_SERIALIZE anywhere, delete that and it will likely fix your problem.

[1] There are certain situations where this is legal, but it requires you to serialize calls to Heap* and is typically not the case for multi-threaded programs.


If these errors occur randomly, there is high probability that you encountered data-races. Please, check: do you modify shared memory pointers from different threads? Intel Thread Checker may help to detect such issues in multithreaded program.


In addition to looking for tools, consider looking for a likely culprit. Is there any component you're using, perhaps not written by you, which may not have been designed and tested to run in a multithreaded environment? Or simply one which you do not know has run in such an environment.

The last time it happened to me, it was a native package which had been successfully used from batch jobs for years. But it was the first time at this company that it had been used from a .NET web service (which is multithreaded). That was it - they had lied about the code being thread safe.


You can use VC CRT Heap-Check macros for _CrtSetDbgFlag: _CRTDBG_CHECK_ALWAYS_DF or _CRTDBG_CHECK_EVERY_16_DF.._CRTDBG_CHECK_EVERY_1024_DF.


I'd like to add my experience. In the last few days, I solved an instance of this error in my application. In my particular case, the errors in the code were:

  • Removing elements from an STL collection while iterating over it (I believe there are debug flags in Visual Studio to catch these things; I caught it during code review)
  • This one is more complex, I'll divide it in steps:
    • From a native C++ thread, call back into managed code
    • In managed land, call Control.Invoke and dispose a managed object which wraps the native object to which the callback belongs.
    • Since the object is still alive inside the native thread (it will remain blocked in the callback call until Control.Invoke ends). I should clarify that I use boost::thread, so I use a member function as the thread function.
    • 해결책 : Control.BeginInvoke객체가 파괴되기 전에 원시 스레드가 종료되도록 콜백의 목적을 사용하십시오 (콜백의 목적은 스레드가 종료되어 객체가 파괴 될 수 있음을 정확하게 알려줍니다).

나는 비슷한 문제를 겪었고 꽤 무작위로 나타났습니다. 아마도 빌드 파일에서 무언가가 손상되었을 수 있지만 프로젝트를 먼저 정리 한 다음 다시 빌드하여 문제를 해결했습니다.

주어진 다른 응답 외에도 :

어떤 종류의 일이 이러한 오류를 일으킬 수 있습니까? 빌드 파일에 문제가 있습니다.

어떻게 디버깅합니까? 프로젝트 청소 및 재건 수정 된 경우 문제 일 수 있습니다.

참고 URL : https://stackoverflow.com/questions/1010106/how-to-debug-heap-corruption-errors

반응형