Microsoft MVP성태의 닷넷 이야기
디버깅 기술: 66. windbg 분석 사례 - cpu 100% 현상 (1) [링크 복사], [링크+제목 복사]
조회: 21526
글쓴 사람
정성태 (techsharer at outlook.com)
홈페이지
첨부 파일
 
(연관된 글이 3개 있습니다.)

windbg 분석 사례 - cpu 100% 현상 (1)

CPU High 현상이 발생했습니다. 테스트 응용 프로그램 중 불규칙하게 재현되는 것이라서 CPU High 현상이 나온 EXE 프로세스가 살아 있는 동안 최대한 정보를 수집했습니다.

우선, Process Explorer를 통해 Thread ID == 7824가 (아마도) 무한 루프에 빠져 CPU를 점유하는 현상이 보이는 것을 확인했습니다. (2vCPU였으므로 50%면 무한 루프!)

windbg_cpu_high_1.png

이제 작업 관리자를 이용해 해당 프로세스의 풀 덤프를 뜨고, "C:\Users\[계정]\AppData\Local\Temp\...dmp"에 저장된 덤프 파일을 간단하게 Debug Diagnostic Tool v2 Update 1 도구를 이용해 분석해 보면 다음과 같이 Top 5 스레드 시간이 출력되어 단독범 소행임을 알 수 있습니다. (windbg에서는 !runaway 명령어로 CPU 소비 시간을 알 수 있습니다.)

Top 5 Threads by CPU time
 Note - Times include both user mode and kernel mode for each thread 

Thread ID: 24
    Total CPU Time: 2 day(s) 09:41:55.936
    Entry Point for Thread: mscorwks!Thread::intermediateThreadProc

Thread ID: 15
    Total CPU Time: 00:01:39.593
    Entry Point for Thread: mscorwks!Thread::intermediateThreadProc
	
Thread ID: 9
    Total CPU Time: 00:00:20.937
    Entry Point for Thread: mscorwks!Thread::intermediateThreadProc
	
Thread ID: 7
    Total CPU Time: 00:00:16.436
    Entry Point for Thread: mscorwks!Thread::intermediateThreadProc

Thread ID: 8
    Total CPU Time: 00:00:13.999
    Entry Point for Thread: mscorwks!Thread::intermediateThreadProc

Debug Diagnostic Tool 도구가 생성하는 리포트 웹 페이지에는 위에서 "Thread ID: 24"는 링크로 제공됩니다. 따라서 그것을 클릭하면 상세 정보로 넘어가는데요. 대충 아래와 같이 나왔습니다.

Thread 24 - System ID 7824

Entry point  mscorwks!Thread::intermediateThreadProc
Create time  2014-12-17 오전 12:40:30
Time spent in user mode  2 Days 08:34:37.515
Time spent in kernel mode  0 Days 01:07:18.421

This thread is not fully resolved and may or may not be a problem. Further analysis of these threads may be required.
.NET Call Stack

Function

MyApp.util.IntBoundSet.put(Int32)+1c8
MyApp.AgentText.addServiceName(Int32, System.String)+32
System.Runtime.Remoting.Channels.BinaryServerFormatterSink.ProcessMessage(System.Runtime.Remoting.Channels.IServerChannelSinkStack, System.Runtime.Remoting.Messaging.IMessage, System.Runtime.Remoting.Channels.ITransportHeaders, System.IO.Stream, System.Runtime.Remoting.Messaging.IMessage ByRef, System.Runtime.Remoting.Channels.ITransportHeaders ByRef, System.IO.Stream ByRef)+8a2
System.Runtime.Remoting.Channels.Ipc.IpcServerTransportSink.ServiceRequest(System.Object)+176
System.Runtime.Remoting.Channels.SocketHandler.ProcessRequestNow()+34
System.Runtime.Remoting.Channels.RequestQueue.ProcessNextRequest(System.Runtime.Remoting.Channels.SocketHandler)+19
System.Runtime.Remoting.Channels.SocketHandler.BeginReadMessageCallback(System.IAsyncResult)+b8
System.Runtime.Remoting.Channels.Ipc.IpcPort.AsyncFSCallback(UInt32, UInt32, System.Threading.NativeOverlapped*)+50
System.Threading._IOCompletionCallback.PerformIOCompletionCallback(UInt32, UInt32, System.Threading.NativeOverlapped*)+7c

