Microsoft MVP성태의 닷넷 이야기
글쓴 사람
정성태 (techsharer at outlook.com)
홈페이지
첨부 파일
 
(연관된 글이 1개 있습니다.)
(시리즈 글이 11개 있습니다.)
.NET Framework: 202. CLR JIT 컴파일러가 생성한 기계어 코드 확인하는 방법
; https://www.sysnet.pe.kr/2/0/975

.NET Framework: 210. Windbg 환경에서 확인해 본 .NET 메서드 JIT 컴파일 전과 후
; https://www.sysnet.pe.kr/2/0/1023

.NET Framework: 395. C# - 프로퍼티로 정의하면 필드보다 느릴까요?
; https://www.sysnet.pe.kr/2/0/1545

.NET Framework: 396. C# - 프로퍼티로 정의하면 필드보다 느릴까요? - windbg / ollydbg
; https://www.sysnet.pe.kr/2/0/1546

.NET Framework: 542. 닷넷 - 특정 클래스가 로드되었는지 여부를 알 수 있을까?
; https://www.sysnet.pe.kr/2/0/10888

.NET Framework: 545. 닷넷 - 특정 클래스가 로드되었는지 여부를 알 수 있을까? - 두 번째 이야기
; https://www.sysnet.pe.kr/2/0/10893

.NET Framework: 763. .NET Core 2.1 - Tiered Compilation 도입
; https://www.sysnet.pe.kr/2/0/11539

디버깅 기술: 161. Windbg 환경에서 확인해 본 .NET 메서드 JIT 컴파일 전과 후 - 두 번째 이야기
; https://www.sysnet.pe.kr/2/0/12133

.NET Framework: 2015. C# - 인라인 메서드(inline methods)
; https://www.sysnet.pe.kr/2/0/13063

.NET Framework: 2016. C# - JIT 컴파일러의 인라인 메서드 처리 유무
; https://www.sysnet.pe.kr/2/0/13064

닷넷: 2132. C# - sealed 클래스의 메서드를 callback 호출했을 때 인라인 처리가 될까요?
; https://www.sysnet.pe.kr/2/0/13391




닷넷 - 특정 클래스가 로드되었는지 여부를 알 수 있을까?

클래스가 로드되었는지를 알아낼 방법이 있을까요?

결론부터 말하면, 공식적으로는 그것을 알아낼 방법이 없습니다. 검색을 해보면 우회적인 방법으로 정적 생성자(cctor)에 로드 여부를 나타내는 플래그를 마련해두는 식으로 해결하고 있습니다.

대략 이런 식입니다.

public class TargetTest
{
    public static bool Loaded = false;
    static TargetTest()
    {
        Loaded = true;
    }
}

이런 거 말고... 혹시 코드 변경없이 알아낼 수는 없는 걸까요?

로드되었는지 여부는 JIT 컴파일 여부를 통해서 알 수 있습니다. 하지만 Type 자체는 JIT 될 것이 없기 때문에 그것의 정적 생성자나 인스턴스 생성자의 JIT 여부를 확인해 보면 됩니다. 그런데, 둘 중의 어느 것이 나을까요? 정적 생성자는 모든 Type들이 가지고 있는 것은 아니기 때문에 인스턴스 생성자가 적당한 후보로 보입니다.

인스턴스 생성자로 타입의 로드 여부를 결정했다면, 이제 해당 메서드가 JIT 컴파일되었는지의 여부를 알아내면 됩니다. 이 방법은 GetFunctionPointer를 이용해 해당 메서드의 어셈블리 코드에 대해 JIT 전/후의 결과에 따른 차이점으로 판별할 수 있습니다. 일단, 여기까지의 내용을 코드로 만들면 다음과 같습니다.

private unsafe static bool IsTypeLoaded(Type type)
{
    bool loaded = false;

    foreach (ConstructorInfo ctor in type.GetConstructors())
    {
        IntPtr ptr = ctor.MethodHandle.GetFunctionPointer();
        byte* asmCodes = (byte*)ptr.ToPointer();

        // asmCodes의 내용에 따라...
    }

    return loaded;
}

제가 테스트했을 때 asmCodes 포인터에 들어온 주소값은 0x00a50d18이었습니다. 그리고 그것을 Visual Studio의 메모리 창에서 확인해 JIT 이전에 가보면 대략 다음과 같이 보입니다.

check_type_loaded_1.png

0xe8 xx xx xx xx

0xe8은 어셈블리 코드로 call에 해당합니다. 그리고 그 뒤의 4바이트는 call에 전달될 주소에 해당하니 큰 의미는 없습니다. JIT 컴파일 되기 이전의 GetFunctionPointer가 반환하는 주소는 언제나 0xe8이 있는 주소를 가리킵니다. 아마도 Pre-JIT해주는 메서드의 위치일 것입니다.

JIT 된 다음의 코드를 그럼 확인해 볼까요? 위의 IsTypeLoaded 메서드를 호출하기 전 해당 생성자를 한번 호출해 주면 됩니다. 그리고 나서 다시 확인해 보면 이번에는 다음과 같은 패턴을 보입니다.

check_type_loaded_2.png

0xe9 xx xx xx xx

0xe9는 JMP문이고 이어서 나오는 4바이트는 변위값입니다. 현재 위치에서 해당 변위값을 더해보면 실제 메서드(여기서는 생성자)의 JIT 컴파일된 기계어 코드가 나올 것입니다.

0x010c0cf8 + 5바이트(JMP 및 옵셋값) + 0x1feb == JIT 컴파일된 메서드 위치

이후에는 사실 중요하지 않으니 생략하고. ^^

자, 그럼 최종 코드를 다음과 같이 만들 수 있습니다.

using System;
using System.Reflection;

namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
        //    new TestClass1();
            Console.WriteLine(IsTypeLoaded(typeof(TestClass1)));  // False 출력
            new TestClass2(5);
            Console.WriteLine(IsTypeLoaded(typeof(TestClass2)));  // True 출력
        }

        private unsafe static bool IsTypeLoaded(Type type)
        {
            bool loaded = false;

            foreach (ConstructorInfo ctor in type.GetConstructors())
            {
                IntPtr ptr = ctor.MethodHandle.GetFunctionPointer();
                byte* asmCodes = (byte*)ptr.ToPointer();

                if (*asmCodes != 0xe8)
                {
                    loaded = true;
                    break;
                }
            }

            return loaded;
        }
    }

    class TestClass1
    {
        public TestClass1()
        {
            Console.WriteLine("TestClass1 ctor");
        }
    }

    class TestClass2
    {
        public TestClass2(int i)
        {
            Console.WriteLine("TestClass2 ctor");
        }
    }
}

물론, 이 코드는 공식적인 방법이 아니기 때문에 그냥 디버깅 용도같은 경우에만 사용하시는 것이 좋습니다. ^^




참고로, 비주얼 스튜디오 내에서 디버그 모드로 실행한 경우 JIT 컴파일 이후의 GetFunctionPointer는 JMP 문이 있는 주소를 반환하지만 그냥 (명령행을 이용하는 등의 방법으로) 실행하면 JMP 문 이후의 메서드 본체의 주소를 반환하는 차이가 있습니다.




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

[연관 글]






[최초 등록일: ]
[최종 수정일: 2/14/2016]

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

비밀번호

댓글 작성자
 




1  2  3  4  5  6  7  8  9  10  [11]  12  13  14  15  ...
NoWriterDateCnt.TitleFile(s)
13667정성태7/7/20246608닷넷: 2273. C# - 리눅스 환경에서의 Hyper-V Socket 연동 (AF_VSOCK)파일 다운로드1
13666정성태7/7/20247686Linux: 74. C++ - Vsock 예제 (Hyper-V Socket 연동)파일 다운로드1
13665정성태7/6/20247863Linux: 73. Linux 측의 socat을 이용한 Hyper-V 호스트와의 vsock 테스트파일 다운로드1
13663정성태7/5/20247470닷넷: 2272. C# - Hyper-V Socket 통신(AF_HYPERV, AF_VSOCK)의 VMID Wildcards 유형파일 다운로드1
13662정성태7/4/20247477닷넷: 2271. C# - WSL 2 VM의 VM ID를 알아내는 방법 - Host Compute System API파일 다운로드1
13661정성태7/3/20247398Linux: 72. g++ - 다른 버전의 GLIBC로 소스코드 빌드
13660정성태7/3/20247504오류 유형: 912. Visual C++ - Linux 프로젝트 빌드 오류
13659정성태7/1/20247843개발 환경 구성: 715. Windows - WSL 2 환경의 Docker Desktop 네트워크
13658정성태6/28/20248218개발 환경 구성: 714. WSL 2 인스턴스와 호스트 측의 Hyper-V에 운영 중인 VM과 네트워크 연결을 하는 방법 - 두 번째 이야기
13657정성태6/27/20247894닷넷: 2270. C# - Hyper-V Socket 통신(AF_HYPERV, AF_VSOCK)을 위한 EndPoint 사용자 정의
13656정성태6/27/20248056Windows: 264. WSL 2 VM의 swap 파일 위치
13655정성태6/24/20247838닷넷: 2269. C# - Win32 Resource 포맷 해석파일 다운로드1
13654정성태6/24/20247787오류 유형: 911. shutdown - The entered computer name is not valid or remote shutdown is not supported on the target computer.
13653정성태6/22/20247925닷넷: 2268. C# 코드에서 MAKEINTREOURCE 매크로 처리
13652정성태6/21/20249232닷넷: 2267. C# - Linux 환경에서 (Reflection 없이) DLL AssemblyFileVersion 구하는 방법파일 다운로드2
13651정성태6/19/20248472닷넷: 2266. C# - (Reflection 없이) DLL AssemblyFileVersion 구하는 방법파일 다운로드1
13650정성태6/18/20248397개발 환경 구성: 713. "WSL --debug-shell"로 살펴보는 WSL 2 VM의 리눅스 환경
13649정성태6/18/20247947오류 유형: 910. windbg - !py 확장 명령어 실행 시 "failed to find python interpreter" (2)
13648정성태6/17/20248263오류 유형: 909. C# - DynamicMethod 사용 시 System.TypeAccessException
13647정성태6/16/20249323개발 환경 구성: 712. Windows - WSL 2의 네트워크 통신 방법 - 세 번째 이야기 (같은 IP를 공유하는 WSL 2 인스턴스) [1]
13646정성태6/14/20247742오류 유형: 908. Process Explorer - "Error configuring dump resources: The system cannot find the file specified."
13645정성태6/13/20248197개발 환경 구성: 711. Visual Studio로 개발 시 기본 등록하는 dev tag 이미지로 Docker Desktop k8s에서 실행하는 방법
13644정성태6/12/20248855닷넷: 2265. C# - System.Text.Json의 기본적인 (한글 등에서의) escape 처리 [1]
13643정성태6/12/20248294오류 유형: 907. MySqlConnector 사용 시 System.IO.FileLoadException 오류
13642정성태6/11/20248196스크립트: 65. 파이썬 - asgi 버전(2, 3)에 따라 달라지는 uvicorn 호스팅
13641정성태6/11/20248659Linux: 71. Ubuntu 20.04를 22.04로 업데이트
1  2  3  4  5  6  7  8  9  10  [11]  12  13  14  15  ...