Microsoft MVP성태의 닷넷 이야기
.NET Framework: 158. 닷넷 프로파일러 - IL 코드 재작성 [링크 복사], [링크+제목 복사]
조회: 27769
글쓴 사람
정성태 (techsharer at outlook.com)
홈페이지
첨부 파일
 
(연관된 글이 1개 있습니다.)

닷넷 프로파일러 - IL 코드 재작성


지난번에, 프로파일러가 제대로 응용이 된 예를 하나 소개해드렸지요.

닷넷 프로파일러의 또 다른 응용: Visual Studio 2010 Historical Debugging 
; https://www.sysnet.pe.kr/2/0/759

이에 대해서 좀 더 알아볼까요?

일반적으로 닷넷 프로파일러는 느리다는 인식이 있습니다. 사실, "Historical Debugging" 기능 역시 켜 놓고 있으면 최초 디버깅 시작 시에 속도가 눈에 띌 정도로 느립니다.

하지만, "모든 프로파일러"가 이와 같이 느린 것은 아니고, 경우에 따라서는 보통의 응용 프로그램 속도와 유사할 수 있습니다. 왜냐하면, 프로파일러는 다음과 같은 종류의 콜백을 취사선택해서 받을 수 있으며 이 중에서 특정 기능만이 응용 프로그램 속도를 느리게 만들기 때문입니다.

enum __MIDL___MIDL_itf_corprof_0000_0002
    {   COR_PRF_MONITOR_NONE    = 0,
    COR_PRF_MONITOR_FUNCTION_UNLOADS    = 0x1,
    COR_PRF_MONITOR_CLASS_LOADS = 0x2,
    COR_PRF_MONITOR_MODULE_LOADS    = 0x4,
    COR_PRF_MONITOR_ASSEMBLY_LOADS  = 0x8,
    COR_PRF_MONITOR_APPDOMAIN_LOADS = 0x10,
    COR_PRF_MONITOR_JIT_COMPILATION = 0x20,
    COR_PRF_MONITOR_EXCEPTIONS  = 0x40,
    COR_PRF_MONITOR_GC  = 0x80,
    COR_PRF_MONITOR_OBJECT_ALLOCATED    = 0x100,
    COR_PRF_MONITOR_THREADS = 0x200,
    COR_PRF_MONITOR_REMOTING    = 0x400,
    COR_PRF_MONITOR_CODE_TRANSITIONS    = 0x800,
    COR_PRF_MONITOR_ENTERLEAVE  = 0x1000,
    COR_PRF_MONITOR_CCW = 0x2000,
    COR_PRF_MONITOR_REMOTING_COOKIE = 0x4000 | COR_PRF_MONITOR_REMOTING,
    COR_PRF_MONITOR_REMOTING_ASYNC  = 0x8000 | COR_PRF_MONITOR_REMOTING,
    COR_PRF_MONITOR_SUSPENDS    = 0x10000,
    COR_PRF_MONITOR_CACHE_SEARCHES  = 0x20000,
    COR_PRF_MONITOR_CLR_EXCEPTIONS  = 0x1000000,
    COR_PRF_MONITOR_ALL = 0x107ffff,
    COR_PRF_ENABLE_REJIT    = 0x40000,
    COR_PRF_ENABLE_INPROC_DEBUGGING = 0x80000,
    COR_PRF_ENABLE_JIT_MAPS = 0x100000,
    COR_PRF_DISABLE_INLINING    = 0x200000,
    COR_PRF_DISABLE_OPTIMIZATIONS   = 0x400000,
    COR_PRF_ENABLE_OBJECT_ALLOCATED = 0x800000,
    COR_PRF_ENABLE_FUNCTION_ARGS    = 0x2000000,
    COR_PRF_ENABLE_FUNCTION_RETVAL  = 0x4000000,
    COR_PRF_ENABLE_FRAME_INFO   = 0x8000000,
    COR_PRF_ENABLE_STACK_SNAPSHOT   = 0x10000000,
    COR_PRF_USE_PROFILE_IMAGES  = 0x20000000,
    }   COR_PRF_MONITOR;

