성태의 닷넷 이야기
홈 주인
모아 놓은 자료
프로그래밍
질문/답변
사용자 관리
사용자
메뉴
아티클
외부 아티클
유용한 코드
온라인 기능
MathJax 입력기
최근 덧글
[정성태] Roll A Lisp In C - Reading ; https...
[정성태] Java - How to use the Foreign Funct...
[정성태] 제가 큰 실수를 했군요. ^^; Delegate를 통한 Bein...
[정성태] Working with Rust Libraries from C#...
[정성태] Detecting blocking calls using asyn...
[정성태] 아쉽게도, 커뮤니티는 아니고 개인 블로그입니다. ^^
[정성태] 질문이 잘 이해가 안 됩니다. 우선, 해당 소스코드에서 ILis...
[양승조
] var대신 dinamic으로 선언해서 해결은 했습니다. 맞는 해...
[양승조
] 또 막혔습니다. ㅠㅠ var list = props[i].Ge...
[양승조
] 아. 감사합니다. 어제는 안됐던것 같은데....정신을 차려야겠네...
글쓰기
제목
이름
암호
전자우편
HTML
홈페이지
유형
제니퍼 .NET
닷넷
COM 개체 관련
스크립트
VC++
VS.NET IDE
Windows
Team Foundation Server
디버깅 기술
오류 유형
개발 환경 구성
웹
기타
Linux
Java
DDK
Math
Phone
Graphics
사물인터넷
부모글 보이기/감추기
내용
<div style='display: inline'> <h1 style='font-family: Malgun Gothic, Consolas; font-size: 20pt; color: #006699; text-align: center; font-weight: bold'>기본적인 CLR Profiler 소스 코드 설명</h1> <p> 사실, 기본 뼈대 만드는 방법을 다음의 글에 이미 소개했었는데요.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > 라이선스까지도 뛰어넘는 .NET Profiler ; <a target='tab' href='http://www.sysnet.pe.kr/2/0/1046'>http://www.sysnet.pe.kr/2/0/1046</a> </pre> <br /> 소스 코드를 포함시켜 두지 않았군요. ^^;;; 그래서, 가장 기본적인 코드만 만족하는 CLR Profiler 예제 소스 코드를 만들어 봤습니다.<br /> <br /> <hr style='width: 50%' /><br /> <br /> 우선, "Visual C++" / "ATL" 범주의 "ATLProject" 유형으로 프로젝트를 생성해 줍니다. <br /> <br /> 그다음 솔루션 탐색기에서 생성된 프로젝트에 대해 마우스 우클릭을 해 "Add" / "Class" 메뉴를 선택하고 뜨는 마법사 창에서 "Visual C++" / "ATL" 범주의 "ATL Simple Object"를 추가합니다. (이 글에서는 추가된 클래스 명이 CBasicClrProfiler)<br /> <br /> 기본적인 COM 객체가 만들어졌으니, 이제 CLR Profiler의 규약에 맞는 작업을 추가합니다. 먼저, ICorProfilerCabllback 인터페이스를 구현해야 하는데, 이를 위해 다음과 같은 기본 뼈대를 이미 갖춘 Impl 헤더 파일을 마련해 프로젝트에 추가합니다.<br /> <br /> <pre style='height: 400px; margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > // ICorProfilerCallbackImpl.h #pragma once #include <cor.h> #include <corprof.h> #include <atlbase.h> #pragma comment (lib, "corguids.lib") template<class T> class ATL_NO_VTABLE ICorProfilerCallback3Impl : public ICorProfilerCallback3 { public: ICorProfilerCallback3Impl() {}; virtual ~ICorProfilerCallback3Impl() {}; STDMETHOD(QueryInterface)(REFIID riid, void** ppvObject) = 0; _ATL_DEBUG_ADDREF_RELEASE_IMPL(ICorProfilerCallback3Impl) STDMETHOD(Initialize)(IUnknown *pICorProfilerInfoUnk) { UNREFERENCED_PARAMETER(pICorProfilerInfoUnk); return E_NOTIMPL; } STDMETHOD(Shutdown)() { return E_NOTIMPL; } STDMETHOD(AppDomainCreationStarted)(AppDomainID appDomainId) { UNREFERENCED_PARAMETER(appDomainId); return E_NOTIMPL; } STDMETHOD(AppDomainCreationFinished)(AppDomainID appDomainId, HRESULT hrStatus) { UNREFERENCED_PARAMETER(appDomainId); UNREFERENCED_PARAMETER(hrStatus); return E_NOTIMPL; } STDMETHOD(AppDomainShutdownStarted)(AppDomainID appDomainId) { UNREFERENCED_PARAMETER(appDomainId); return E_NOTIMPL; } STDMETHOD(AppDomainShutdownFinished)(AppDomainID appDomainId, HRESULT hrStatus) { UNREFERENCED_PARAMETER(appDomainId); UNREFERENCED_PARAMETER(hrStatus); return E_NOTIMPL; } STDMETHOD(AssemblyLoadStarted)(AssemblyID assemblyId) { UNREFERENCED_PARAMETER(assemblyId); return S_OK; } STDMETHOD(AssemblyLoadFinished)(AssemblyID assemblyId, HRESULT hrStatus) { UNREFERENCED_PARAMETER(assemblyId); UNREFERENCED_PARAMETER(hrStatus); return S_OK; } STDMETHOD(AssemblyUnloadStarted)(AssemblyID assemblyId) { UNREFERENCED_PARAMETER(assemblyId); return E_NOTIMPL; } STDMETHOD(AssemblyUnloadFinished)(AssemblyID assemblyId, HRESULT hrStatus) { UNREFERENCED_PARAMETER(assemblyId); UNREFERENCED_PARAMETER(hrStatus); return E_NOTIMPL; } STDMETHOD(ModuleLoadStarted)(ModuleID moduleId) { UNREFERENCED_PARAMETER(moduleId); return S_OK; } STDMETHOD(ModuleLoadFinished)(ModuleID moduleId, HRESULT hrStatus) { UNREFERENCED_PARAMETER(moduleId); UNREFERENCED_PARAMETER(hrStatus); return S_OK; } STDMETHOD(ModuleUnloadStarted)(ModuleID moduleId) { UNREFERENCED_PARAMETER(moduleId); return E_NOTIMPL; } STDMETHOD(ModuleUnloadFinished)(ModuleID moduleId, HRESULT hrStatus) { UNREFERENCED_PARAMETER(moduleId); UNREFERENCED_PARAMETER(hrStatus); return E_NOTIMPL; } STDMETHOD(ModuleAttachedToAssembly)(ModuleID moduleId, AssemblyID assemblyId) { UNREFERENCED_PARAMETER(moduleId); UNREFERENCED_PARAMETER(assemblyId); return S_OK; } STDMETHOD(ClassLoadStarted)(ClassID classId) { UNREFERENCED_PARAMETER(classId); return E_NOTIMPL; } STDMETHOD(ClassLoadFinished)(ClassID classId, HRESULT hrStatus) { UNREFERENCED_PARAMETER(classId); UNREFERENCED_PARAMETER(hrStatus); return E_NOTIMPL; } STDMETHOD(ClassUnloadStarted)(ClassID classId) { UNREFERENCED_PARAMETER(classId); return E_NOTIMPL; } STDMETHOD(ClassUnloadFinished)(ClassID classId, HRESULT hrStatus) { UNREFERENCED_PARAMETER(classId); UNREFERENCED_PARAMETER(hrStatus); return E_NOTIMPL; } STDMETHOD(FunctionUnloadStarted)(FunctionID functionId) { UNREFERENCED_PARAMETER(functionId); return E_NOTIMPL; } STDMETHOD(JITCompilationStarted)(FunctionID functionId, BOOL fIsSafeToBlock) { UNREFERENCED_PARAMETER(functionId); UNREFERENCED_PARAMETER(fIsSafeToBlock); return S_OK; } STDMETHOD(JITCompilationFinished)(FunctionID functionId, HRESULT hrStatus, BOOL fIsSafeToBlock) { UNREFERENCED_PARAMETER(functionId); UNREFERENCED_PARAMETER(hrStatus); UNREFERENCED_PARAMETER(fIsSafeToBlock); return S_OK; } STDMETHOD(JITCachedFunctionSearchStarted)(FunctionID functionId, BOOL * pbUseCachedFunction) { UNREFERENCED_PARAMETER(functionId); UNREFERENCED_PARAMETER(pbUseCachedFunction); return E_NOTIMPL; } STDMETHOD(JITCachedFunctionSearchFinished)(FunctionID functionId, COR_PRF_JIT_CACHE result) { UNREFERENCED_PARAMETER(functionId); UNREFERENCED_PARAMETER(result); return E_NOTIMPL; } STDMETHOD(JITFunctionPitched)(FunctionID functionId) { UNREFERENCED_PARAMETER(functionId); return E_NOTIMPL; } STDMETHOD(JITInlining)(FunctionID callerId, FunctionID calleeId, BOOL * pfShouldInline) { UNREFERENCED_PARAMETER(callerId); UNREFERENCED_PARAMETER(calleeId); UNREFERENCED_PARAMETER(pfShouldInline); return E_NOTIMPL; } STDMETHOD(ThreadCreated)(ThreadID threadId) { UNREFERENCED_PARAMETER(threadId); return E_NOTIMPL; } STDMETHOD(ThreadDestroyed)(ThreadID threadId) { UNREFERENCED_PARAMETER(threadId); return E_NOTIMPL; } STDMETHOD(ThreadAssignedToOSThread)(ThreadID managedThreadId, ULONG osThreadId) { UNREFERENCED_PARAMETER(managedThreadId); UNREFERENCED_PARAMETER(osThreadId); return E_NOTIMPL; } STDMETHOD(RemotingClientInvocationStarted)() { return E_NOTIMPL; } STDMETHOD(RemotingClientSendingMessage)(GUID * pCookie, BOOL fIsAsync) { UNREFERENCED_PARAMETER(pCookie); UNREFERENCED_PARAMETER(fIsAsync); return E_NOTIMPL; } STDMETHOD(RemotingClientReceivingReply)(GUID * pCookie, BOOL fIsAsync) { UNREFERENCED_PARAMETER(pCookie); UNREFERENCED_PARAMETER(fIsAsync); return E_NOTIMPL; } STDMETHOD(RemotingClientInvocationFinished)() { return E_NOTIMPL; } STDMETHOD(RemotingServerReceivingMessage)(GUID * pCookie, BOOL fIsAsync) { UNREFERENCED_PARAMETER(pCookie); UNREFERENCED_PARAMETER(fIsAsync); return E_NOTIMPL; } STDMETHOD(RemotingServerInvocationStarted)() { return E_NOTIMPL; } STDMETHOD(RemotingServerInvocationReturned)() { return E_NOTIMPL; } STDMETHOD(RemotingServerSendingReply)(GUID * pCookie, BOOL fIsAsync) { UNREFERENCED_PARAMETER(pCookie); UNREFERENCED_PARAMETER(fIsAsync); return E_NOTIMPL; } STDMETHOD(UnmanagedToManagedTransition)(FunctionID functionId, COR_PRF_TRANSITION_REASON reason) { UNREFERENCED_PARAMETER(functionId); UNREFERENCED_PARAMETER(reason); return E_NOTIMPL; } STDMETHOD(ManagedToUnmanagedTransition)(FunctionID functionId, COR_PRF_TRANSITION_REASON reason) { UNREFERENCED_PARAMETER(functionId); UNREFERENCED_PARAMETER(reason); return E_NOTIMPL; } STDMETHOD(RuntimeSuspendStarted)(COR_PRF_SUSPEND_REASON suspendReason) { UNREFERENCED_PARAMETER(suspendReason); return E_NOTIMPL; } STDMETHOD(RuntimeSuspendFinished)() { return E_NOTIMPL; } STDMETHOD(RuntimeSuspendAborted)() { return E_NOTIMPL; } STDMETHOD(RuntimeResumeStarted)() { return E_NOTIMPL; } STDMETHOD(RuntimeResumeFinished)() { return E_NOTIMPL; } STDMETHOD(RuntimeThreadSuspended)(ThreadID threadId) { UNREFERENCED_PARAMETER(threadId); return E_NOTIMPL; } STDMETHOD(RuntimeThreadResumed)(ThreadID threadId) { UNREFERENCED_PARAMETER(threadId); return E_NOTIMPL; } STDMETHOD(MovedReferences)(ULONG cMovedObjectIDRanges, ObjectID oldObjectIDRangeStart[], ObjectID newObjectIDRangeStart[], ULONG cObjectIDRangeLength[]) { UNREFERENCED_PARAMETER(cMovedObjectIDRanges); UNREFERENCED_PARAMETER(oldObjectIDRangeStart); UNREFERENCED_PARAMETER(newObjectIDRangeStart); UNREFERENCED_PARAMETER(cObjectIDRangeLength); return E_NOTIMPL; } STDMETHOD(ObjectAllocated)(ObjectID objectId, ClassID classId) { UNREFERENCED_PARAMETER(objectId); UNREFERENCED_PARAMETER(classId); return E_NOTIMPL; } STDMETHOD(ObjectsAllocatedByClass)(ULONG cClassCount, ClassID classIds[], ULONG cObjects[]) { UNREFERENCED_PARAMETER(cClassCount); UNREFERENCED_PARAMETER(classIds); UNREFERENCED_PARAMETER(cObjects); return E_NOTIMPL; } STDMETHOD(ObjectReferences)(ObjectID objectId, ClassID classId, ULONG cObjectRefs, ObjectID objectRefIds[]) { UNREFERENCED_PARAMETER(objectId); UNREFERENCED_PARAMETER(classId); UNREFERENCED_PARAMETER(cObjectRefs); UNREFERENCED_PARAMETER(objectRefIds); return E_NOTIMPL; } STDMETHOD(RootReferences)(ULONG cRootRefs, ObjectID rootRefIds[]) { UNREFERENCED_PARAMETER(cRootRefs); UNREFERENCED_PARAMETER(rootRefIds); return E_NOTIMPL; } STDMETHOD(ExceptionThrown)(ObjectID thrownObjectId) { UNREFERENCED_PARAMETER(thrownObjectId); return E_NOTIMPL; } STDMETHOD(ExceptionSearchFunctionEnter)(FunctionID functionId) { UNREFERENCED_PARAMETER(functionId); return E_NOTIMPL; } STDMETHOD(ExceptionSearchFunctionLeave)() { return E_NOTIMPL; } STDMETHOD(ExceptionSearchFilterEnter)(FunctionID functionId) { UNREFERENCED_PARAMETER(functionId); return E_NOTIMPL; } STDMETHOD(ExceptionSearchFilterLeave)() { return E_NOTIMPL; } STDMETHOD(ExceptionSearchCatcherFound)(FunctionID functionId) { UNREFERENCED_PARAMETER(functionId); return E_NOTIMPL; } STDMETHOD(ExceptionOSHandlerEnter)(FunctionID functionId) { UNREFERENCED_PARAMETER(functionId); return E_NOTIMPL; } STDMETHOD(ExceptionOSHandlerLeave)(FunctionID functionId) { UNREFERENCED_PARAMETER(functionId); return E_NOTIMPL; } STDMETHOD(ExceptionUnwindFunctionEnter)(FunctionID functionId) { UNREFERENCED_PARAMETER(functionId); return E_NOTIMPL; } STDMETHOD(ExceptionUnwindFunctionLeave)() { return E_NOTIMPL; } STDMETHOD(ExceptionUnwindFinallyEnter)(FunctionID functionId) { UNREFERENCED_PARAMETER(functionId); return E_NOTIMPL; } STDMETHOD(ExceptionUnwindFinallyLeave)() { return E_NOTIMPL; } STDMETHOD(ExceptionCatcherEnter)(FunctionID functionId, ObjectID objectId) { UNREFERENCED_PARAMETER(functionId); UNREFERENCED_PARAMETER(objectId); return E_NOTIMPL; } STDMETHOD(ExceptionCatcherLeave)() { return E_NOTIMPL; } STDMETHOD(COMClassicVTableCreated)(ClassID wrappedClassId, REFGUID implementedIID, void *pVTable, ULONG cSlots) { UNREFERENCED_PARAMETER(wrappedClassId); UNREFERENCED_PARAMETER(implementedIID); UNREFERENCED_PARAMETER(pVTable); UNREFERENCED_PARAMETER(cSlots); return E_NOTIMPL; } STDMETHOD(COMClassicVTableDestroyed)(ClassID wrappedClassId, REFGUID implementedIID, void *pVTable) { UNREFERENCED_PARAMETER(wrappedClassId); UNREFERENCED_PARAMETER(implementedIID); UNREFERENCED_PARAMETER(pVTable); return E_NOTIMPL; } STDMETHOD(ExceptionCLRCatcherFound)(void) { return E_NOTIMPL; } STDMETHOD(ExceptionCLRCatcherExecute)(void) { return E_NOTIMPL; } STDMETHOD(ThreadNameChanged)(ThreadID threadId, ULONG cchName, WCHAR* name) { UNREFERENCED_PARAMETER(threadId); UNREFERENCED_PARAMETER(cchName); UNREFERENCED_PARAMETER(name); return E_NOTIMPL; } STDMETHOD(GarbageCollectionStarted)( /* [in] */ int cGenerations, /* [length_is][size_is][in] */ BOOL generationCollected[ ], /* [in] */ COR_PRF_GC_REASON reason) { UNREFERENCED_PARAMETER(cGenerations); UNREFERENCED_PARAMETER(generationCollected); UNREFERENCED_PARAMETER(reason); return E_NOTIMPL; } STDMETHOD(SurvivingReferences)( /* [in] */ ULONG cSurvivingObjectIDRanges, /* [size_is][in] */ ObjectID objectIDRangeStart[ ], /* [size_is][in] */ ULONG cObjectIDRangeLength[ ]) { UNREFERENCED_PARAMETER(cSurvivingObjectIDRanges); UNREFERENCED_PARAMETER(objectIDRangeStart); UNREFERENCED_PARAMETER(cObjectIDRangeLength); return E_NOTIMPL; } STDMETHOD(GarbageCollectionFinished)() { return E_NOTIMPL; } STDMETHOD(FinalizeableObjectQueued)( /* [in] */ DWORD finalizerFlags, /* [in] */ ObjectID objectID) { UNREFERENCED_PARAMETER(finalizerFlags); UNREFERENCED_PARAMETER(objectID); return E_NOTIMPL; } STDMETHOD(RootReferences2)( /* [in] */ ULONG cRootRefs, /* [size_is][in] */ ObjectID rootRefIds[ ], /* [size_is][in] */ COR_PRF_GC_ROOT_KIND rootKinds[ ], /* [size_is][in] */ COR_PRF_GC_ROOT_FLAGS rootFlags[ ], /* [size_is][in] */ UINT_PTR rootIds[ ]) { UNREFERENCED_PARAMETER(cRootRefs); UNREFERENCED_PARAMETER(rootRefIds); UNREFERENCED_PARAMETER(rootKinds); UNREFERENCED_PARAMETER(rootFlags); UNREFERENCED_PARAMETER(rootIds); return E_NOTIMPL; } STDMETHOD(HandleCreated)( /* [in] */ GCHandleID handleId, /* [in] */ ObjectID initialObjectId) { UNREFERENCED_PARAMETER(handleId); UNREFERENCED_PARAMETER(initialObjectId); return E_NOTIMPL; } STDMETHOD(HandleDestroyed)( /* [in] */ GCHandleID handleId) { UNREFERENCED_PARAMETER(handleId); return E_NOTIMPL; } STDMETHOD(InitializeForAttach)(/* [in] */ IUnknown *pCorProfilerInfoUnk, /* [in] */ void *pvClientData, /* [in] */ UINT cbClientData) { UNREFERENCED_PARAMETER(pCorProfilerInfoUnk); UNREFERENCED_PARAMETER(pvClientData); UNREFERENCED_PARAMETER(cbClientData); return E_NOTIMPL; } STDMETHOD(ProfilerAttachComplete)() { return E_NOTIMPL; } STDMETHOD(ProfilerDetachSucceeded)() { return E_NOTIMPL; } }; </pre> <br /> 우리가 만든 클래스(CBasicClrProfiler)에서는 이를 상속받도록 만들고, COM_INTERFACE_ENTRY를 추가해 줍니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > #pragma once #include "resource.h" // main symbols #include "SampleProfiler_i.h" <span style='color: blue; font-weight: bold'>#include "ICorProfilerCallback3Impl.h"</span> // ..[생략]... class ATL_NO_VTABLE CBasicClrProfiler : // ..[생략]... <span style='color: blue; font-weight: bold'>public ICorProfilerCallback3Impl<CBasicClrProfiler></span> { public: CBasicClrProfiler() { } DECLARE_REGISTRY_RESOURCEID(IDR_BASICCLRPROFILER) BEGIN_COM_MAP(CBasicClrProfiler) COM_INTERFACE_ENTRY(IBasicClrProfiler) COM_INTERFACE_ENTRY(IDispatch) <span style='color: blue; font-weight: bold'>COM_INTERFACE_ENTRY(ICorProfilerCallback) COM_INTERFACE_ENTRY(ICorProfilerCallback2) COM_INTERFACE_ENTRY(ICorProfilerCallback3)</span> END_COM_MAP() // ..[생략]... }; OBJECT_ENTRY_AUTO(__uuidof(BasicClrProfiler), CBasicClrProfiler) </pre> <br /> 여기까지가 CLR이 요구하는 프로파일러 요건의 전부입니다. CLR은 이 클래스를 로드해서 ICorProfilerCallback::Initialize 메서드를 호출하는데, 이 메서드에서 우리는 CLR에게 관심있는 콜백이 무엇인지를 등록해 주면 됩니다.<br /> <br /> 일단, Initialize 메서드까지 불리는 것을 확인해 보고 다음으로 넘어가겠습니다.<br /> <br /> 테스트를 위해 C# EXE 프로젝트를 하나 만들고,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > using System; namespace ConsoleApplication1 { class Program { static void Main(string[] args) { Console.WriteLine("TEST"); Console.ReadLine(); } } } </pre> <br /> C++ 프로파일러 프로젝트의 디버깅 시작 옵션을 C# EXE로 맞춘 후 CLR에게 프로파일러를 로드하도록 환경 변수를 설정해줍니다.<br /> <br /> <img onclick='toggle_img(this)' class='imgView' alt='sample_clr_profiler_1.png' src='/SysWebRes/bbs/sample_clr_profiler_1.png' /><br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > Command: $(SolutionDir)ConsoleApplication1\bin\Debug\ConsoleApplication1.exe Debugger Type: Native Only Environment: COR_ENABLE_PROFILING=1 COR_PROFILER={F89DDF6B-9451-4EA9-8E02-7902E61C0BCC} <a target='tab' href='https://docs.microsoft.com/en-us/previous-versions/dotnet/netframework-4.0/ee471451(v=vs.100)'>COR_PROFILER_PATH=$(TargetPath)</a> </pre> <br /> CLR이 로드 후 단 한번 불러 줄 Initialize 메서드에서 닷넷 모듈이 로드되는 시점의 콜백을 받고 싶다면 다음과 같이 구현해 주면 됩니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > // BasicClrProfiler.h #pragma once #include "resource.h" // main symbols #include "SampleProfiler_i.h" #include "ICorProfilerCallback3Impl.h" // ..[생략]... class ATL_NO_VTABLE CBasicClrProfiler : // ..[생략]... public ICorProfilerCallback3Impl<CBasicClrProfiler> { public: // ..[생략]... <span style='color: blue; font-weight: bold'>STDMETHOD(Initialize)(IUnknown *pICorProfilerInfoUnk); CComQIPtr<ICorProfilerInfo2> m_pICorProfilerInfo2;</span> // ..[생략]... }; OBJECT_ENTRY_AUTO(__uuidof(BasicClrProfiler), CBasicClrProfiler) </pre> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > // BasicClrProfiler.cpp : Implementation of CBasicClrProfiler #include "stdafx.h" #include "BasicClrProfiler.h" HRESULT CBasicClrProfiler::Initialize(IUnknown *pICorProfilerInfoUnk) { if (pICorProfilerInfoUnk == NULL) { return S_OK; } m_pICorProfilerInfo2 = pICorProfilerInfoUnk; DWORD dwEventMask = <a target='tab' href='https://docs.microsoft.com/en-us/dotnet/framework/unmanaged-api/profiling/cor-prf-monitor-enumeration'>COR_PRF_MONITOR_MODULE_LOADS</a>; m_pICorProfilerInfo2-><a target='tab' href='https://docs.microsoft.com/en-us/dotnet/framework/unmanaged-api/profiling/icorprofilerinfo-seteventmask-method'>SetEventMask</a>(dwEventMask); return S_OK; } </pre> <br /> 이제 Initialize에 BP(BreakPoint)를 걸고 "F5" 키를 눌러 디버깅을 시작하면 끝!<br /> <br /> <hr style='width: 50%' /><br /> <br /> COR_PRF_MONITOR_MODULE_LOADS 관련 이벤트를 받겠다고 명시했으니 이제 CLR은 모듈의 로드/언로드에 따라 다음과 같은 콜백을 호출해 줍니다.<br /> <br /> <ul> <li>ModuleLoadStarted</li> <li>ModuleLoadFinished</li> <li>ModuleUnloadStarted</li> <li>ModuleUnloadFinished</li> </ul> <br /> 여기서는 ModuleLoadFinished 콜백을 받아 어떤 모듈이 로드되었는지에 대한 정보를 출력하겠습니다.<br /> <br /> 이를 위해 헤더 파일과 소스 코드에 다음과 같은 코드를 추가합니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > // BasicClrProfiler.h #pragma once #include "resource.h" // main symbols #include "SampleProfiler_i.h" #include "ICorProfilerCallback3Impl.h" // ..[생략]... class ATL_NO_VTABLE CBasicClrProfiler : // ..[생략]... public ICorProfilerCallback3Impl<CBasicClrProfiler> { public: // ..[생략]... <span style='color: blue; font-weight: bold'>STDMETHOD(ModuleLoadFinished)(ModuleID moduleId, HRESULT hrStatus);</span> void OutputDebugText(const wchar_t* format, ...); // ..[생략]... }; OBJECT_ENTRY_AUTO(__uuidof(BasicClrProfiler), CBasicClrProfiler) </pre> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > // BasicClrProfiler.cpp : Implementation of CBasicClrProfiler #include "stdafx.h" #include "BasicClrProfiler.h" #include <Strsafe.h> // ...[생략]... HRESULT CBasicClrProfiler::ModuleLoadFinished(ModuleID moduleId, HRESULT hrStatus) { ULONG cchModule = _MAX_PATH; ULONG rCchModule = 0; AssemblyID assemblyId = 0; LPCBYTE pModuleBaseLoadAddress; wchar_t szModule[_MAX_PATH]; HRESULT hr = m_pICorProfilerInfo2->GetModuleInfo(moduleId, &pModuleBaseLoadAddress, cchModule, &rCchModule, szModule, &assemblyId); OutputDebugText(L"=============== %s loaded at 0x%x, ModuleID = 0x%x\n", szModule, pModuleBaseLoadAddress, moduleId); return S_OK; } void CBasicClrProfiler::OutputDebugText(const wchar_t* format, ...) { wchar_t buffer[4096]; va_list vaList; va_start(vaList, format); StringCchVPrintfW(buffer, 4096, format, vaList); va_end(vaList); OutputDebugString(buffer); } </pre> <br /> 이렇게 하고 실행하면, Debug 창에 다음과 같은 출력을 확인할 수 있습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > C:\WINDOWS\Microsoft.Net\assembly\GAC_32\mscorlib\v4.0_4.0.0.0__b77a5c561934e089\mscorlib.dll loaded at <span style='color: blue; font-weight: bold'>0x6a6b0000</span>, ModuleID = <span style='color: blue; font-weight: bold'>0x6a6b1000</span> C:\...\ConsoleApplication1\bin\Debug\ConsoleApplication1.exe loaded at <span style='color: blue; font-weight: bold'>0x5d0000</span>, ModuleID = <span style='color: blue; font-weight: bold'>0x973fbc</span> </pre> <br /> 이 때 출력되는 "loaded at" 주소 값은 windbg.exe에서 "lm" 명령으로 확인했을 때의 모듈 로드 주소입니다. 그리고 ModuleID는 다음과 같이 !name2ee로 출력했을 때의 Module 값에 대응합니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > 0:007> <span style='color: blue; font-weight: bold'>!name2ee</span> *!ConsoleApplication1 Module: <span style='color: blue; font-weight: bold'>6a6b1000</span> Assembly: mscorlib.dll -------------------------------------- Module: <span style='color: blue; font-weight: bold'>00973fbc</span> Assembly: ConsoleApplication1.exe </pre> <br /> 이 정도면, 대충 프로파일러가 돌아가는 구조가 눈에 보이실 것입니다.<br /> <br /> (<a target='tab' href='http://www.sysnet.pe.kr/bbs/DownloadAttachment.aspx?fid=1016&boardid=331301885'>첨부한 파일은 이 글의 예제 코드를 포함</a>합니다.)<br /> </p><br /> <br /><hr /><span style='color: Maroon'>[이 글에 대해서 여러분들과 의견을 공유하고 싶습니다. 틀리거나 미흡한 부분 또는 의문 사항이 있으시면 언제든 댓글 남겨주십시오.]</span> </div>
첨부파일
스팸 방지용 인증 번호
1028
(왼쪽의 숫자를 입력해야 합니다.)