보시는 바와 같이 IntBoundSet 클래스의 put 메서드를 실행 중에 있습니다. 사실 이 정보만으로는 cpu high 현상이 왜 발생하는지 알 수 없습니다. 그래서 Debug Diagnostic Tool v2 Update 1 도구에서는 덤프를 여러 개 떠서 비교 분석하는 방법을 제공하는데요. 그렇긴 해도 역시나 정확한 반복 영역이나 왜 그런 현상이 발생했는지는 알 수 없습니다. 반복 덤프된 것을 통해 IntBoundSet.put 메서드가 항상 상위에 있다는 점을 통해 put 메서드 내부에서만 무한 루프가 있을 수 있다는 (100% 확실이 아닌) 가능성을 확인하는 정도에 불과합니다.

여기까지만 확인하고 (현실에서는 불가능한 경우가 많지만, 시간 관계상) 이제 실행 중인 EXE 프로세스에 windbg로 attach를 시켜서 확인해 봤습니다. SOS를 로드하고,

0:172> .loadby sos mscorwks

문제가 있는 스레드의 ID(0x1e90 == 7824)를 확인하고,

0:172> !threads
ThreadCount: 169
UnstartedThread: 0
BackgroundThread: 168
PendingThread: 0
DeadThread: 0
Hosted Runtime: no
                                      PreEmptive   GC Alloc           Lock
       ID OSID ThreadOBJ    State     GC       Context       Domain   Count APT Exception
   0    1 14d4 00664a78      a020 Enabled  00000000:00000000 005e6f08     0 MTA
...[생략]...
  24   17 1e90 05841320   880b220 Disabled 00000000:00000000 005e6f08     1 MTA (Threadpool Completion Port)
...[생략]...
 170   a9 1348 05b4fba0   880b220 Enabled  0169eb58:016a09fc 005e6f08     0 MTA (Threadpool Completion Port)

문맥 전환을 한 다음,

0:172> ~24s
eax=00000000 ebx=00000000 ecx=00009c47 edx=00000000 esi=014e5f3c edi=07cbecd0
eip=0508a92a esp=07cbec94 ebp=07cbece0 iopl=0         nv up ei pl zr na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000246
0508a92a 0f95c0          setne   al

콜스택으로 해당 스레드가 맞는지 확인합니다.

0:024> !clrstack
OS Thread Id: 0x1e90 (24)
ESP       EIP     
07cbec94 0508a92a MyApp.util.IntBoundSet.put(Int32)
07cbece8 05068042 MyApp.AgentText.addServiceName(Int32, System.String)
07cbeeec 04afc9ea System.Runtime.Remoting.Channels.BinaryServerFormatterSink.ProcessMessage(System.Runtime.Remoting.Channels.IServerChannelSinkStack, System.Runtime.Remoting.Messaging.IMessage, System.Runtime.Remoting.Channels.ITransportHeaders, System.IO.Stream, System.Runtime.Remoting.Messaging.IMessage ByRef, System.Runtime.Remoting.Channels.ITransportHeaders ByRef, System.IO.Stream ByRef)
07cbef90 050ec3fe System.Runtime.Remoting.Channels.Ipc.IpcServerTransportSink.ServiceRequest(System.Object)
07cbefec 04afa8dc System.Runtime.Remoting.Channels.SocketHandler.ProcessRequestNow()
07cbf01c 04afa7b1 System.Runtime.Remoting.Channels.RequestQueue.ProcessNextRequest(System.Runtime.Remoting.Channels.SocketHandler)
07cbf024 04afa3c0 System.Runtime.Remoting.Channels.SocketHandler.BeginReadMessageCallback(System.IAsyncResult)
07cbf054 050ec198 System.Runtime.Remoting.Channels.Ipc.IpcPort.AsyncFSCallback(UInt32, UInt32, System.Threading.NativeOverlapped*)
07cbf06c 04af7f54 System.Threading._IOCompletionCallback.PerformIOCompletionCallback(UInt32, UInt32, System.Threading.NativeOverlapped*)
07cbf210 6e801b4c [GCFrame: 07cbf210] 

