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


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.