자... 그럼 이 중에서 "Historical Debugging"과 같은 기능을 구현하려면 어떻게 해야 할까요? 그렇죠... ^^ "COR_PRF_MONITOR_JIT_COMPILATION" 플래그를 설정해야 합니다. 방법도 간단합니다. ICorProfilerCallback::Initialize 단계에서 ICorProfilerInfo::SetEventMask(COR_PRF_MONITOR_JIT_COMPILATION);으로 호출하면 이후로 ICorProfilerCallback::JITCompilationStarted 콜백이 불려지게 됩니다. 가장 중요한 기능인 IL 코드 변경은 바로 이 콜백 함수가 호출되는 단계에서 처리해주면 됩니다.

그런데, 실제로 해보면 결과가 원하는 대로 나오지 않는 것을 볼 수 있습니다.

예를 들어, "Windows Forms 빈 응용 프로그램"을 대상으로 아래와 같이 JITCompilationStarted 메서드를 구현해 놓으면,

HRESULT CNativeBridge::JITCompilationStarted(FunctionID functionId, BOOL fIsSafeToBlock)
{
    IMetaDataImport * pIMetaDataImport = 0;
    HRESULT hr = S_OK;
    mdToken funcToken = 0;

    hr = m_pICorProfilerInfo->GetTokenAndMetaDataFromFunction (functionId,
                                                        IID_IMetaDataImport,
                                                        (LPUNKNOWN *) &pIMetaDataImport,
                                                        &funcToken);
                                                        
    ... [중간 생략] ...
    ... buf에는 JIT 컴파일될 함수의 이름 ...
    LogEntry( L"\r\JITCompilationStarted - %s\r\n", buf );

    pIMetaDataImport->Release();

    return S_OK;
}

이때 출력되는 메서드 이름은 다음과 같습니다.

JITCompilationStarted - TestWinForm.Program.Main
JITCompilationStarted - TestWinForm.Form1..ctor
JITCompilationStarted - TestWinForm.Form1.InitializeComponent
JITCompilationStarted - TestWinForm.Form1.Dispose

척 봐도... 뭔가 많이 모자랍니다. 가만 보니, 우리가 만든 Windows Forms 응용 프로그램에만 구현된 메서드에 대해서 콜백 함수가 불려졌다는 것을 알 수 있는데요. 이렇다 보니, 프로파일링을 걸어놨지만 속도는 거의 차이가 나지 않을 정도로 빠릅니다. 만약 .NET Framework 레벨의 메서드가 아닌 사용자가 만든 코드 수준에 대해서만 IL 재작성을 할 필요가 있다면 프로파일링 API를 붙이는 정도는 부담되지 않는 선택입니다. (한 예로, 사용자 코드에 대해서만 Call-graph를 작성한다든지!)




하지만, Historical Debugging은 위와 같이 개발자가 만든 소스 코드만 JIT 컴파일링 되는 것을 잡아내는 것만으로는 구현이 안됩니다. .NET Framework 어셈블리까지도 IL 코드를 임의로 조작할 수 있어야 합니다.

생각해 보면, 여기에 약간의 어려움이 예상됩니다. 왜냐하면, .NET 프레임워크에서 제공되는 어셈블리들은 설치시에 NGen에 의해 미리 JIT 컴파일링되어 있기 때문에 IL 코드를 끼워넣을 수 있는 여지가 없기 때문입니다.

다행히, 이 부분에 대해서는 마이크로소프트 측에서 해법을 제공해 주고 있습니다.

Creating an IL-rewriting profiler
; https://learn.microsoft.com/en-us/archive/blogs/davbr/creating-an-il-rewriting-profiler

재미있지요. ^^ 우선 COR_PRF_USE_PROFILE_IMAGES 이벤트를 ICorProfilerInfo::SetEventMask에 추가합니다. 이걸 추가하게 되면, CLR은 NGen된 모듈을 로드하는 것을 거부하고 같은 버전의 IL 코드가 담긴 어셈블리를 로드해서 무조건 재컴파일하게 됩니다.