PDB 파일이 있는 경우, 소스 코드의 라인 정보까지 나옵니다.

0:021> !clrstack
OS Thread Id: 0x1120 (21)
Child SP         IP               Call Site
00000000204fe0f8 0000000076fb05fa [RedirectedThreadFrame: 00000000204fe0f8] 
00000000204fe190 000007ff007e8882 MyApp.util.IntBoundSet.put(Int32) [d:\TestApp\IntBoundSet.cs @ 74]
00000000204fe2a0 000007ff007fdd50 MyApp.AgentText.addServiceName(Int32, System.String) [d:\TestApp\AgentText.cs @ 76]
00000000204fe650 000007ff006bec3e System.Runtime.Remoting.Channels.BinaryServerFormatterSink.ProcessMessage(System.Runtime.Remoting.Channels.IServerChannelSinkStack, System.Runtime.Remoting.Messaging.IMessage, System.Runtime.Remoting.Channels.ITransportHeaders, System.IO.Stream, System.Runtime.Remoting.Messaging.IMessage ByRef, System.Runtime.Remoting.Channels.ITransportHeaders ByRef, System.IO.Stream ByRef)
00000000204fe740 000007ff00a14303 System.Runtime.Remoting.Channels.Ipc.IpcServerTransportSink.ServiceRequest(System.Object)
00000000204fe840 000007ff006bbf93 System.Runtime.Remoting.Channels.SocketHandler.ProcessRequestNow()
00000000204fe880 000007ff006bb66e System.Runtime.Remoting.Channels.SocketHandler.BeginReadMessageCallback(System.IAsyncResult)
00000000204fe8e0 000007ff006b7226 System.Threading._IOCompletionCallback.PerformIOCompletionCallback(UInt32, UInt32, System.Threading.NativeOverlapped*)
00000000204feb80 000007fef8d9c9e4 [GCFrame: 00000000204feb80] 
00000000204fed50 000007fef8d9c9e4 [DebuggerU2MCatchHandlerFrame: 00000000204fed50] 

이처럼 PDB 파일이 있으면 디버깅이 매우 쉽습니다. F10/F11 키를 눌러 Step Over/Step Into 기능으로 기계어 코드를 해당 스레드에서 한 단계씩 실행하면서 소스 코드 위치를 확인하면 어떤 코드에서 무한 루프가 형성되는지 알 수 있습니다. 좀 더 문제를 자세하게 분석해 볼까요? ^^ 이제 put 메서드의 MethodDesc 식별자와 기계어로 번역된 어셈블리의 주소를 알아내고,

0:024> !name2ee MyApp!MyApp.util.IntBoundSet.put
Module: 04a5f3dc (MyApp.dll)
Token: 0x06001241
MethodDesc: 04ccbe74
Name: MyApp.util.IntBoundSet.put(Int32)
JITTED Code Address: 0508a788

이를 통해 IL 코드 정보를 얻을 수 있지만,

