DebugDiag의 메모리 누수 stack sample을 자세히 보기

Application에서 메모리 누수가 발생했는데, 원인을 찾을 수 없다는 case 는 빈번히 접할 수 있다. 다소 당황스러울 수 있지만, DebugDiag라는 Tool을 이용한다면, 쉽게 Allocation Thread stack tracking 할 수 있다. 이유는 leaktrack.dll이라는 injection DLL때문에 그것이 가능한데, 이로 인하여 DebugDiag Tool Allocation Stack sample이라는 형태로 간혹, leak으로 의심할 수 있는 Function Call Check 할 수 있게 한다. 하지만, Callstack이 간혹 너무 단순하거나 Callstack이 너무 복잡하고 길 경우에 일부 Function Call이 생략되는 경우가 발생한다. , 생략된 Function Memory Leak Check 할 수 있는 중요한 Function 이라면 reporting된 정보가 의미 없어 질 수도 있는 것 아닌가. 하지만 방법이 없는 것은 아니다. 현재, DebugDiag 1.1 version에서 제공하는 Leaktrack.dll을 사용하는 경우 아래와 같은 방법을 통해서 보다 Detail Thread Stack 에 대한 정보를 얻을 수 있다.

 

0:000> x leaktrack!*

0a437c74 LeakTrack!g_hLTHeap (<no parameter info>)

0a437c78 LeakTrack!g_TlsSlot (<no parameter info>)

0a437c7c LeakTrack!g_pMemoryManager (<no parameter info>) 

0a437c80 LeakTrack!g_pHandleManager (<no parameter info>)

0a437c84 LeakTrack!g_hUnloadEvent (<no parameter info>)

 

여기서 주목해야 할 global variable g_pMemoryManager 이다. 이놈이 thread stacks에 대한 정보를 메모리의 임의의 부분에 dumping 하고 있다. 그 위치를 찾아가보면,

 

0:000> !list -x "dds " -a "+4 l1" poi(LeakTrack!g_pMemoryManager)+3c

0a6d1f00  00000000

0a6d9244  4ed1c2da gdiplus!GpMemoryBitmap::AllocBitmapData+0x71

0a6d92ac  4ed3d893 gdiplus!GpMalloc+0x16

0a6d944c  747225c2 msctf!cicMemAllocClear+0x10

0a6d951c  74dae45b usp10!UspAllocCache+0x2b

0a6d992c  79e78bb2 mscorwks!EEHeapAlloc+0x12d

0a6d9994  7c9119e6 ntdll!RtlpAllocateDebugInfo+0x49

0a70a6f4  79e7495b mscorwks!EEVirtualAlloc+0x104

0a6d96bc  6e56231f msdtcprx!std::_Allocate<char>+0x17

 

이렇게 보여주는 list 들은 소위 Leaktrack에 의해서 tracking 되는 thread stack의 첫 번째 Funcation에 대한 address 이다. 그리고, 그 중 debugDiag report에서 이미 추정했던 Allocation이 눈에 띄게 많았던 첫번째 Funcation을 확인하여 해당 Funcation Call을 가지고 있는 Thread stack 정보들을 다음과 같이 쫓아갈 수 있다.

 

0:000> !list -x "dds " -a "+4 l1" poi(poi(poi(0a6d9244-4)+8)+4)

0a6d5b64  4ed3d893 gdiplus!GpMalloc+0x16

0b09fce4  4ecd211f gdiplus!GpFaceRealization::AllocateCache+0x21

0a6f0d74  4ecd2274 gdiplus!GpFaceRealization::IsMetricsCached+0x1d

0b0b0854  4ed3d893 gdiplus!GpMalloc+0x16

0b0a8834  4ecd30f5 gdiplus!GpFaceRealization::GetGlyphDataCached+0x1b

0b0ad15c  4ecd3448 gdiplus!GpFaceRealization::GetGlyphPos+0x16f

0b0ab2c4  4ecbd101 gdiplus!GpGraphics::DrawPlacedGlyphs+0x38e

0a6f48fc  7c910732 ntdll!RtlpAllocateFromHeapLookaside+0x42

0a705f54  7c9106ab ntdll!RtlAllocateHeap+0x1c2

0a6d575c  7c9106eb ntdll!RtlAllocateHeap+0xeac

0b0b299c  7c9106ab ntdll!RtlAllocateHeap+0x1c2

0a6d4dd4  7c9106eb ntdll!RtlAllocateHeap+0xeac

0a6fbf24  4ed181a2 gdiplus!GlyphImager::DrawGlyphs+0x14a

0a6d50a4  4ed1299f gdiplus!FullTextImager::DrawGlyphs+0x3da

0b0a66e4  4ecde261 gdiplus!DpRegion::Set+0x3b

0b0a6654  4ed7afad gdiplus!itrp_FDEF+0x190

0b09c974  4ed7654e gdiplus!itrp_Execute+0x1e7

0b0962f4  4ed14bd7 gdiplus!FullTextImager::GdipLscbkDrawGlyphs+0xe4

0a70b4bc  4ed284fa gdiplus!FmtText+0x5ce

0a6f3ffc  4ed285e9 gdiplus!DisplayText+0xc3

0a7023f4  4ed246a5 gdiplus!LsDisplayLine+0xfa

0a706fcc  4ed0fa1b gdiplus!BuiltLine::Draw+0x13

0a6f402c  4ed12eec gdiplus!FullTextImager::RenderLine+0x4d

0a70ade4  4ed13222 gdiplus!FullTextImager::Render+0x28e

0a70abec  4ed13337 gdiplus!FullTextImager::Draw+0xbe

0a70aba4  4ecbcd48 gdiplus!GpGraphics::DrawString+0x362

. . .너무 길어서 중간 생략한다.

 

해당 Windbg command 중에 poi(poi(0a6d9244-4)+8) 에 해당하는 address는 사실 Linked list header 부분에 해당한다. 이 말은 해당 Function Call(여기서는 gdiplus!GpMemoryBitmap::AllocBitmapData 이놈이 되겠다.)을 호출하는 thread stack이 단지 1개가 아니라는 것이다. 그러므로, 다음과 같은 방법을 통해 해당 Function call을 호출하는 다른 Thread stack에 대한 정보를 순차적으로 얻을 수 있다.

 

!list -x "dds " -a "+4 l1" poi(poi(poi(0a6d9244-4)+8)+4)

!list -x "dds " -a "+4 l1" poi(poi(poi(poi(0a6d9244-4)+8))+4)

!list -x "dds " -a "+4 l1" poi(poi(poi(poi(poi(0a6d9244-4)+8)))+4)

. . .

의미 없는 정보가 보일 때까지 계속 확인하는 작업도 가능하겠지만, Leak 여부를 Check Leak code를 찾는 것은 모든 Thread Stack을 뒤지지 않더라고 그 전에 Hint 를 얻을 듯 하다.

by 강세윤 | 2009/04/15 13:53 | Windows debugging | 트랙백 | 덧글(0)
트랙백 주소 : http://byung.egloos.com/tb/4879576
☞ 내 이글루에 이 글과 관련된 글 쓰기 (트랙백 보내기) [도움말]

:         :

:

비공개 덧글

< 이전페이지 다음페이지 >