ICorProfilerInfo::GetILToNativeMapping 메서드가 0x80131358을 반환하는 경우
아래의 예제 코드가 있음에도 불구하고,
How do you map a native to IL instruction pointer in-process
; http://stackoverflow.com/questions/198754/how-do-you-map-a-native-to-il-instruction-pointer-in-process
제 경우에는 GetILToNativeMapping 메서드를 아무리 호출해도 0x80131358 값만을 반환하는 군요. ^^;
ULONG32 cNeedMap = 1024;
COR_DEBUG_IL_TO_NATIVE_MAP zeroBuf[1024];
cNeedMap = 0;
hr = pInfo->GetILToNativeMapping(funcId, 0, &cNeedMap, NULL);
// hr == 0x80131358
Error Lookup 프로그램에서 0x80131358 값이 뭔지 조회해도 그런 값을 찾을 수 없다고 나옵니다.
그러다가, 혹시 SSCLI에서 이 답을 구할 수 도 있지 않을까 싶었는데요.
shared-source-cli-2.0 / clr / src / vm / proftoeeinterfaceimpl.cpp
; https://github.com/gbarnett/shared-source-cli-2.0/blob/master/clr/src/vm/proftoeeinterfaceimpl.cpp
GetILToNativeMapping 구현 코드에서 오류로 인해 중간에 return 하는 것을 보니 답이 나옵니다. 바로 CORPROF_E_JITMAPS_NOT_ENABLED의 값이었던 것입니다.
2979 /*
2980 * GetILToNativeMapping returns a map from IL offsets to native
2981 * offsets for this code. An array of COR_DEBUG_IL_TO_NATIVE_MAP
2982 * structs will be returned, and some of the ilOffsets in this array
2983 * may be the values specified in CorDebugIlToNativeMappingTypes.
2984 */
2985 HRESULT ProfToEEInterfaceImpl::GetILToNativeMapping(
2986 /* [in] */ CodeID codeId,
2987 /* [in] */ ULONG32 cMap,
2988 /* [out] */ ULONG32 *pcMap,
2989 /* [out, size_is(cMap), length_is(*pcMap)] */
2990 COR_DEBUG_IL_TO_NATIVE_MAP map[])
2991 {
2992 CONTRACTL
2993 {
2994 SO_NOT_MAINLINE;
2995 THROWS; // MethodDesc::FindOrCreateInstantiationAtObject throws
2996 GC_TRIGGERS; // MethodDesc::FindOrCreateInstantiationAtObject triggers
2997 } CONTRACTL_END;
2998
2999 LOG((LF_CORPROF, LL_INFO1000, "**PROF: GetILToNativeMapping 0x%p.\n", codeId));
3000
3001 ENSURE_CALLBACK_STATE_FLAGS_SET(COR_PRF_CALLBACKSTATE_INCALLBACK);
3002
3003 // If JIT maps are not enabled, then we can't provide it
3004 if (!CORProfilerJITMapEnabled())
3005 return (CORPROF_E_JITMAPS_NOT_ENABLED);
3006
3007 #ifdef DEBUGGING_SUPPORTED
3008 // Cast to proper type
3009 MethodDesc *pMD = CodeIDToMethodDesc(codeId);
3010
3011 // If the code is generic, then GetCodeInfo is not really supported and may not give
3012 // accurate results. This is better than always failing,
3013 // though we may prejudice profiling by which
3014 // copy of the code we return, so we just return none.
3015 if (pMD->HasClassOrMethodInstantiation() && pMD->IsTypicalMethodDefinition())
3016 pMD = pMD->FindOrCreateInstantiationAtObject(); // Can we pass allowCreate=FALSE to make this no trigger?
3017
3018 return (g_pDebugInterface->GetILToNativeMapping(pMD, cMap, pcMap, map));
3019 #else
3020 return E_NOTIMPL;
3021 #endif
3022 }
이 값의 정의는 Visual C++의 CorError.h 헤더 파일에서 찾을 수 있습니다.
#define CORPROF_E_JITMAPS_NOT_ENABLED EMAKEHR(0x1358)
CORPROF_E_JITMAPS_NOT_ENABLED의 원인은 .NET Profiler를 만져본 분들이라면 금방 눈치챌 수 있습니다. .NET Profiler는 callback 이벤트를 받을 유형을 지정할 수가 있는데요. 제 경우에는 해당 옵션을 활성화시키지 않았기 때문이었습니다. 그 옵션의 이름은 COR_PRF_MONITOR::COR_PRF_ENABLE_JIT_MAPS입니다.
COR_PRF_MONITOR Enumeration
; https://docs.microsoft.com/en-us/dotnet/framework/unmanaged-api/profiling/cor-prf-monitor-enumeration
재미있는 것은 문서의 내용입니다. 위의 글에 보면 COR_PRF_ENABLE_JIT_MAPS 옵션에 다음과 같은 내용이 있습니다.
Deprecated.
Allows the profiler to obtain IL-to-native maps by using GetILToNativeMapping. Starting with the .NET Framework 2.0, the runtime always tracks IL-to-native maps; therefore, this flag is always considered to be set.
하지만, 문서의 내용과는 달리 .NET 2.0 이후에도 여전히 해당 옵션을 명시적으로 설정해줘야 하는 것입니다. 결국, 다음과 같이 ICorProfilerInfo2의 SetEventMask를 설정해 주는 것으로 문제 해결!
m_dwEventMask = ...;
m_dwEventMask |= COR_PRF_ENABLE_JIT_MAPS;
m_pICorProfilerInfo2->SetEventMask(m_dwEventMask);
[이 글에 대해서 여러분들과 의견을 공유하고 싶습니다. 틀리거나 미흡한 부분 또는 의문 사항이 있으시면 언제든 댓글 남겨주십시오.]