0:024> !dumpil 04ccbe74
ilAddr = 6ba5f404
IL_0000: nop 
IL_0001: ldarg.0 
IL_0002: dup 
IL_0003: stloc.s VAR OR ARG 5
IL_0005: call System.Threading.Monitor::Enter 
IL_000a: nop 
.try
{
  IL_000b: nop 
  IL_000c: ldarg.0 
  IL_000d: ldfld MyApp.util.IntBoundSet::table
  IL_0012: stloc.0 
...[생략]... 
  IL_01a7: add 
  IL_01a8: stfld MyApp.util.IntBoundSet::count
  IL_01ad: ldc.i4.1 
  IL_01ae: stloc.s VAR OR ARG 4
  IL_01b0: leave.s IL_01bb
} // end .try
.finally
{
  IL_01b2: ldloc.s VAR OR ARG 5
  IL_01b4: call System.Threading.Monitor::Exit 
  IL_01b9: nop 
  IL_01ba: endfinally 
} // end .finally
IL_01bb: nop 
IL_01bc: ldloc.s VAR OR ARG 4
IL_01be: ret 

사실 IL 코드 정보가 그다지 도움이 되진 않습니다. 왜냐하면 실행은 기계어를 바탕으로 하기 때문에 IL 코드는 소스코드를 가지고 있지 않을 때 문제 분석을 위해 필요한 정도일 뿐입니다. (물론, 소스코드가 없을 때는 유용합니다.) 중요한 것은 기계어 정보인데, "JITTED Code Address: 0508a788" 값을 이용해 다음과 같이 구할 수 있습니다.

0:024> !U 0508a788

Normal JIT generated code
MyApp.util.IntBoundSet.put(Int32)
Begin 0508a788, size 353
>>> 0508a788 55              push    ebp
0508a789 8bec            mov     ebp,esp
0508a78b 57              push    edi
0508a78c 56              push    esi
0508a78d 53              push    ebx
0508a78e 83ec40          sub     esp,40h
0508a791 8bf1            mov     esi,ecx
0508a793 8d7db8          lea     edi,[ebp-48h]
0508a796 b90e000000      mov     ecx,0Eh
0508a79b 33c0            xor     eax,eax
0508a79d f3ab            rep stos dword ptr es:[edi]
0508a79f 8bce            mov     ecx,esi
0508a7a1 33c0            xor     eax,eax
0508a7a3 8945e8          mov     dword ptr [ebp-18h],eax
0508a7a6 894dcc          mov     dword ptr [ebp-34h],ecx
0508a7a9 8955dc          mov     dword ptr [ebp-24h],edx
0508a7ac 833d94f5a50400  cmp     dword ptr ds:[4A5F594h],0
0508a7b3 7405            je      0508a7ba
0508a7b5 e8cf609f69      call    mscorwks!JIT_DbgIsJustMyCode (6ea80889)
0508a7ba 33d2            xor     edx,edx
0508a7bc 8955c4          mov     dword ptr [ebp-3Ch],edx
0508a7bf c745d000000000  mov     dword ptr [ebp-30h],0
0508a7c6 33d2            xor     edx,edx
0508a7c8 8955b8          mov     dword ptr [ebp-48h],edx
0508a7cb 33d2            xor     edx,edx
0508a7cd 8955c8          mov     dword ptr [ebp-38h],edx
0508a7d0 33d2            xor     edx,edx
0508a7d2 8955d8          mov     dword ptr [ebp-28h],edx
0508a7d5 33d2            xor     edx,edx
0508a7d7 8955c0          mov     dword ptr [ebp-40h],edx
0508a7da 33d2            xor     edx,edx
0508a7dc 8955bc          mov     dword ptr [ebp-44h],edx
0508a7df c745d400000000  mov     dword ptr [ebp-2Ch],0
0508a7e6 90              nop
0508a7e7 8b45cc          mov     eax,dword ptr [ebp-34h]
0508a7ea 8945bc          mov     dword ptr [ebp-44h],eax
0508a7ed 8b4dbc          mov     ecx,dword ptr [ebp-44h]
0508a7f0 e8c0837769      call    mscorwks!JIT_MonEnterWorker (6e802bb5)
0508a7f5 90              nop
0508a7f6 90              nop
0508a7f7 8b45cc          mov     eax,dword ptr [ebp-34h]
0508a7fa 8b4004          mov     eax,dword ptr [eax+4]
0508a7fd 8945c8          mov     dword ptr [ebp-38h],eax
0508a800 90              nop
0508a801 e938010000      jmp     0508a93e

