http://blog.naver.com/techshare/100143182037
위의 링크에서 퍼온글입니다.
Primitive 타입에 대한 배열은 그렇다 치고, 기타 타입들에 대한 배열은 "dumpheap -stat"에서 직접적으로 보이지 않습니다. 이에 대한 이해를 돕기 위해 예제를 다뤄볼까요?
위와 같은 코드로 작성된 프로그램을 windbg 로 로드하고 Thread.Sleep 까지 진행 시킨 후, Heap을 살펴보면 다음과 같은 결과를 얻을 수 있습니다.
예상대로라면, Count == 1, TotalSize == 12 조건을 만족하는 목록 중에서 "ConsoleApplication1.Program[]" 인 항목이 나와야 하는데 없는 것입니다. 왜 그럴까요?
아쉽게도 (네, 아쉽습니다.) Primitive 타입을 제외하고는 모든 타입에 대해서 "System.Object[]" 로 통털어서 출력이 됩니다. 그래서, 다음과 같이 부가적인 검사를 해야 합니다.
그런데, 과연 67개의 인스턴스 중에서 어떤 것이 Program[] 의 개체일까요? (크기를 정확하게 알고 있다면 대상이 좀더 축소가 되겠지만) 현실적으로는 방법이 없습니다. 67개의 개체 모두 일일이 "!do" 명령어를 내려서 확인해야 합니다. 물론, 위의 경우에는 작은 프로그램이니 그나마 쉽게(?) 찾을 수 있겠지만 대형 프로그램의 경우에는 어림도 없는 작업입니다.
대신 그때 그때 상황에 맞게 지름길을 선택하는 정도가 최선일텐데요. 가령 예를 들어서, 위와 같은 예제 코드에서 pgList 인스턴스가 아직 스택에 존재할 거라는 정보가 있기 때문에 이를 찾아나서면 됩니다.
우선, "~*e!CLRStack" 명령어를 이용해서 pgList 를 보유하고 있는 메서드를 어떤 쓰레드에서 실행하고 있는지 확인해야 하는데,
총 5개의 쓰레드가 실행되는 중에 우리가 원하는 메서드(Main)를 실행 중인 것은 0번 인덱스의 0x2490 쓰레드임을 알 수 있습니다. 또한, 현재 windbg의 쓰레드 문맥은 "0:004>" 프롬프트에서 알려주듯이 4번 인덱스의 쓰레드이므로 이후의 명령어를 진행하기에 앞서 쓰레드 전환을 해줍니다.
위와 같이 "~[쓰레드 인덱스 번호]s" 명령어 이외에, windbg 의 "View" / "Processes and Threads (Alt+9)" 메뉴를 이용하여 GUI 윈도우에서 마우스를 이용하여 전환하는 것도 가능합니다.
그 다음, 현재 선택된 쓰레드 문맥에 포함된 stack object 들을 모두 나열시키면,
Name 필드에 친절하게도 어떤 타입의 배열인지 알려주기 때문에 쉽게 Object 주소를 알 수 있습니다. 이후의 Method Table 이나 EEClass 를 찾는 것은 목적에 따라 실행해 주시면 되겠고. ^^
위의 링크에서 퍼온글입니다.
.NET 입장에서, 배열은 결국 System.Array 를 상속받은 개체로 다뤄집니다. 지난 번 이야기에서 "System.Char[]" 개체가 "dumpheap -stat" 명령으로 확인되었던 것을 볼 수 있었는데요.
StringBuilder 에서의 OutOfMemoryException 오류 원인 분석 ; http://www.sysnet.pe.kr/2/0/1171
Primitive 타입에 대한 배열은 그렇다 치고, 기타 타입들에 대한 배열은 "dumpheap -stat"에서 직접적으로 보이지 않습니다. 이에 대한 이해를 돕기 위해 예제를 다뤄볼까요?
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
Program[] pgList = new Program[0];
Thread.Sleep(-1);
}
}
}
위와 같은 코드로 작성된 프로그램을 windbg 로 로드하고 Thread.Sleep 까지 진행 시킨 후, Heap을 살펴보면 다음과 같은 결과를 얻을 수 있습니다.
0:004> .loadby sos clr 0:004> !dumpheap -stat 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 72f86ba8 67 19148 System.Object[] Total 784 objects
예상대로라면, Count == 1, TotalSize == 12 조건을 만족하는 목록 중에서 "ConsoleApplication1.Program[]" 인 항목이 나와야 하는데 없는 것입니다. 왜 그럴까요?
아쉽게도 (네, 아쉽습니다.) Primitive 타입을 제외하고는 모든 타입에 대해서 "System.Object[]" 로 통털어서 출력이 됩니다. 그래서, 다음과 같이 부가적인 검사를 해야 합니다.
0:004> !dumpheap -mt 72f86ba8 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[] Total 67 objects
그런데, 과연 67개의 인스턴스 중에서 어떤 것이 Program[] 의 개체일까요? (크기를 정확하게 알고 있다면 대상이 좀더 축소가 되겠지만) 현실적으로는 방법이 없습니다. 67개의 개체 모두 일일이 "!do" 명령어를 내려서 확인해야 합니다. 물론, 위의 경우에는 작은 프로그램이니 그나마 쉽게(?) 찾을 수 있겠지만 대형 프로그램의 경우에는 어림도 없는 작업입니다.
대신 그때 그때 상황에 맞게 지름길을 선택하는 정도가 최선일텐데요. 가령 예를 들어서, 위와 같은 예제 코드에서 pgList 인스턴스가 아직 스택에 존재할 거라는 정보가 있기 때문에 이를 찾아나서면 됩니다.
우선, "~*e!CLRStack" 명령어를 이용해서 pgList 를 보유하고 있는 메서드를 어떤 쓰레드에서 실행하고 있는지 확인해야 하는데,
0:004> ~*e!CLRStack OS Thread Id: 0x2490 (0) Child SP IP Call Site 003af0dc 7762fd81 [HelperMethodFrame: 003af0dc] System.Threading.Thread.SleepInternal(Int32) 003af128 72f03409 System.Threading.Thread.Sleep(Int32) 003af12c 004b00e9 ConsoleApplication1.Program.Main(System.String[]) [D:\...\Program.cs @ 17] 003af398 73b421bb [GCFrame: 003af398] OS Thread Id: 0x1dd8 (1) 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 OS Thread Id: 0x2c2c (2) Child SP IP Call Site 00fdfdd8 7763014d [DebuggerU2MCatchHandlerFrame: 00fdfdd8] OS Thread Id: 0x312c (3) 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 OS Thread Id: 0x2ee8 (4) 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
총 5개의 쓰레드가 실행되는 중에 우리가 원하는 메서드(Main)를 실행 중인 것은 0번 인덱스의 0x2490 쓰레드임을 알 수 있습니다. 또한, 현재 windbg의 쓰레드 문맥은 "0:004>" 프롬프트에서 알려주듯이 4번 인덱스의 쓰레드이므로 이후의 명령어를 진행하기에 앞서 쓰레드 전환을 해줍니다.
0:004> ~0s
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
위와 같이 "~[쓰레드 인덱스 번호]s" 명령어 이외에, windbg 의 "View" / "Processes and Threads (Alt+9)" 메뉴를 이용하여 GUI 윈도우에서 마우스를 이용하여 전환하는 것도 가능합니다.
그 다음, 현재 선택된 쓰레드 문맥에 포함된 stack object 들을 모두 나열시키면,
0:000> !dso OS Thread Id: 0x2490 (0) ESP/REG Object Name 003AF09C 027dbf80 System.Object[] (System.String[]) 003AF12C 027dbf90 System.Object[] (ConsoleApplication1.Program[]) 003AF130 027dbf90 System.Object[] (ConsoleApplication1.Program[]) 003AF134 027dbf90 System.Object[] (ConsoleApplication1.Program[]) 003AF140 027dbf80 System.Object[] (System.String[]) 003AF214 027dbf80 System.Object[] (System.String[]) 003AF3BC 027dbf80 System.Object[] (System.String[]) 003AF3F0 027dbf80 System.Object[] (System.String[])
Name 필드에 친절하게도 어떤 타입의 배열인지 알려주기 때문에 쉽게 Object 주소를 알 수 있습니다. 이후의 Method Table 이나 EEClass 를 찾는 것은 목적에 따라 실행해 주시면 되겠고. ^^
0:000> !do 027dbf90 Name: System.Object[] MethodTable: 72f86ba8 EEClass: 72d09688 Size: 20(0x14) bytes Array: Rank 1, Number of elements 1, Type CLASS Element Type:ConsoleApplication1.Program Fields: None 0:000> !dumpmt 72f86ba8 EEClass: 72d09688 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> !dumpclass 72d09688 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