그래서, COR_PRF_USE_PROFILE_IMAGES를 적용하고 Windows Forms 빈 응용 프로그램을 실행하게 되면 다음과 같은 JIT 컴파일링 순서를 얻을 수 있습니다. (한마디로, 모든 메서드의 JIT 컴파일링 기록이 출력됩니다.)

JITCompilationStarted - System.AppDomain.SetupDomain
JITCompilationStarted - System.AppDomainSetup.get_Value
JITCompilationStarted - System.AppDomainSetup.set_DisallowBindingRedirects
JITCompilationStarted - System.AppDomain.SetupFusionStore
JITCompilationStarted - System.AppDomainSetup.get_DeveloperPath
JITCompilationStarted - System.AppDomainSetup.VerifyDirList
JITCompilationStarted - System.AppDomainSetup.set_DeveloperPath
JITCompilationStarted - System.AppDomainSetup.SetupFusionContext
JITCompilationStarted - System.String..cctor
JITCompilationStarted - System.Text.StringBuilder..ctor
JITCompilationStarted - System.String.GetStringForStringBuilder
JITCompilationStarted - System.Text.StringBuilder.Append
JITCompilationStarted - System.Text.StringBuilder.GetNewString
... 너무 많아서 생략 ...
JITCompilationStarted - System.Windows.Forms.Internal.DeviceContext.get_BackgroundMode
JITCompilationStarted - System.Windows.Forms.Internal.DeviceContext.SetBackgroundMode
JITCompilationStarted - System.Windows.Forms.Internal.WindowsGraphics.AdjustForVerticalAlignment
JITCompilationStarted - System.Drawing.Rectangle.get_Left
JITCompilationStarted - System.Drawing.Rectangle.get_Top
JITCompilationStarted - System.Windows.Forms.ButtonInternal.ButtonBaseAdapter.DrawFocus
JITCompilationStarted - System.Windows.Forms.Control.get_ShowFocusCues
JITCompilationStarted - System.Drawing.BufferedGraphics.Render
JITCompilationStarted - System.Drawing.BufferedGraphics.RenderInternal
JITCompilationStarted - System.Drawing.BufferedGraphics.Dispose
JITCompilationStarted - System.Drawing.BufferedGraphics.Dispose

그런데, 여기서 한 가지 선택을 할 여지가 하나 있습니다. 만약 "모든 메서드"에 대해서 IL 코드를 끼워넣어야 한다면 굳이 COR_PRF_USE_PROFILE_IMAGES | COR_PRF_MONITOR_JIT_COMPILATION 조합을 사용하는 것은 너무 번거롭습니다. 차라리 COR_PRF_MONITOR_ENTERLEAVE를 사용하면 모든 메서드의 호출에 대해서 시작과 끝 지점에서 콜백을 받을 수 있는 메서드를 등록할 수 있기 때문입니다.

제가 잠시 테스트를 하고 나서 결론을 내려 본다면,
COR_PRF_MONITOR_ENTERLEAVE에 실행될 코드를 지정하는 것은 너무 많은 오버헤드를 수반하기 때문에, 원하는 메서드가 호출된 경우에만 IL 코드를 삽입하는 COR_PRF_USE_PROFILE_IMAGES | COR_PRF_MONITOR_JIT_COMPILATION 조합이 더 현실적이라고 판단됩니다.

오늘은 여기까지만! ^^



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

[연관 글]






[최초 등록일: ]
[최종 수정일: 2/13/2024]

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

비밀번호

댓글 작성자
 



2011-03-03 07시57분
[안기석] 안녕하세요~ 안기석입니다

요즘 프로파일링에 대해서 공부하다보니 시스넷에 자주 들어오게 됩니다.
=========================================================================
제가 잠시 테스트를 하고 나서 결론을 내려본다면,
COR_PRF_MONITOR_ENTERLEAVE 에 실행될 코드를 지정하는 것은 너무 많은 오버헤드를 수반하기 때문에, 원하는 메서드가 호출된 경우에만 IL코드를 삽입하는 COR_PRF_USE_PROFILE_IMAGES | COR_PRF_MONITOR_JIT_COMPILATION 조합이 더 현실적이라고 판단됩니다.