...[생략]...

무한 루프이기 때문에 F10/F11키를 누르면서 !U 출력 결과와 함께 비교하며 실행하다 보면 무한 반복이 시작되는 코드 지점을 그리 어렵지 않게 찾아낼 수 있습니다. 이렇게 해서 일단 기계어 상에서 반복 루프 지점을 알아냈는데, 그렇다면 과연 소스 코드상에서는 어느 지점에 해당하는 것일까요?

제가 테스트 하던 당시의 MyApp.util.IntBoundSet.put 코드는 다음과 같습니다. (라인 수를 줄이기 위해 if 문 블록 코드를 가능한 없앴습니다.)

public bool put(int key)
{
    lock (this)
    {
        Entry[] tab = table;
        int index;
        Entry e, prev;

        while (count >= capacity)
        {
            index = (tail.key & 0x7fffffff) % tab.Length;
            e = tab[index];
            prev = null;

            for (; e != null; e = e.hash_next)
            {
                if (e == tail)
                {
                    if (prev != null) prev.hash_next = e.hash_next;
                    else tab[index] = e.hash_next;

                    if (tail.link_next == null) head = tail = null;
                    else tail = tail.link_next;

                    count--;
                    e.hash_next = e.link_next = null;
                    break;
                }

                prev = e;
            }
        }

        index = (key & 0x7fffffff) % tab.Length;
        for (e = tab[index]; e != null; e = e.hash_next)
        {
            if (e.key == key) return false;
        }

        e = new Entry();
        e.key = key;
        e.hash_next = tab[index];
        tab[index] = e;

        if (head == null) head = tail = e;
        else 
        {
            head.link_next = e;
            head = e;
        }
        count++;
        return true;
    }
}

어떤가요? 기계어 코드만 보면서 소스 코드의 위치를 알 수 있을까요? ^^ 어쨌든, 제 경우에는 기계어의 0508a806 지점이 최초 무한 루프의 시작지점이었습니다. 그럼, 거기부터 시작해 볼까요? ^^

0508a806 90              nop 
0508a807 8b45cc          mov     eax,dword ptr [ebp-34h] 

우선 [ebp-34h]에 어떤 값이 들어있는지 확인해야 하는데요. 닷넷 프로그램이기 때문에 대개의 경우 !dumpobject 명령을 내려보면 알 수 있습니다.

0:024> !do [ebp-34h]
Name: MyApp.util.IntBoundSet
MethodTable: 04ccbea4
EEClass: 04e57534
Size: 28(0x1c) bytes
 (C:\Windows\assembly\GAC_MSIL\MyApp\5.0.5.0__cc862a9be44088eb\MyApp.dll)
Fields:
      MT    Field   Offset                 Type VT     Attr    Value Name
001b84e4  4000fc6        4      System.Object[]  0 instance 0257a518 table
005bedf0  4000fc7       10         System.Int32  1 instance    20000 count
005bedf0  4000fc8       14         System.Int32  1 instance    20000 capacity
04ccc030  4000fc9        8 ...IntBoundSet+Entry  0 instance 01538510 tail
04ccc030  4000fca        c ...IntBoundSet+Entry  0 instance 015e70a8 head

이 명령이 좋은 것은, 해당 인스턴스의 Offset 위치에 대한 필드의 이름 뿐만 아니라 그것에 대한 메모리 상의 값(Value)도 알 수 있다는 점입니다. 어쨌든, [ebp-34h] 값은 MyApp.util.IntBoundSet이기 때문에 일단 this 포인터 값이라고 보면 됩니다. 정리해 보면 현재 eax 값에 this 포인터가 있는 것입니다. F10 키를 눌러 연 이은 기계어 코드를 실행하면서 분석을 계속해 보겠습니다.

0508a80a 8b4008          mov     eax,dword ptr [eax+8]    ; IntBoundSet.tail (0x01538510 == MyApp.util.IntBoundSet+Entry)

