Win32에서 힙 손상; 찾는 방법?
힙을 손상 시키는 다중 스레드 C ++ 응용 프로그램 에서 작업 중입니다 . 이 손상을 찾는 일반적인 도구는 적용 할 수없는 것 같습니다. 소스 코드의 이전 빌드 (18 개월)는 최신 릴리스와 동일한 동작을 나타내므로 이것은 오랫동안 사용되어 왔으며 눈에 띄지 않았습니다. 단점은 소스 델타를 사용하여 버그가 도입 된시기를 식별하는 데 사용할 수 없다는 것 입니다. 저장소에 많은 코드 변경이 있습니다.
충돌 동작에 대한 프롬프트는이 시스템에서 처리량을 생성하는 것입니다. 내부 표현으로 뭉쳐진 데이터의 소켓 전송입니다. 주기적으로 앱을 예외로 만드는 테스트 데이터 세트가 있습니다 (힙 할당 실패를 포함하여 다양한 장소, 다양한 원인, 따라서 힙 손상).
동작은 CPU 성능 또는 메모리 대역폭과 관련이있는 것 같습니다. 각각의 기계가 많을수록 충돌하기가 더 쉽습니다. 하이퍼 스레딩 코어 또는 듀얼 코어 코어를 비활성화하면 손상 비율이 줄어들지 만 제거하지는 않습니다. 이것은 타이밍 관련 문제를 암시합니다.
이제 문제는 다음과 같습니다.
경량 디버그 환경 (예 :)에서 실행 Visual Studio 98 / AKA MSVC6
하면 힙 손상을 재현하기가 합리적으로 쉽습니다 alloc;
. 정교한 디버그 환경에서 실행 하는 경우 (Rational Purify, VS2008/MSVC9
또는 Microsoft Application Verifier) 시스템이 메모리 속도 제한이되어 충돌하지 않습니다 (메모리 제한 : CPU가 초과되지 않고 50%
디스크 표시등이 켜지지 않음, 프로그램이 최대한 빠르게 1.3G
진행됨 , 상자 에서 2G RAM을 사용함) . 따라서 문제를 재현 할 수 있는지 (원인을 식별 할 수 없음) 또는 원인을 식별 할 수 있는지 또는 재현 할 수없는 문제를 식별 할 수 있는지 중에서 선택할 수 있습니다.
다음에 할 위치에 대한 나의 현재 최선의 추측은 다음과 같습니다.
- 엄청나게 지저분한 상자를 가져옵니다 (현재 개발 상자를 대체하려면에서 2Gb RAM
E6550 Core2 Duo
). 이렇게하면 강력한 디버그 환경에서 실행할 때 오작동을 일으키는 충돌을 재현 할 수 있습니다. 또는 - 연산자를 재 작성
new
하고delete
사용VirtualAlloc
하고VirtualProtect
읽기 전용으로 즉시이 함께 할 것으로 표시 메모리. 아래로 실행MSVC6
하고 OS가 해제 된 메모리에 쓰는 나쁜 사람을 잡도록합니다. 예, 이것은 절망의 표시는 다음과 같습니다 도대체 재 작성 누구new
와delete
?! 이것이 Purify et al. 에서처럼 느리게 만들지 궁금합니다.
그리고 아니요 : Purify 기기가 내장 된 배송은 옵션이 아닙니다.
동료가 방금지나 가서 "스택 오버플로? 지금 스택 오버플로가 발생합니까?!?"라고 물었습니다.
이제 질문 : 힙 손상자를 어떻게 찾습니까?
업데이트 : 균형 new[]
과 delete[]
문제 해결을 향한 먼 길을받은 것으로 보인다. 15 분 대신 앱이 충돌하기 전에 약 2 시간이 걸립니다. 아직 없습니다. 추가 제안이 있습니까? 힙 손상이 지속됩니다.
업데이트 : Visual Studio 2008의 릴리스 빌드가 훨씬 더 좋아 보입니다. 현재 의혹은와 STL
함께 제공 되는 구현에 있습니다 VS98
.
- 문제를 재현하십시오.
Dr Watson
추가 분석에 도움이 될 수있는 덤프를 생성합니다.
나는 그것을 기록 할 것이지만, Dr Watson이 힙이 밟힐 때가 아니라 사실 이후에만 넘어 질 것이 걱정됩니다.
또 다른 시도
WinDebug
는 매우 강력한 동시에 경량 인 디버깅 도구 로 사용할 수 있습니다 .
다시 한 번 말씀 드리지만, 뭔가 잘못 될 때까지 많은 도움이되지 않았습니다. 나는 행위에서 파괴자를 붙잡고 싶다.
이러한 도구를 사용하면 적어도 특정 구성 요소로 문제를 좁힐 수 있습니다.
희망은별로 없지만 절박한시기는 ...
그리고 프로젝트의 모든 구성 요소에 올바른 런타임 라이브러리 설정 (
C/C++ tab
, VS 6.0 프로젝트 설정의 코드 생성 범주) 이 있는지 확인 합니까?
아니요, 그렇지 않습니다. 내일 작업 공간 (58 개의 프로젝트)을 살펴보고 모두 적절한 플래그로 컴파일 및 연결되고 있는지 확인하는 데 몇 시간을 보낼 것입니다.
업데이트 : 30 초가 걸렸습니다.
Settings
대화 상자 에서 모든 프로젝트를
선택하고 올바른 설정이없는 프로젝트 (모두 올바른 설정이 있음)를 찾을 때까지 선택을 취소합니다.
첫 번째 선택은 pageheap.exe 와 같은 전용 힙 도구 입니다.
새로 작성하고 삭제하는 것이 유용 할 수 있지만 하위 수준 코드에서 커밋 된 할당을 포착하지 못합니다. 이것이 당신이 원하는 것이라면 low-level alloc API
Microsoft Detours를 사용 하여 s 를 우회하는 것이 좋습니다 .
또한 다음과 같은 온 전성 검사를 수행합니다. 런타임 라이브러리가 일치하는지 확인 (릴리스 대 디버그, 다중 스레드 대 단일 스레드, dll 대 정적 lib), 잘못된 삭제를 찾습니다 (예 : delete []가 있어야하는 위치 삭제). 사용), 할당을 혼합하고 일치시키지 않는지 확인하십시오.
또한 선택적으로 스레드를 끄고 문제가 해결되는시기 /가 있는지 확인하십시오.
첫 번째 예외가 발생했을 때 호출 스택 등은 어떻게 생겼습니까?
나는 내 작업에서 같은 문제를 가지고 있습니다 ( VC6
때때로 사용 합니다). 그리고 그것에 대한 쉬운 해결책은 없습니다. 몇 가지 힌트 만 있습니다.
- 프로덕션 시스템에서 자동 크래시 덤프를 사용해보십시오 ( Process Dumper 참조 ). 내 경험에 따르면 Dr. Watson은 덤핑에 완벽하지 않습니다 .
- 코드에서 모든 catch (...) 를 제거하십시오 . 그들은 종종 심각한 메모리 예외를 숨 깁니다.
- 고급 Windows 디버깅 확인 -귀하와 같은 문제에 대한 유용한 팁이 많이 있습니다. 진심으로 추천합니다.
STL
시도STLPort
하고 확인한 빌드 를 사용하는 경우 . 잘못된 반복자는 지옥입니다.
행운을 빕니다. 귀하와 같은 문제는 해결하는 데 몇 달이 걸립니다. 준비하세요 ...
ADplus -crash -pn appnename.exe
메모리 문제가 발생할 때 원래 응용 프로그램을 실행하면 멋진 큰 덤프를 얻을 수 있습니다.
덤프를 분석하여 손상된 메모리 위치를 파악할 수 있습니다. 운이 좋으면 덮어 쓰기 메모리가 고유 한 문자열이므로 어디에서 왔는지 알아낼 수 있습니다. 운이 좋지 않다면 win32
힙 을 파고 원래 기억 특성이 무엇인지 파악해야합니다. (힙 -x가 도움이 될 수 있음)
엉망이 된 것을 알고 나면 특별한 힙 설정으로 appverifier 사용을 좁힐 수 있습니다. 즉 DLL
, 모니터링 할 항목 또는 모니터링 할 할당 크기를 지정할 수 있습니다 .
이로 인해 범인을 잡을 수있을만큼 모니터링 속도가 빨라지기를 바랍니다.
제 경험상 저는 전체 힙 검증 모드가 필요하지 않았지만 크래시 덤프를 분석하고 소스를 찾아 보는 데 많은 시간을 보냈습니다.
추신 : DebugDiag 를 사용하여 덤프를 분석 할 수 있습니다 . DLL
손상된 힙을 소유하고 있음을 지적하고 기타 유용한 세부 정보를 제공 할 수 있습니다.
우리 자신의 malloc과 무료 함수를 작성하여 행운을 빕니다. 프로덕션에서는 표준 malloc을 호출하고 무료이지만 디버그에서는 원하는 모든 작업을 수행 할 수 있습니다. 또한 이러한 함수를 사용하기 위해 new 및 delete 연산자를 재정의하는 간단한 기본 클래스도 있습니다. 그러면 작성한 모든 클래스는 해당 클래스에서 상속 할 수 있습니다. 엄청난 양의 코드가 있다면 malloc에 대한 호출을 대체하고 새로운 malloc에 무료로 무료 (realloc을 잊지 마세요!)하는 것이 큰 일이 될 수 있지만 장기적으로는 매우 유용합니다.
Steve Maguire의 저서 Writing Solid Code (강력 권장)에는 다음과 같은 루틴에서 수행 할 수있는 디버그 작업의 예가 있습니다.
- 누출을 찾기 위해 할당을 추적하십시오.
- 필요한 것보다 더 많은 메모리를 할당하고 메모리의 시작과 끝에 마커를 배치합니다. 자유 루틴 동안 이러한 마커가 여전히 존재하는지 확인할 수 있습니다.
- 할당 (초기화되지 않은 메모리의 사용량을 찾기 위해) 및 free (free'd 메모리의 사용량을 찾기 위해)에 마커로 메모리를 memset
또 다른 좋은 아이디어는 것입니다 결코 일이 좋아 사용하지 strcpy
, strcat
또는 sprintf
항상 사용 - strncpy
, strncat
하고 snprintf
. 우리는 우리가 버퍼의 끝을 쓰지 않도록하기 위해 우리 자신의 버전도 작성했고 이것들도 많은 문제를 포착했습니다.
런타임 및 정적 분석 모두에서이 문제를 공격해야합니다.
For static analysis consider compiling with PREfast (cl.exe /analyze
). It detects mismatched delete
and delete[]
, buffer overruns and a host of other problems. Be prepared, though, to wade through many kilobytes of L6 warning, especially if your project still has L4
not fixed.
PREfast is available with Visual Studio Team System and, apparently, as part of Windows SDK.
The apparent randomness of the memory corruption sounds very much like a thread synchronization issue - a bug is reproduced depending on machine speed. If objects (chuncks of memory) are shared among threads and synchronization (critical section, mutex, semaphore, other) primitives are not on per-class (per-object, per-class) basis, then it is possible to come to a situation where class (chunk of memory) is deleted / freed while in use, or used after deleted / freed.
As a test for that, you could add synchronization primitives to each class and method. This will make your code slower because many objects will have to wait for each other, but if this eliminates the heap corruption, your heap-corruption problem will become a code optimization one.
Is this in low memory conditions? If so it might be that new is returning NULL
rather than throwing std::bad_alloc. Older VC++
compilers didn't properly implement this. There is an article about Legacy memory allocation failures crashing STL
apps built with VC6
.
You tried old builds, but is there a reason you can't keep going further back in the repository history and seeing exactly when the bug was introduced?
Otherwise, I would suggest adding simple logging of some kind to help track down the problem, though I am at a loss of what specifically you might want to log.
If you can find out what exactly CAN cause this problem, via google and documentation of the exceptions you are getting, maybe that will give further insight on what to look for in the code.
My first action would be as follows:
- Build the binaries in "Release" version but creating debug info file (you will find this possibility in project settings).
- Use Dr Watson as a defualt debugger (DrWtsn32 -I) on a machine on which you want to reproduce the problem.
- Repdroduce the problem. Dr Watson will produce a dump that might be helpful in further analysis.
Another try might be using WinDebug as a debugging tool which is quite powerful being at the same time also lightweight.
Maybe these tools will allow you at least to narrow the problem to certain component.
And are you sure that all the components of the project have correct runtime library settings (C/C++ tab, Code Generation category in VS 6.0 project settings)?
So from the limited information you have, this can be a combination of one or more things:
- Bad heap usage, i.e., double frees, read after free, write after free, setting the HEAP_NO_SERIALIZE flag with allocs and frees from multiple threads on the same heap
- Out of memory
- Bad code (i.e., buffer overflows, buffer underflows, etc.)
- "Timing" issues
If it's at all the first two but not the last, you should have caught it by now with either pageheap.exe.
Which most likely means it is due to how the code is accessing shared memory. Unfortunately, tracking that down is going to be rather painful. Unsynchronized access to shared memory often manifests as weird "timing" issues. Things like not using acquire/release semantics for synchronizing access to shared memory with a flag, not using locks appropriately, etc.
At the very least, it would help to be able to track allocations somehow, as was suggested earlier. At least then you can view what actually happened up until the heap corruption and attempt to diagnose from that.
Also, if you can easily redirect allocations to multiple heaps, you might want to try that to see if that either fixes the problem or results in more reproduceable buggy behavior.
When you were testing with VS2008, did you run with HeapVerifier with Conserve Memory set to Yes? That might reduce the performance impact of the heap allocator. (Plus, you have to run with it Debug->Start with Application Verifier, but you may already know that.)
You can also try debugging with Windbg and various uses of the !heap command.
MSN
If you choose to rewrite new/delete, I have done this and have simple source code at:
http://gandolf.homelinux.org/~smhanov/blog/?id=10
This catches memory leaks and also inserts guard data before and after the memory block to capture heap corruption. You can just integrate with it by putting #include "debug.h" at the top of every CPP file, and defining DEBUG and DEBUG_MEM.
Graeme's suggestion of custom malloc/free is a good idea. See if you can characterize some pattern about the corruption to give you a handle to leverage.
For example, if it is always in a block of the same size (say 64 bytes) then change your malloc/free pair to always allocate 64 byte chunks in their own page. When you free a 64 byte chunk then set the memory protection bits on that page to prevent reads and wites (using VirtualQuery). Then anyone attempting to access this memory will generate an exception rather than corrupting the heap.
This does assume that the number of outstanding 64 byte chunks is only moderate or you have a lot of memory to burn in the box!
The little time I had to solve a similar problem. If the problem still exists I suggest you do this : Monitor all calls to new/delete and malloc/calloc/realloc/free. I make single DLL exporting a function for register all calls. This function receive parameter for identifying your code source, pointer to allocated area and type of call saving this information in a table. All allocated/freed pair is eliminated. At the end or after you need you make a call to an other function for create report for left data. With this you can identify wrong calls (new/free or malloc/delete) or missing. If have any case of buffer overwritten in your code the information saved can be wrong but each test may detect/discover/include a solution of failure identified. Many runs to help identify the errors. Good luck.
Do you think this is a race condition? Are multiple threads sharing one heap? Can you give each thread a private heap with HeapCreate, then they can run fast with HEAP_NO_SERIALIZE. Otherwise, a heap should be thread safe, if you're using the multi-threaded version of the system libraries.
A couple of suggestions. You mention the copious warnings at W4 - I would suggest taking the time to fix your code to compile cleanly at warning level 4 - this will go a long way to preventing subtle hard to find bugs.
Second - for the /analyze switch - it does indeed generate copious warnings. To use this switch in my own project, what I did was to create a new header file that used #pragma warning to turn off all the additional warnings generated by /analyze. Then further down in the file, I turn on only those warnings I care about. Then use the /FI compiler switch to force this header file to be included first in all your compilation units. This should allow you to use the /analyze switch while controling the output
참고URL : https://stackoverflow.com/questions/1069/heap-corruption-under-win32-how-to-locate
'developer tip' 카테고리의 다른 글
익명 스레드 클래스를 시작하는 방법 (0) | 2020.12.10 |
---|---|
Editor.updateCursorPositionMz의 Meizu 장치에 대한 NullPointerException (0) | 2020.12.09 |
ggplot2에서 범례를 이동하거나 배치하는 방법 (0) | 2020.12.09 |
순수 가상 함수에는 인라인 정의가 없을 수 있습니다. (0) | 2020.12.09 |
int에서 해제 / nullable 변환이있는 심각한 버그, 십진수 변환 허용 (0) | 2020.12.09 |