오늘은 여기까지만! ^^
=========================================================================
라고 하신부분을 읽다보면 다음과 같은 질문을 하고 싶습니다.

질문 1 : COR_PRF_MONITOR_JIT_COMPILATION 만 설정할 경우에는 확실히 Windows Forms 응용 프로그램에만 구현된 메서드 가 로그로 남은 것을 볼 수 있습니다.
         그리고 COR_PRF_USE_PROFILE_IMAGES 까지 설정할 경우 모든 메서드의 JIT 컴파일링 기록이 출력이 되었습니다.
         그런데 이렇게 남겨진 기록들은 매서드들이 실제로 호출되는 순서와는 상관이 없는 것 같아 보이는데 맞습니까?
         그렇다면 이렇게 여러 매서드가가 호출되고 종료되는 상황을 기록 할때는 어떻게 해야 합니까?
         더 나아가서 여러 매서드 중에서 특정 매서드의 호출만을 기록 할때는 또 어떻게 해야 합니까?

지식이 없다보니 제대로 질문을 한 것인지 모르겠습니다.
[guest]
2011-03-03 10시21분
COR_PRF_MONITOR_JIT_COMPILATION 콜백은, 말 그대로 IL 코드가 Native로 JIT 컴파일 되는 단계에서만 불려지는 것입니다. 즉, (예외가 있지만) '하나의 메서드'에 대해서 '한 번만 불려집니다.'

메서드가 호출되고 종료되는 것은 쉽게는 COR_PRF_MONITOR_ENTERLEAVE 콜백을 구현하시면 됩니다. 그럼, 메서드 시작/종료 시에 특정 콜백 메서드를 호출해줍니다. 다만, 그걸로 하면 실행이 굉장히 느려지므로 COR_PRF_MONITOR_JIT_COMPILATION 콜백 단계에서 해당 메서드가 JIT 컴파일되기 전에 IL 코드를 변경해주는 방법이 있습니다. 즉, 자신이 원하는 로직을 담은 IL 코드를 원래의 메서드 앞/뒤에 심어놓는 것이죠.

구현이 쉽기로는 COR_PRF_MONITOR_ENTERLEAVE 콜백을 이용하는 것이고, 반면 COR_PRF_MONITOR_JIT_COMPILATION을 이용한 방법은 속도가 빠르지만 구현이 어렵습니다.
정성태
2011-03-04 05시46분
[안기석] [안기석] 안녕하세요~ 안기석입니다
오늘도 또 질문을 가지고 왔습니다.^^

질문 1.

HRESULT CNativeBridge::JITCompilationStarted(FunctionID functionId, BOOL fIsSafeToBlock)
{
    ... [중간 생략] ...
    ... buf에는 JIT 컴파일될 함수의 이름 ...
    LogEntry( L"\r\JITCompilationStarted - %s\r\n", buf );

    pIMetaDataImport->Release();

    return S_OK;
}
와 같은 코드에서
LogEntry( L"\r\JITCompilationStarted - %s\r\n", buf );
하는 부분이 IL 코드 변경에 변경을 가져 오는 부분이 맞습니까?

가령 COR_PRF_MONITOR_JIT_COMPILATION 플래그가 설정되어있는 상황에서
모니터링 대상 프로그램을 실행시키고, 메소드가 최초로 호출되게 되면
CLR 에 의해서 JIT 컴파일이 수행되고
이 순간에 JITCompilationStarted 콜백이 불려지게되고
logEntry 함수가 실행되어서 기록이 남게되고..

그렇다면 소스코드를 컴파일 해서 생긴 IL 과 실행시 호출되는 콜백함수의 코드가 합쳐져서
JIT 컴파일이 일어나서 navive code 가 생성되어진다 고 정리 할 수 있겠습니까?

음..IL 에 코드가 추가되는 것이 아니라 native code 에 추가되어진다로 이해해야 하는 건가요?

