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


165

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

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

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

  • 어떻게 디버깅합니까?

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

답변:


128

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 Writing Solid Code :

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

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


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


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


3
Application Verifier에 대해 주목할 가치가있는 작은 것 : 기호 검색 경로에서 Microsoft 기호 서버 기호보다 Application Verifier의 기호를 등록해야합니다.이를 사용하는 경우 ...! 필요한 기호를 찾는 것.
— leander

Application Verifier는 많은 도움이되었으며 추측과 결합하여 문제를 해결할 수있었습니다! 유용한 포인트를 제시해 주셔서 감사합니다.

Application Verifier를 WinDbg와 함께 사용해야합니까, 아니면 Visual Studio 디버거와 함께 작동해야합니까? 사용하려고했지만 VS2012에서 디버깅 할 때 오류가 발생하지 않거나 아무것도하지 않습니다.
— Nathan Reed

@NathanReed : 나는 그것뿐만 아니라 VS 작동 생각 - 볼 msdn.microsoft.com/en-us/library/ms220944(v=vs.90).aspx - 노트이 링크 VS2008에 대한 있지만, 아니에요 이후 버전에 대한 확신. 메모리는 약간 희미하지만 "초기 질문"링크에서 문제가 발생했을 때 방금 Application Verifier를 실행하고 옵션을 저장하고 프로그램을 실행했으며 충돌 할 때 디버깅 할 VS를 선택했습니다. AV는 방금 이전에 충돌 / 주장했습니다. ! avrf 명령은 내가 아는 한 WinDbg에만 해당됩니다. 다른 사람들이 더 많은 정보를 제공 할 수 있기를 바랍니다.
— leander

감사. 나는 실제로 원래의 문제를 해결했으며 결국 힙 손상이 아닌 다른 것으로 판명되었으므로 App Verifier가 아무것도 찾지 못한 이유를 설명합니다. :)
— Nathan Reed

35

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

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

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


네, 일단 메모리 손상 충돌 후 콜 스택 덤프 에서이 함수 호출을 받으면 : wow64! Wow64NotifyDebugger, 어떻게해야합니까? 나는 여전히 내 응용 프로그램에서 무엇이 잘못되고 있는지 모른다.
— Guillaume07

매우 유용한 작은 도구 인 gflags를 사용하여 힙 손상을 디버깅하십시오. gflags로 인스트루먼트 할 때 디버거에 즉시 침입하는 사용 가능한 메모리에 액세스하는 것으로 나타났습니다 ... 핸디!
— Dave F

훌륭한 도구! Windows가 손상의 주소를 말하지 않고 "뭔가"만 잘못되어 실제로 도움이되지 않기 때문에 며칠 동안 사냥하고있는 버그를 발견했습니다.
— Devolus

파티에 약간 늦었지만 Page Heap을 켤 때 디버깅중인 응용 프로그램의 메모리 사용량이 크게 증가했습니다. 불행하게도 힙 손상 감지가 트리거되기 전에 (32 비트) 응용 프로그램의 메모리가 부족합니다. 그 문제를 해결하는 방법에 대한 아이디어가 있습니까?
— uceumern

13

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

_CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF | _CRTDBG_CHECK_ALWAYS_DF );


8

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

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

어떻게 디버깅합니까?

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

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

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


1
디버깅 런타임 (/ MDd 또는 / MTd 플래그)으로 응용 프로그램을 다시 작성하는 것이 첫 단계입니다. 이들은 malloc에서 추가 검사를 수행하고 무료로 수행하며 종종 버그 위치를 좁히는 데 효과적입니다.
— 러시아어를

MicroQuill의 HeapAgent : 많은 글을 듣거나 들리지 않았지만 힙 손상의 경우 목록에 있어야합니다.
— Samrat Patil

1
BoundsChecker는 연기 테스트로 잘 작동하지만 프로덕션에서 해당 프로그램을 실행하려고 시도하는 동안 프로그램을 실행하는 것에 대해 생각조차하지 않습니다. 사용중인 옵션 및 컴파일러 계측 기능 사용 여부에 따라 60x에서 300x 사이의 속도 저하가 발생할 수 있습니다. 면책 조항 : 저는 Micro Focus의 제품을 유지 관리하는 사람들 중 하나입니다.
— Rick Papo

8

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

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

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

5

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

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

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


4

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

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


3

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