eax에는 this포인터가 들어가 있고 거기에 +8 옵셋위치로 넘어갔으니 "!do [ebp-34h]"로 출력된 MyApp.util.IntBoundSet의 구조를 보면 "Offset" 컬럼의 값이 8에 해당하는 필드가 "tail"임을 알 수 있습니다. 따라서, 위의 코드는 eax 레지스터에 this.tail 값을 대입하게 됩니다. tail은 IntBoundSet 클래스의 내부 클래스로 이렇게 정의되어 있습니다.

public sealed class IntBoundSet
{
	//...[생략]...

	public sealed class Entry
	{
		public int key;
		public Entry link_next, hash_next;
	}

	//...[생략]...
}

역시 !do 명령으로 분석해 보면 다음과 같이 나옵니다.

0:024> !do [eax+8]
Name: MyApp.util.IntBoundSet+Entry
MethodTable: 04ccc030
EEClass: 04e576c8
Size: 20(0x14) bytes
 (C:\Windows\assembly\GAC_MSIL\MyApp\5.0.5.0__cc862a9be44088eb\MyApp.dll)
Fields:
      MT    Field   Offset                 Type VT     Attr    Value Name
005bedf0  4000fcb        c         System.Int32  1 instance 494085374 key
04ccc030  4000fcc        4 ...IntBoundSet+Entry  0 instance 01538524 link_next
04ccc030  4000fcd        8 ...IntBoundSet+Entry  0 instance 00000000 hash_next

대충 어떤 식으로 분석하는 지 이제 감이 오시죠? ^^ 그리하여 다음의 코드는 쉽게 이해가 됩니다.

0508a80d 8b400c          mov     eax,dword ptr [eax+0Ch]  ; MyApp.util.IntBoundSet+Entry.key
0508a810 25ffffff7f      and     eax,7FFFFFFFh  ; // (tail.key & 0x7fffffff)

eax에 있는 값이 this.tail이고, 거기에 다시 offset 값으로 0c를 더했으니 "MyApp.util.IntBoundSet+Entry"의 구조에 보면 그것이 "key" 필드임을 알 수 있습니다. 결과적으로 eax에는 this.tail.key값이 들어갑니다. 그리곤 eax와 7FFFFFFFh 값으로 and 연산을 하니... 오호~~~ 이 정도만 해도 소스 코드에서 유사한 라인이 나옵니다.