질문 2. COR_PRF_MONITOR_ENTERLEAVE 콜백 구현시 SetEnterLeaveFunctionHooks 를 통해서
설정한 FunctionEnter, FunctionLeave2, FunctionTailcall2 에 logEntry 와 같은 함수를
추가하게 되면 실제 함수가 수행된 시간을 측정하는 용도로 사용 할 수 있을 것 같은데
이 방법이 올바른 방법으로 판단되시는지요?
물론 전체 메소드를 모두 로그로 남기지 않기 위해서 특정 함수를 판별하는 조건식을 두어야겠지만요.

말씀하신 COR_PRF_MONITOR_JIT_COMPILATION 을 이용하면서 특정 함수의 로그를 남기는 방법은
아직 발견하지 못했습니다. 어렵다고 하셨지만 그게 더 올바른 방법이라면 배워보고 싶습니다.
[guest]
2011-03-04 09시37분
1.
넵 맞습니다. 그 부분에서 IL 코드를 변경합니다.
단지, 방법은 안기석 님이 설명한 것과 다릅니다.

그 단계에서, 원본 IL 코드를 BYTE 배열에 복사해 가져와서, 그에 대해 변경을 한 다음 다시 메서드 BODY로 지정해주는 방식입니다.

2.
재차 말씀드리지만, COR_PRF_MONITOR_ENTERLEAVE를 이용한 방식이 가장 안전하고 구현하기가 쉽습니다.
COR_PRF_MONITOR_JIT_COMPILATION은, ... 헤쳐 나가야 할 난관이 많습니다. ^^ 사실, 저도 '대부분의 메서드'는 변경해도, 아직 '모든 메서드'에 대해서 안전하게 IL 코드를 변경하는 방법은 모릅니다.
정성태
2011-03-05 02시42분
[안기석] 답면 감사합니다.

말씀하신 바 대로 COR_PRF_MONITOR_ENTERLEAVE 를 사용할 경우 안전한 대신 상당한 오버헤드가 발생함을 몸소 체험하겠습니다^^
그런데 COR_PRF_MONITOR_ENTERLEAVE 와 COR_PRF_MONITOR_JIT_COMPILATION 에는 다소 차이가 있는 것 같습니다.
COR_PRF_MONITOR_ENTERLEAVE 는 모든 메소드(닷넷프레임웍 제공 메소드)들이 수행시작과 끝에서 콜백을 받을 수 있는 반면에
COR_PRF_MONITOR_JIT_COMPILATION 는 실제 호출되는 메소드의 JIT 컴파일이(수행이 아니고) 시작되고 끝나는 시점에 콕백을 받을 수 있는 것 같습니다.

그런데 COR_PRF_USE_PROFILE_IMAGES | COR_PRF_MONITOR_JIT_COMPILATION 조합을 사용할 경우에
JIT 컴파일 시작 및 끝이 아닌 실제 메소드 수행의 시작과 끝에서 콜백을 받을 수 있다고 이해해도 될른지요?
그리고 특정 메소드의 수행 시작과 끝을 알려면 CLR로 특정 메소드를 어떻게든 전달을 해야할 것 같은데
어떤 방법으로 전달하는지 궁금합니다. 혹시 알고 계시면 조언 부탁 드립니다.
[guest]
2011-03-05 07시34분
뭔가 오해가 있으신 것 같은데요. COR_PRF_USE_PROFILE_IMAGES 옵션은 NGen 된 것들을 쓰지 않는 옵션일 뿐, '실제 메서드 수행의 시작과 끝'에서 콜백을 받는 용도가 아닙니다.

이야기가 자꾸 ^^; 반복되는데요. '메서드 수행의 시작과 끝'에서 콜백을 받는 것은 COR_PRF_MONITOR_ENTERLEAVE이고, '메서드 JIT 컴파일 전/후'에 콜백을 받는 것은 COR_PRF_MONITOR_JIT_COMPILATION입니다. 단지, COR_PRF_MONITOR_JIT_COMPILATION 콜백에서 IL 메서드를 조작하면 그 IL 코드가 '메서드의 수행 시작/끝'에서 수행되도록 만들 수 있는 것입니다.
정성태
2011-03-07 08시15분
[안기석] 바쁘실텐데도 고마운 답변 남겨주셔서 감사합니다.
COR_PRF_MONITOR_JIT_COMPILATION 콜백에서의 IL 조작으로
'실제 메서드 수행의 시작과 끝'에서 콜백을 받을 수 있게 한다는 생각은 못했었는데,
그 부분을 공부해보도록 하겠습니다.