HEAP_NO_SERIALIZE옵션으로 힙을 실수로 생성 한 것으로 나타났습니다 . 따라서 본질적으로 힙 기능이 스레드 안전없이 실행됩니다. 올바르게 사용하면 성능이 향상되지만 다중 스레드 프로그램에서 HeapAlloc을 사용하는 경우 사용해서는 안됩니다 [1]. 귀하의 게시물에 멀티 스레드 응용 프로그램이 있다고 언급했기 때문에 이것을 언급합니다. HEAP_NO_SERIALIZE를 어디에서나 사용하는 경우이를 삭제하면 문제가 해결 될 수 있습니다.

[1] 이것이 합법적 인 특정 상황이 있지만 Heap *에 대한 호출을 직렬화해야하지만 일반적으로 다중 스레드 프로그램에는 해당되지 않습니다.


예 : 응용 프로그램의 컴파일러 / 빌드 옵션을보고 C 런타임 라이브러리의 "멀티 스레드"버전과 연결되도록 빌드되어 있는지 확인하십시오.
— ChrisW 2016 년

HeapAlloc 스타일 API의 @ChrisW는 다릅니다. 실제로는 링크 시간이 아니라 힙 생성시 변경 될 수있는 매개 변수입니다.
— JaredPar 2016 년

오. OP가 CRT의 힙이 아니라 해당 힙에 대해 이야기하고있는 것은 나에게 발생하지 않았습니다.
— ChrisW 2016 년

@ChrisW, 질문은 다소 모호하지만 ~ 1 주 전에 자세히 설명한 문제에 부딪 혔으므로 마음이 상쾌합니다.
— JaredPar 2016 년

3

이러한 오류가 임의로 발생하면 데이터 레이스가 발생할 가능성이 높습니다. 확인하십시오 : 다른 스레드에서 공유 메모리 포인터를 수정합니까? 인텔 스레드 검사기는 다중 스레드 프로그램에서 이러한 문제를 감지하는 데 도움이 될 수 있습니다.


1

도구를 찾는 것 외에도 범인을 찾아보십시오. 다중 스레드 환경에서 실행되도록 설계 및 테스트되지 않은 구성 요소가 있습니까? 또는 단순히 하나의 당신이하지 않는 알고 이러한 환경에서 실행하고있다.

그것이 마지막으로 일어 났을 때, 수년간 배치 작업에서 성공적으로 사용 된 기본 패키지였습니다. 그러나이 회사에서 .NET 웹 서비스 (멀티 스레드)를 사용한 것은 이번이 처음입니다. 그게 전부였습니다-코드가 스레드 안전하다는 거짓말을했습니다.


1

_CrtSetDbgFlag에 대해 VC CRT 힙 검사 매크로를 사용할 수 있습니다 . _CRTDBG_CHECK_ALWAYS_DF 또는 _CRTDBG_CHECK_EVERY_16_DF .. _CRTDBG_CHECK_EVERY_1024_DF .


0

내 경험을 추가하고 싶습니다. 지난 며칠 동안 응용 프로그램 에서이 오류의 인스턴스를 해결했습니다. 필자의 경우 코드의 오류는 다음과 같습니다.

  • STL 컬렉션에서 요소를 반복하면서 제거합니다 (Visual Studio에는 이러한 것들을 잡기 위해 디버그 플래그가 있다고 생각합니다. 코드 검토 중에 잡았습니다)
  • 이것은 더 복잡합니다. 단계별로 나눌 것입니다.
    • 네이티브 C ++ 스레드에서 관리 코드로 다시 호출
    • 관리되는 토지에서는 Control.Invoke콜백이 속한 기본 개체를 래핑하는 관리되는 개체를 호출 하고 폐기합니다.
    • 객체가 여전히 네이티브 스레드 내부에 있기 때문에 ( Control.Invoke종료 될 때까지 콜백 호출에서 차단 된 상태로 유지 됩니다). 내가 사용한다는 것을 명확히해야 boost::thread하므로 멤버 함수를 스레드 함수로 사용합니다.
    • 솔루션 : Control.BeginInvoke객체가 파괴되기 전에 원시 스레드가 종료되도록 콜백의 목적을 사용하십시오 (콜백의 목적은 스레드가 종료되어 객체가 파괴 될 수 있음을 정확하게 알려줍니다).

0

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

주어진 다른 응답 외에도 :

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

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

당사 사이트를 사용함과 동시에 당사의 쿠키 정책과 개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.