Microsoft MVP성태의 닷넷 이야기
.NET Framework: 185. windbg로 확인하는 .NET CLR 메서드 [링크 복사], [링크+제목 복사],
조회: 26181
글쓴 사람
정성태 (techsharer at outlook.com)
홈페이지
첨부 파일
(연관된 글이 3개 있습니다.)
windbg로 확인하는 .NET CLR 메서드

windbg와 sos 명령어 실습 겸 CLR 메서드에 대한 JIT 컴파일 여부를 확인해 볼 텐데요. 대략 다음의 글에 대한 실습 과정이라고 해야겠지요. ^^

Drill Into .NET Framework Internals to See How the CLR Creates Runtime Objects
; https://learn.microsoft.com/en-us/archive/msdn-magazine/2005/may/net-framework-internals-how-the-clr-creates-runtime-objects

실습을 위해 다음과 같이 간단한 코드를 만들어서 실행시키고,

using System;
namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            MyMethod();
            Console.ReadLine();
        }

        static void MyMethod()
        {
            Console.WriteLine("MyMethod");
        }

        static void MyMethod2()
        {
            Console.WriteLine("MyMethod2");
        }
    }
}

멈춘 그 시점에 windbg로 연결하고 다음과 같이 sos.dll을 로딩시켜 줍니다. (여기서는 x64 / .NET 3.5 환경에서 테스트합니다.)

0:003> .load "C:\\Windows\\Microsoft.NET\\Framework64\\v2.0.50727\\SOS.dll"

SOS.dll (SOS Debugging Extension)
; https://learn.microsoft.com/en-us/dotnet/framework/tools/sos-dll-sos-debugging-extension

우선, 기계어로 번역되지 않은 MyMethod2 먼저 확인해 보겠습니다.

0:003> !name2ee ConsoleApplication1.exe!ConsoleApplication1.Program.MyMethod2   
Module: 000007ff000433d0 (ConsoleApplication1.exe)
Token: 0x0000000006000003
MethodDesc: 000007ff000439a8
Name: ConsoleApplication1.Program.MyMethod2()
Not JITTED yet. Use !bpmd -md 000007ff000439a8 to break on run.

0:003> !dumpmd 000007ff000439a8
Method Name: ConsoleApplication1.Program.MyMethod2()
Class: 000007ff00182210
MethodTable: 000007ff000439d0
mdToken: 06000003
Module: 000007ff000433d0
IsJitted: no
CodeAddr: ffffffffffffffff

JIT 컴파일링이 된 MyMethod를 확인해 보면 다음과 같습니다.

0:003> !name2ee ConsoleApplication1.exe!ConsoleApplication1.Program.MyMethod
Module: 000007ff000433d0 (ConsoleApplication1.exe)
Token: 0x0000000006000002
MethodDesc: 000007ff00043998
Name: ConsoleApplication1.Program.MyMethod()
JITTED Code Address: 000007ff00190180

0:003> !dumpmd 000007ff00043998
Method Name: ConsoleApplication1.Program.MyMethod()
Class: 000007ff00182210
MethodTable: 000007ff000439d0
mdToken: 06000002
Module: 000007ff000433d0
IsJitted: yes
CodeAddr: 000007ff00190180

JIT 컴파일된 기계어 주소 "CodeAddr: 000007ff00190180"에 대해서 "u" 명령어를 내려서 확인하는 것도 가능합니다.