public bool put(int key)
{
    lock (this)
    {
        Entry[] tab = table;
        int index;
        Entry e, prev;

        while (count >= capacity)
        {
            index = (tail.key & 0x7fffffff) % tab.Length;
            e = tab[index];
            prev = null;
	...[생략]...
}

대략 이런 식으로 분석하면 무한 루프의 끝 지점도 찾아낼 수 있고, 아울러 각 변수들의 값도 확인할 수 있으므로 도대체 어떤 상태에서 무한 루프가 발생하는 것인지 그 원인을 밝힐 수 있습니다.

그 외에 도움이 될 수 있는 것으로는 !clrstack에서 "-p" 옵션을 이용해 스레드 스택 프레임마다 전달된 인자의 값을 확인하는 방법이 있습니다.

0:024> !clrstack -p
OS Thread Id: 0x1e90 (24)
ESP       EIP     
07cbec94 0508a821 MyApp.util.IntBoundSet.put(Int32)
    PARAMETERS:
        this = 0x014e4f30
        key = 0x77b96530

07cbece8 05068042 MyApp.AgentText.addServiceName(Int32, System.String)
    PARAMETERS:
        hash = 0x77b96530
        value = 0x015e7454

07cbeeec 04afc9ea System.Runtime.Remoting.Channels.BinaryServerFormatterSink.ProcessMessage(System.Runtime.Remoting.Channels.IServerChannelSinkStack, System.Runtime.Remoting.Messaging.IMessage, System.Runtime.Remoting.Channels.ITransportHeaders, System.IO.Stream, System.Runtime.Remoting.Messaging.IMessage ByRef, System.Runtime.Remoting.Channels.ITransportHeaders ByRef, System.IO.Stream ByRef)
07cbef90 050ec3fe System.Runtime.Remoting.Channels.Ipc.IpcServerTransportSink.ServiceRequest(System.Object)
    PARAMETERS:
        this = 0x014d1250
        state = <no data>

07cbefec 04afa8dc System.Runtime.Remoting.Channels.SocketHandler.ProcessRequestNow()
    PARAMETERS:
        this = 0x015b3acc

07cbf01c 04afa7b1 System.Runtime.Remoting.Channels.RequestQueue.ProcessNextRequest(System.Runtime.Remoting.Channels.SocketHandler)
    PARAMETERS:
        this = <no data>
        sh = <no data>

07cbf024 04afa3c0 System.Runtime.Remoting.Channels.SocketHandler.BeginReadMessageCallback(System.IAsyncResult)
    PARAMETERS:
        this = <no data>
        ar = <no data>

07cbf054 050ec198 System.Runtime.Remoting.Channels.Ipc.IpcPort.AsyncFSCallback(UInt32, UInt32, System.Threading.NativeOverlapped*)
    PARAMETERS:
        errorCode = <no data>
        numBytes = <no data>
        pOverlapped = <no data>

07cbf06c 04af7f54 System.Threading._IOCompletionCallback.PerformIOCompletionCallback(UInt32, UInt32, System.Threading.NativeOverlapped*)
    PARAMETERS:
        errorCode = <no data>
        numBytes = <no data>
        pOVERLAP = <no data>

07cbf210 6e801b4c [GCFrame: 07cbf210] 

위의 출력 결과에서 2번째 addServiceName 메서드에 전달된 인자는 this가 없는 것으로 봐서 static 메서드입니다. hash 인자는 int32 값이므로 더 검사할 필요가 없고, System.String 타입의 value(0x015e7454)는 역시 !dumpobject 명령어로 조사할 수 있습니다.

0:024> !do 0x015e7454
Name: System.String
MethodTable: 001b914c
EEClass: 004b2824
Size: 110(0x6e) bytes
 (C:\Windows\assembly\GAC_32\mscorlib\2.0.0.0__b77a5c561934e089\mscorlib.dll)
String: ThisIsTestThisIsTestThisIsTestThisIsTestThis
Fields:
      MT    Field   Offset                 Type VT     Attr    Value Name
005bedf0  4000096        4         System.Int32  1 instance       47 m_arrayLength
005bedf0  4000097        8         System.Int32  1 instance       44 m_stringLength
001b9870  4000098        c          System.Char  1 instance       54 m_firstChar
001b914c  4000099       10        System.String  0   shared   static Empty
    >> Domain:Value  005e6f08:014b1700 <<
005b4620  400009a       14        System.Char[]  0   shared   static WhitespaceChars
    >> Domain:Value  005e6f08:014b1714 <<

이 정도만 알고 계시면, 향후 cpu 100% 현상을 보이게 될 여러분의 EXE 프로세스를 가볍게 요리하실 수 있을 것입니다. ^^




[이 글에 대해서 여러분들과 의견을 공유하고 싶습니다. 틀리거나 미흡한 부분 또는 의문 사항이 있으시면 언제든 댓글 남겨주십시오.]

[연관 글]






[최초 등록일: ]
[최종 수정일: 7/10/2021]

Creative Commons License
이 저작물은 크리에이티브 커먼즈 코리아 저작자표시-비영리-변경금지 2.0 대한민국 라이센스에 따라 이용하실 수 있습니다.
by SeongTae Jeong, mailto:techsharer at outlook.com

비밀번호

댓글 작성자
 



2015-01-04 03시16분
Windbg - 비정상 종료된 닷넷 프로그램의 StackTrace 에서 보여지는 offset 값 의미
; http://www.sysnet.pe.kr/2/0/1095

ICorDebugILFrame::GetIP
; http://msdn.microsoft.com/en-us/library/ms230263(v=vs.110).aspx

IL offset 0 vs. Native offset 0
; http://blogs.msdn.com/b/jmstall/archive/2005/09/08/il-vs-native-offset-0.aspx

정성태

[1]  2  3  4  5  6  7  8  9  10  11  12  13  14  15  ...
NoWriterDateCnt.TitleFile(s)
13600정성태4/18/2024253닷넷: 2242. C# - 관리 스레드와 비관리 스레드
13599정성태4/17/2024273닷넷: 2241. C# - WAV 파일의 PCM 사운드 재생(Windows Multimedia)파일 다운로드1
13598정성태4/16/2024288닷넷: 2240. C# - WAV 파일 포맷 + LIST 헤더파일 다운로드1
13597정성태4/15/2024364닷넷: 2239. C# - WAV 파일의 PCM 데이터 생성 및 출력파일 다운로드1
13596정성태4/14/2024725닷넷: 2238. C# - WAV 기본 파일 포맷파일 다운로드1
13595정성태4/13/2024848닷넷: 2237. C# - Audio 장치 열기 (Windows Multimedia, NAudio)파일 다운로드1
13594정성태4/12/20241002닷넷: 2236. C# - Audio 장치 열람 (Windows Multimedia, NAudio)파일 다운로드1
13593정성태4/8/20241050닷넷: 2235. MSBuild - AccelerateBuildsInVisualStudio 옵션
13592정성태4/2/20241206C/C++: 165. CLion으로 만든 Rust Win32 DLL을 C#과 연동
13591정성태4/2/20241166닷넷: 2234. C# - WPF 응용 프로그램에 Blazor App 통합파일 다운로드1
13590정성태3/31/20241071Linux: 70. Python - uwsgi 응용 프로그램이 k8s 환경에서 OOM 발생하는 문제
13589정성태3/29/20241141닷넷: 2233. C# - 프로세스 CPU 사용량을 나타내는 성능 카운터와 Win32 API파일 다운로드1
13588정성태3/28/20241192닷넷: 2232. C# - Unity + 닷넷 App(WinForms/WPF) 간의 Named Pipe 통신파일 다운로드1
13587정성태3/27/20241151오류 유형: 900. Windows Update 오류 - 8024402C, 80070643
13586정성태3/27/20241294Windows: 263. Windows - 복구 파티션(Recovery Partition) 용량을 늘리는 방법
13585정성태3/26/20241094Windows: 262. PerformanceCounter의 InstanceName에 pid를 추가한 "Process V2"
13584정성태3/26/20241046개발 환경 구성: 708. Unity3D - C# Windows Forms / WPF Application에 통합하는 방법파일 다운로드1
13583정성태3/25/20241154Windows: 261. CPU Utilization이 100% 넘는 경우를 성능 카운터로 확인하는 방법
13582정성태3/19/20241413Windows: 260. CPU 사용률을 나타내는 2가지 수치 - 사용량(Usage)과 활용률(Utilization)파일 다운로드1
13581정성태3/18/20241586개발 환경 구성: 707. 빌드한 Unity3D 프로그램을 C++ Windows Application에 통합하는 방법
13580정성태3/15/20241136닷넷: 2231. C# - ReceiveTimeout, SendTimeout이 적용되지 않는 Socket await 비동기 호출파일 다운로드1
13579정성태3/13/20241493오류 유형: 899. HTTP Error 500.32 - ANCM Failed to Load dll
13578정성태3/11/20241628닷넷: 2230. C# - 덮어쓰기 가능한 환형 큐 (Circular queue)파일 다운로드1
13577정성태3/9/20241872닷넷: 2229. C# - 닷넷을 위한 난독화 도구 소개 (예: ConfuserEx)
13576정성태3/8/20241543닷넷: 2228. .NET Profiler - IMetaDataEmit2::DefineMethodSpec 사용법
[1]  2  3  4  5  6  7  8  9  10  11  12  13  14  15  ...