좋은 한주 되시길 바랍니다.
[guest]
2011-03-11 02시17분
[안기석] 안녕하셨습니까?
COR_PRF_MONITOR_JIT_COMPILATION .. 해쳐나가야 할 난관앞에 머리칼이 많이 빠지고 있습니다.

Creating an IL-rewriting profiler
; http://blogs.msdn.com/davbr/archive/2007/03/06/creating-an-il-rewriting-profiler.aspx
의 아래부분에 있는 example 이 있는 링크(http://msdn.microsoft.com/en-us/magazine/cc188743.aspx)
에서 다운 받은 NETProfilingAPI.exe 를 보면서 공부 하고 있습니다.

JITCompilationStarted 콜백함수 내에서
최초 호출된 매서드의 IL 코드 내용 중간에 Prolog, Epilog 를 삽입하는 형태로
매서드의 실행, 종료를 모니터링 할 수 있겠다고 판단이 되어 집니다.

예제 코드에서 보여주는 Prolog, Epilog 를 삽입하는 모습은
좀 당황스럽기까지 합니다. memcpy 를 사용해서 IL 코드를 하나하나 넣는 것 같이 보여서요.

여기까지 따라오면서 든 생각이..

원본 IL에 원하는 코드를 삽입하는 형태는 최종적으로 IL 의 형태로 삽입되어야 하지 않겠는가 하는 것이고
삽입하고자 하는 기능(예를 들어 현재 시간을 로그로 남기는 기능) 을 IL 의 형태로 만드는 함수가 필요할 것 같다.
그렇다면 현재 시간을 로그로 남기는 기능을 가진 함수는 어떤 언어로 만들어 져야 하는 것인가?
C# 으로 만들어진 프로그램의 매서드 내에 시간을 삽입하려면 C# 코드로 작성되어야 하지 않을까?
C++ 로 만들고 있는 프로파일러 내에서 어떻게 C# 코드로 만들어진 함수를 추가할 것인가?

이런 저런 막막한 생각이 막 꼬여 있습니다.

정성태 님께서 보시기에 제가 제대로 생각하고 있는 것인지? 아니면 뭔가 또 착각 혹은 오류를 범하고 있는 것인지
좀 봐주시길 부탁드립니다.

아울러 어떻게 풀어 나가야 하는지 힌트도 부탁드립니다.
[guest]
2011-03-11 05시15분
저도 좀 당황스러웠습니다. ^^

일단, C++로 작업을 해야 하긴 하지만 IL 코드에 대해 바이트 단위로 원본 IL 코드 배열에 추가해 주면 됩니다. 예를 들어, a = 0;이라는 코드를 추가하고 싶다면 a 변수를 해당 Method의 로컬 변수 셋에 int32 타입으로 추가시켜 주어야 하고, IL 코드를 그에 맞게 넣어주어야 합니다. 예를 들어, 아래와 같은 식이겠죠.

ldnull (바이트 코드 값으로 0x14)
stloc.1 (바이트 코드 값으로 0x0B)

^^; 이전에 말씀드렸지만... 매우 복잡합니다.

위와 같이 IL 코드를 넣었다고 해서 끝나는 것이 아닙니다.

.NET 메서드는 정해진 기준에 따라 header 타입이 tiny와 fat으로 나뉘게 되는데, 만약 Tiny Header를 가진 메서드의 IL 코드를 넣어주다가 용량이 넘어버리면 헤더까지 Fat Header로 바꿔주어야 하는 등의... 아뭏든 심각하게 복잡한 상황에 빠지게 됩니다. 게다가 try/catch 핸들러는 변경된 IL 코드 뭉치를 이해하지 못하기 때문에 그런 handler에 대해 일일이 방문하면서 변경된 IL로의 옵셋값으로 바꿔주어야 합니다.

그런 거 하나하나 풀어가다 보면, .NET에 대한 많은 이해를 하게 될 것입니다. (아니면... 차라리 속도가 늦어도 COR_PRF_MONITOR_ENTERLEAVE를 이용하는 것이 나을 것 같다는... 심각한 고민도 하게 될 것입니다)
정성태
2011-03-11 06시42분
[안기석] 같은 질문이 세번이나 올라갔던 것 중 두개를 지워주셔서 감사합니다.
저도 볼때마다 민망했었는데^^

답변 감사합니다

저만 당황스러운게 아니었군요.
그리고 제대로 가고 있는 것 같아서 다행입니다.

아직 가야 할 길이 멀다는 생각이 듭니다.

주말 잘 보내세요~~
[guest]
2011-03-16 11시01분
[안기석] 한발씩 가고는 있는데, 한걸음 나아가기가 더딥니다. 응원해주세요^^

나름대로 생각한바를 말씀드려보겠습니다.
일단 prolog 는 놔두고 epilog 만 붙여 보았습니다.

1. 새 프로젝트를 만든 후 추가하고 싶은 코드를 C# 에서 만든다.

public void SayHello()
{
    Console.Out.WriteLine("Hello world");
}

2. 1을 컴파일해서 IL code 의 body 를 구한다.
==>0,40,52,0,0,10,114,85,1,0,112,111,53,0,0,10,0,42

3. JITCompilationStarted 콜백 내에서
프로파일링 대상 메서드의 마지막 부분에서 0x2A 를 제거하고
새로 만들어진 2를 연결시키고 최종적으로 0x2A 를 붙인다.

=================================================================
이렇게 해보았는데, 프로파일링 대상 프로그램이
정상적으로 작동을 하지 않네요.

JITCompilationStarted 까지는 로그가 남아있고
JITCompilationFinished 는 로그가 없습니다.

New method 의 덤프까지 떠진걸로 봐서는
JITCompilationStarted 콜백의 맨 아랫단까지는 진행이 된 것 같은데..

비교를 위해서
0,40,52,0,0,10,114,85,1,0,112,111,53,0,0,10,0,42 대신에
nop(0x00) 만 잔뜩 넣었을 경우에는 정상적으로 작동을 하구요.

말씀하셨던 tiny 혹은 Fat 문제는 일어나지 않고 지나갈 수 있도록
최소한의 코드만 추가해서 실행시켜 보았구요,
Tiny 의 경우 header(1Byte) 의 상위 6bit(body size)도 정상적으로
변경되었고..

어느 부분을 점검해 봐야 하는지 좀 막혀있습니다.

바쁘시겠지만, 지도조언 부탁 드립니다.

좋은밤 되세요~
[guest]
2011-03-18 10시35분
추가한 코드를 부분으로 나눠서 넣어보세요. 분명 어느 부분부터 추가했을 때 오류가 나는 경우가 있을 것입니다.

그리고, Console.Out.WriteLine의 IL 코드를 그대로 떠서 붙이는 것이 옳은 방법이 아닙니다. Console 개체의 토큰과 Out 토큰, WriteLine 토큰 값이 해당 프로그램에서 이미 사용되어져서 동일한 토큰이 사용된다면 오류가 없겠지만, 처음 사용되는 것이라면 유효하지 않기 때문에 이 부분을 런타임 시에 동적으로 토큰값을 구해서 IL 코드에 넣어야 합니다.
정성태
2015-11-11 05시56분
GC Heap and Alignment Padding
; (broken) http://blogs.msdn.com/b/davbr/archive/2011/12/29/gc-heap-and-alignment-padding.aspx
; https://github.com/Potapy4/dotnet-coreclr/blob/master/Documentation/Profiling/davbr-blog-archive/GC%20Heap%20and%20Alignment%20Padding.md
정성태
2015-11-11 05시57분
When is it safe to use ObjectIDs?
; (broken) http://blogs.msdn.com/b/davbr/archive/2011/12/29/when-is-it-safe-to-use-objectids.aspx
; https://github.com/Potapy4/dotnet-coreclr/blob/master/Documentation/Profiling/davbr-blog-archive/When%20is%20it%20safe%20to%20use%20ObjectIDs.md
정성태

[1]  2  3  4  5  6  7  8  9  10  11  12  13  14  15  ...
NoWriterDateCnt.TitleFile(s)
13607정성태4/25/2024185닷넷: 2248.C# - 인터페이스 타입의 다중 포인터를 인자로 갖는 C/C++ 함수 연동
13606정성태4/24/2024201닷넷: 2247. C# - tensorflow 연동 (MNIST 예제)파일 다운로드1
13605정성태4/23/2024373닷넷: 2246. C# - Python.NET을 이용한 파이썬 소스코드 연동파일 다운로드1
13604정성태4/22/2024407오류 유형: 901. Visual Studio - Unable to set the next statement. Set next statement cannot be used in '[Exception]' call stack frames.
13603정성태4/21/2024704닷넷: 2245. C# - IronPython을 이용한 파이썬 소스코드 연동파일 다운로드1
13602정성태4/20/2024801닷넷: 2244. C# - PCM 오디오 데이터를 연속(Streaming) 재생 (Windows Multimedia)파일 다운로드1
13601정성태4/19/2024850닷넷: 2243. C# - PCM 사운드 재생(NAudio)파일 다운로드1
13600정성태4/18/2024879닷넷: 2242. C# - 관리 스레드와 비관리 스레드
13599정성태4/17/2024869닷넷: 2241. C# - WAV 파일의 PCM 사운드 재생(Windows Multimedia)파일 다운로드1
13598정성태4/16/2024892닷넷: 2240. C# - WAV 파일 포맷 + LIST 헤더파일 다운로드2
13597정성태4/15/2024880닷넷: 2239. C# - WAV 파일의 PCM 데이터 생성 및 출력파일 다운로드1
13596정성태4/14/20241068닷넷: 2238. C# - WAV 기본 파일 포맷파일 다운로드1
13595정성태4/13/20241051닷넷: 2237. C# - Audio 장치 열기 (Windows Multimedia, NAudio)파일 다운로드1
13594정성태4/12/20241069닷넷: 2236. C# - Audio 장치 열람 (Windows Multimedia, NAudio)파일 다운로드1
13593정성태4/8/20241084닷넷: 2235. MSBuild - AccelerateBuildsInVisualStudio 옵션
13592정성태4/2/20241219C/C++: 165. CLion으로 만든 Rust Win32 DLL을 C#과 연동
13591정성태4/2/20241199닷넷: 2234. C# - WPF 응용 프로그램에 Blazor App 통합파일 다운로드1
13590정성태3/31/20241079Linux: 70. Python - uwsgi 응용 프로그램이 k8s 환경에서 OOM 발생하는 문제
13589정성태3/29/20241154닷넷: 2233. C# - 프로세스 CPU 사용량을 나타내는 성능 카운터와 Win32 API파일 다운로드1
13588정성태3/28/20241268닷넷: 2232. C# - Unity + 닷넷 App(WinForms/WPF) 간의 Named Pipe 통신 [2]파일 다운로드1
13587정성태3/27/20241170오류 유형: 900. Windows Update 오류 - 8024402C, 80070643
13586정성태3/27/20241336Windows: 263. Windows - 복구 파티션(Recovery Partition) 용량을 늘리는 방법
13585정성태3/26/20241131Windows: 262. PerformanceCounter의 InstanceName에 pid를 추가한 "Process V2"
13584정성태3/26/20241249개발 환경 구성: 708. Unity3D - C# Windows Forms / WPF Application에 통합하는 방법파일 다운로드1
13583정성태3/25/20241471Windows: 261. CPU Utilization이 100% 넘는 경우를 성능 카운터로 확인하는 방법
[1]  2  3  4  5  6  7  8  9  10  11  12  13  14  15  ...