0:003> u 000007ff00190180 Lf  // 또는 !u [MethodDesc 값]
000007ff`00190180 4883ec28        sub     rsp,28h
000007ff`00190184 90              nop
000007ff`00190185 48b8c8360400ff070000 mov rax,7FF000436C8h
000007ff`0019018f 8b00            mov     eax,dword ptr [rax]
000007ff`00190191 85c0            test    eax,eax
000007ff`00190193 7405            je      000007ff`0019019a
000007ff`00190195 e846b29ef1      call    mscorwks!JIT_DbgIsJustMyCode (000007fe`f1b7b3e0)
000007ff`0019019a 90              nop
000007ff`0019019b 48b950306e1200000000 mov rcx,126E3050h
000007ff`001901a5 488b09          mov     rcx,qword ptr [rcx]
000007ff`001901a8 e8b3908ef0      call    mscorlib_ni+0x319260 (000007fe`f0a79260)  // Console.WriteLine
000007ff`001901ad 90              nop
000007ff`001901ae eb00            jmp     000007ff`001901b0
000007ff`001901b0 4883c428        add     rsp,28h
000007ff`001901b4 f3c3            rep ret

IL 코드도 확인해 볼까요?

0:003> !dumpil 000007ff00043998
ilAddr = 0000000000332065
IL_0000: nop 
IL_0001: ldstr "MyMethod"
IL_0006: call System.Console::WriteLine 
IL_000b: nop 
IL_000c: ret 

보시는 것처럼 ilAddr == 0000000000332065 주소에 MyMethod의 Body가 확인됩니다. 풀어볼까요? ^^

0:003> db 0000000000332065 Le
00000000`00332065  36 00 72 01 00 00 70 28-11 00 00 0a 00 2a        6.r...p(.....*

0x36 == Tiny header
    typedef struct IMAGE_COR_ILMETHOD_TINY
    {
        BYTE Flags_CodeSize;
    } IMAGE_COR_ILMETHOD_TINY;
    
    0x36 == 00110110b
    하위 2bit 10b == CorILMethod_TinyFormat
    상위 6bit 001101b == 0xd IL 코드 크기 (즉, 이후 13byte들은 실행되어야 할 IL코드)
        
0x00 == nop
0x72 == ldstr
0x01 0x00 0x00 0x70 == [0x70000001 문자열 token]
0x28 == call
0x11 0x00 0x00 0x0a == [0x0a000011 메서드 token]
0x00 == nop
0x2a == ret




JIT 컴파일 되기 전과 후를 windbg의 MethodDesc 테이블을 확인해 보면 알 수 있습니다.


0:003> !DumpMT -MD 000007ff000439d0
EEClass: 000007ff00182210
Module: 000007ff000433d0
Name: ConsoleApplication1.Program
mdToken: 02000002  (D:\...[경로생략]...\ConsoleApplication1.exe)
BaseSize: 0x18
ComponentSize: 0x0
Number of IFaces in IFaceMap: 0
Slots in VTable: 9
--------------------------------------
MethodDesc Table
           Entry       MethodDesc      JIT Name
000007fef0a4abe0 000007fef07ce828   PreJIT System.Object.ToString()
000007fef0a52560 000007fef07ce830   PreJIT System.Object.Equals(System.Object)
000007fef0a4bc70 000007fef07ce870   PreJIT System.Object.GetHashCode()
000007fef0afe5f0 000007fef07ce8a0   PreJIT System.Object.Finalize()
000007ff0004c040 000007ff000439c8     NONE ConsoleApplication1.Program..ctor()
000007ff00190120 000007ff00043988      JIT ConsoleApplication1.Program.Main(System.String[])
000007ff00190180 000007ff00043998      JIT ConsoleApplication1.Program.MyMethod()
000007ff0004c030 000007ff000439a8     NONE ConsoleApplication1.Program.MyMethod2()
000007ff001901e0 000007ff000439b8      JIT ConsoleApplication1.Program.ShowMethodDesc()

Entry 값이 "000007ff0004c030"이므로, IL 코드에서 이 함수를 부를 때 "000007ff0004c030" 주소에 있는 코드를 실행하게 됩니다. 어떤 코드인지는 감이 잡히시죠?

0:003> u 000007ff0004c030 
000007ff`0004c030 e8bbea8bf1      call    mscorwks!PrecodeFixupThunk (000007fe`f190aaf0)
000007ff`0004c035 cc              int     3
000007ff`0004c036 0402            add     al,2
000007ff`0004c038 e8b3ea8bf1      call    mscorwks!PrecodeFixupThunk (000007fe`f190aaf0)
000007ff`0004c03d cc              int     3
000007ff`0004c03e 06              ???
000007ff`0004c03f 01e8            add     eax,ebp
000007ff`0004c041 ab              stos    dword ptr [rdi]

물론, JIT된 이후에는 Entry 값이 교체되는데, 그것이 바로 실제 기계어로 번역된 코드의 주솟값입니다.

재미있게도, 위의 확인 과정은 Reflection으로도 가능합니다.

위에서 name2ee를 통해서 "MethodDesc" 값을 확인해 보았는데요. 코드에서는 다음과 같이 MethodInfo.MethodHandle.Value 값이 MethodDesc 값과 동일하다는 것을 확인할 수 있습니다.

