성태의 닷넷 이야기
홈 주인
모아 놓은 자료
프로그래밍
질문/답변
사용자 관리
사용자
메뉴
아티클
외부 아티클
유용한 코드
온라인 기능
MathJax 입력기
최근 덧글
[정성태] VT sequences to "CONOUT$" vs. STD_O...
[정성태] NetCoreDbg is a managed code debugg...
[정성태] Evaluating tail call elimination in...
[정성태] What’s new in System.Text.Json in ....
[정성태] What's new in .NET 9: Cryptography ...
[정성태] 아... 제시해 주신 "https://akrzemi1.wordp...
[정성태] 다시 질문을 정리할 필요가 있을 것 같습니다. 제가 본문에...
[이승준] 완전히 잘못 짚었습니다. 댓글 지우고 싶네요. 검색을 해보...
[정성태] 우선 답글 감사합니다. ^^ 그런데, 사실 저 예제는 (g...
[이승준] 수정이 안되어서... byteArray는 BYTE* 타입입니다...
글쓰기
제목
이름
암호
전자우편
HTML
홈페이지
유형
제니퍼 .NET
닷넷
COM 개체 관련
스크립트
VC++
VS.NET IDE
Windows
Team Foundation Server
디버깅 기술
오류 유형
개발 환경 구성
웹
기타
Linux
Java
DDK
Math
Phone
Graphics
사물인터넷
부모글 보이기/감추기
내용
<div style='display: inline'> <h1 style='font-family: Malgun Gothic, Consolas; font-size: 20pt; color: #006699; text-align: center; font-weight: bold'>windbg - GC Heap에서 .NET 타입에 대한 배열을 찾는 방법</h1> <p> .NET 입장에서, 배열은 결국 System.Array를 상속받은 개체로 다뤄집니다. 지난번 이야기에서 "System.Char[]" 개체가 "dumpheap -stat" 명령으로 확인되었던 것을 볼 수 있었는데요.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > StringBuilder에서의 OutOfMemoryException 오류 원인 분석 ; <a target='tab' href='http://www.sysnet.pe.kr/2/0/1171'>http://www.sysnet.pe.kr/2/0/1171</a> </pre> <br /> Primitive 타입에 대한 배열은 그렇다 치고, 기타 타입들에 대한 배열은 "dumpheap -stat"에서 직접적으로 보이지 않습니다. 이에 대한 이해를 돕기 위해 예제를 다뤄볼까요?<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > namespace ConsoleApplication1 { class Program { static void Main(string[] args) { <span style='color: blue; font-weight: bold'>Program[] pgList = new Program[0];</span> Thread.Sleep(-1); } } } </pre> <br /> 위와 같은 코드로 작성된 프로그램을 windbg로 로드하고 Thread.Sleep까지 진행 시킨 후, Heap을 살펴보면 다음과 같은 결과를 얻을 수 있습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > 0:004> <span style='color: blue; font-weight: bold'>.loadby sos clr</span> 0:004> <span style='color: blue; font-weight: bold'>!dumpheap -stat</span> total 0 objects Statistics: MT Count TotalSize Class Name 72fd5f10 1 12 System.Security.HostSecurityManager 72fd5938 1 12 System.Collections.Generic.ObjectEqualityComparer`1[[System.Type, mscorlib]] 72fd4b44 1 12 System.Security.Permissions.ReflectionPermission 72fd4a60 1 12 System.Security.Permissions.FileDialogPermission 72fd4980 1 12 System.Security.PolicyManager 72fd4898 1 12 System.Security.SecurityDocument 72fd27fc 1 12 System.Collections.Generic.GenericEqualityComparer`1[[System.String, mscorlib]] 72fd5ec8 1 16 System.Security.Policy.Evidence+EvidenceLockHolder 72fd4bf4 1 16 System.Security.Permissions.UIPermission 72fd4640 1 16 System.Security.Util.Parser ... [생략] ... 72fcf92c 469 15652 System.String <span style='color: blue; font-weight: bold'>72f86ba8 67 19148 System.Object[]</span> Total 784 objects </pre> <br /> 예상대로라면, Count == 1, TotalSize == 12 조건을 만족하는 목록 중에서 "ConsoleApplication1.Program[]"인 항목이 나와야 하는데 없는 것입니다. 왜 그럴까요?<br /> <br /> 아쉽게도 (네, 아쉽습니다.) Primitive 타입을 제외하고는 모든 타입에 대해서 "System.Object[]"로 통털어서 출력이 됩니다. 그래서, 다음과 같이 부가적인 검사를 해야 합니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > 0:004> <span style='color: blue; font-weight: bold'>!dumpheap -mt 72f86ba8</span> Address MT Size 027d18e4 72f86ba8 88 027d5578 72f86ba8 56 027d55d0 72f86ba8 20 027d55e4 72f86ba8 32 027d5604 72f86ba8 20 ... [생략] ... 037d1010 72f86ba8 4096 037d2020 72f86ba8 528 037d2240 72f86ba8 4096 total 0 objects Statistics: MT Count TotalSize Class Name 72f86ba8 67 19148 System.Object[] <span style='color: blue; font-weight: bold'>Total 67 objects</span> </pre> <br /> 그런데, 과연 67개의 인스턴스 중에서 어떤 것이 Program[]의 개체일까요? (크기를 정확하게 알고 있다면 대상이 좀 더 축소가 되겠지만) 현실적으로는 방법이 없습니다. 67개의 개체 모두 일일이 "!do" 명령어를 내려서 확인해야 합니다. 물론, 위의 경우에는 작은 프로그램이니 그나마 쉽게(?) 찾을 수 있겠지만 대형 프로그램의 경우에는 어림도 없는 작업입니다.<br /> <br /> 대신 그때그때 상황에 맞게 지름길을 선택하는 정도가 최선일 텐데요. 가령 예를 들어서, 위와 같은 예제 코드에서 pgList 인스턴스가 아직 스택에 존재할 거라는 정보가 있기 때문에 이를 찾아나서면 됩니다.<br /> <br /> 우선, "~*e!CLRStack" 명령어를 이용해서 pgList를 보유하고 있는 메서드를 어떤 스레드에서 실행하고 있는지 확인해야 하는데,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > <span style='color: blue; font-weight: bold'>0:004> ~*e!CLRStack</span> <span style='color: blue; font-weight: bold'>OS Thread Id: 0x2490 (0)</span> Child SP IP Call Site 003af0dc 7762fd81 [HelperMethodFrame: 003af0dc] System.Threading.Thread.SleepInternal(Int32) 003af128 72f03409 System.Threading.Thread.Sleep(Int32) 003af12c 004b00e9 <span style='color: blue; font-weight: bold'>ConsoleApplication1.Program.Main(System.String[])</span> [D:\...\Program.cs @ 17] 003af398 73b421bb [GCFrame: 003af398] <span style='color: blue; font-weight: bold'>OS Thread Id: 0x1dd8 (1)</span> Unable to walk the managed stack. The current thread is likely not a managed thread. You can run !threads to get a list of managed threads in the process <span style='color: blue; font-weight: bold'>OS Thread Id: 0x2c2c (2)</span> Child SP IP Call Site 00fdfdd8 7763014d [DebuggerU2MCatchHandlerFrame: 00fdfdd8] <span style='color: blue; font-weight: bold'>OS Thread Id: 0x312c (3)</span> Unable to walk the managed stack. The current thread is likely not a managed thread. You can run !threads to get a list of managed threads in the process <span style='color: blue; font-weight: bold'>OS Thread Id: 0x2ee8 (4)</span> Unable to walk the managed stack. The current thread is likely not a managed thread. You can run !threads to get a list of managed threads in the process </pre> <br /> 총 5개의 스레드가 실행되는 중에 우리가 원하는 메서드(Main)를 실행 중인 것은 0번 인덱스의 0x2490 스레드임을 알 수 있습니다. 또한, 현재 windbg의 스레드 문맥은 "0:004>" 프롬프트에서 알려주듯이 4번 인덱스의 스레드이므로 이후의 명령어를 진행하기에 앞서 스레드 전환을 해줍니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > 0:004> <span style='color: blue; font-weight: bold'>~0s</span> eax=00000000 ebx=00000001 ecx=00000000 edx=00000000 esi=003aeff8 edi=00000000 eip=7762fd81 esp=003aefb4 ebp=003af01c iopl=0 nv up ei pl zr na pe nc cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000246 ntdll!NtDelayExecution+0x15: 7762fd81 83c404 add esp,4 </pre> <br /> 위와 같이 "~[스레드 인덱스 번호]s" 명령어 이외에, windbg의 "View" / "Processes and Threads (Alt+9)" 메뉴를 이용하여 GUI 윈도우에서 마우스를 이용하여 전환하는 것도 가능합니다.<br /> <br /> <img alt='how_to_find_type_array_mt_1.png' src='/SysWebRes/bbs/how_to_find_type_array_mt_1.png' /><br /> <br /> 그다음, 현재 선택된 스레드 문맥에 포함된 stack object들을 모두 나열시키면,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > <span style='color: blue; font-weight: bold'>0:000> !dso</span> OS Thread Id: 0x2490 (0) ESP/REG Object Name 003AF09C 027dbf80 System.Object[] (System.String[]) 003AF12C <span style='color: blue; font-weight: bold'>027dbf90</span> System.Object[] (<span style='color: blue; font-weight: bold'>ConsoleApplication1.Program[]</span>) 003AF130 <span style='color: blue; font-weight: bold'>027dbf90</span> System.Object[] (<span style='color: blue; font-weight: bold'>ConsoleApplication1.Program[]</span>) 003AF134 <span style='color: blue; font-weight: bold'>027dbf90</span> System.Object[] (<span style='color: blue; font-weight: bold'>ConsoleApplication1.Program[]</span>) 003AF140 027dbf80 System.Object[] (System.String[]) 003AF214 027dbf80 System.Object[] (System.String[]) 003AF3BC 027dbf80 System.Object[] (System.String[]) 003AF3F0 027dbf80 System.Object[] (System.String[]) </pre> <br /> Name 필드에 친절하게도 어떤 타입의 배열인지 알려주기 때문에 쉽게 Object 주소를 알 수 있습니다. 이후의 Method Table이나 EEClass를 찾는 것은 목적에 따라 실행해 주시면 되겠고. ^^<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > 0:000> <span style='color: blue; font-weight: bold'>!do 027dbf90 </span> Name: System.Object[] MethodTable: <span style='color: blue; font-weight: bold'>72f86ba8</span> EEClass: <span style='color: blue; font-weight: bold'>72d09688</span> Size: 20(0x14) bytes Array: Rank 1, Number of elements 1, Type CLASS Element Type:ConsoleApplication1.Program Fields: None 0:000> <span style='color: blue; font-weight: bold'>!dumpmt 72f86ba8</span> EEClass: <span style='color: blue; font-weight: bold'>72d09688</span> Module: 72cb1000 Name: System.Object[] mdToken: 02000000 File: C:\Windows\Microsoft.Net\assembly\GAC_32\mscorlib\v4.0_4.0.0.0__b77a5c561934e089\mscorlib.dll BaseSize: 0x10 ComponentSize: 0x4 Slots in VTable: 28 Number of IFaces in IFaceMap: 6 0:000> <span style='color: blue; font-weight: bold'>!dumpclass 72d09688</span> Class Name: System.Object[] mdToken: 02000000 File: C:\Windows\Microsoft.Net\assembly\GAC_32\mscorlib\v4.0_4.0.0.0__b77a5c561934e089\mscorlib.dll Parent Class: 72d08ad8 Module: 72cb1000 Method Table: 72f86ba8 Vtable Slots: 18 Total Method Slots: 1c Class Attributes: 2101 Transparency: Transparent NumInstanceFields: 0 NumStaticFields: 0 </pre> </p><br /> <br /><hr /><span style='color: Maroon'>[이 글에 대해서 여러분들과 의견을 공유하고 싶습니다. 틀리거나 미흡한 부분 또는 의문 사항이 있으시면 언제든 댓글 남겨주십시오.]</span> </div>
첨부파일
스팸 방지용 인증 번호
3837
(왼쪽의 숫자를 입력해야 합니다.)