MethodInfo methodInfo = typeof(Program).GetMethod("MyMethod", 
        System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.NonPublic);

IntPtr methodDesc = methodInfo.MethodHandle.Value;

기계어로 번역되기 전과 후의 주소는 다음과 같이 확인해 볼 수 있습니다.

MethodInfo methodInfo = ...;
IntPtr pAddress = methodInfo.MethodHandle.GetFunctionPointer();
Console.WriteLine("Stub or Jitted Address == " + pAddress.ToString("x"));

JIT 컴파일되기 전과 후에 각각 MethodHandle.GetFunctionPointer() 값을 구하면 서로 다르다는 것을 확인할 수 있고 그 값을 windbg의 MethodDesc 테이블에 있는 Entry 값과 비교해보면 동일한 것을 알 수 있습니다.

첨부한 솔루션은 이를 확인해 본 간단한 소스 코드입니다.



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

[연관 글]






[최초 등록일: ]
[최종 수정일: 6/28/2023]

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

비밀번호

댓글 작성자
 




... 166  167  168  169  170  171  172  173  174  175  176  177  [178]  179  180  ...
NoWriterDateCnt.TitleFile(s)
536정성태9/12/200732358.NET Framework: 97. WCF : netTcpBinding에서의 각종 Timeout 값 설명 [11]
535정성태9/11/200729814.NET Framework: 96. WCF - PerSession에서의 클라이언트 연결 관리 [5]
534정성태9/3/200725314개발 환경 구성: 29. VHD 파일 크기 줄이기
533정성태9/2/200728020개발 환경 구성: 28. CA 서비스 - 사용자 정의 템플릿 유형 추가
532정성태9/2/200730554개발 환경 구성: 27. AD CA에서 Code Signing 인증서 유형 추가 방법
531정성태9/2/200726299.NET Framework: 95. WCF에서의 DataTable 사용
530정성태9/1/200722810.NET Framework: 94. WCF 예외에 대한 시행착오
529정성태8/31/200725705.NET Framework: 93. WCF - DataContract와 KnownType 특성 [1]
528정성태8/30/200720361오류 유형: 47. VPC - 네트워크 어댑터 MAC 주소 중복 오류
527정성태8/30/200730434Team Foundation Server: 20. 잠긴 파일을 강제로 해제 [2]
526정성태8/29/200720332오류 유형: 46. VS.NET 2008 - ASP.NET 디버깅 : Strong name validation failed.
525정성태8/27/200722572VS.NET IDE: 54. VS.NET 2008 - 새롭게 도입되는 XSD Schema Designer
524정성태8/23/200740077오류 유형: 45. 요청한 작업은, 사용자가 매핑한 구역이 열려 있는...
523정성태8/16/200722739VS.NET IDE: 53. VS.NET 2008 - 서비스 참조 시 기존 데이터 컨테이너 DLL 사용
522정성태8/13/200726377VS.NET IDE: 52. VS.NET 2008 - WCF를 위한 디버깅 환경 개선
521정성태8/8/200726386.NET Framework: 92. XmlSerializer 생성자의 실행 속도를 올리는 방법 - 두 번째 이야기 [3]
520정성태8/7/200721589VS.NET IDE: 51. Visual Studio 2008 베타 2 설치
519정성태7/27/200727964오류 유형: 44. System.BadImageFormatException [2]
518정성태7/26/200728984오류 유형: 43. System.ComponentModel.LicenseException [1]
517정성태7/19/200717285개발 환경 구성: 26. VPC - 일반 사용자 계정으로 구동
516정성태7/19/200720395오류 유형: 42. TFS - Error loading menu: Index was outside the bounds of the array [2]
515정성태7/18/200728136오류 유형: 41. SSL 서버 자격 증명을 만드는 동안 심각한 오류가 발생했습니다.
514정성태7/14/200720829Team Foundation Server: 19. Orcas에서 개선되는 TFS 기능들
513정성태7/4/200731803.NET Framework: 91. Foreground Thread / Background Thread [1]
512정성태6/27/200721730오류 유형: 40. error PRJ0050: Failed to register output.
511정성태6/25/200729741.NET Framework: 90. XmlSerializer 생성자의 실행 속도를 올리는 방법 [2]
... 166  167  168  169  170  171  172  173  174  175  176  177  [178]  179  180  ...