Problem using GetFunctionInfo2 to identify the class containing a called function
-
Wednesday, December 30, 2009 2:21 PM
In my profiler, I'm calling GetFunctionInfo2 from the function enter hook to determine which class contains the called method. This appears to work fine, except when generics are used. Without generics:
class Superclass { public virtual void method(string a) { // ... } } class Subclass : Superclass { public override void method(string a) { base.method(a); // ... } }Calling method("foo") on a Subclass object results in 2 calls to the function enter callback, one for the call to Subclass.method and one for the call to Superclass.method. The first time, GetFunctionInfo2 gives the ClassID of Subclass, the second time GetFunctionInfo2 gives the ClassID of Superclass.
However, when extending this with generics:class Superclass<T> { public virtual void method(T a) { // ... } } class Subclass<T> : Superclass<T> { public override void method(T a) { base.method(a); // ... } }Calling method("foo") on an object of type Subclass<string> again results in two calls to the function enter callback, however in both cases calling GetFunctionInfo2 from the callback gives the ClassID of Subclass<string>. Is it possible to distinguish between the Subclass and Superclass, as in the non-generic case, and if so, how?
Thanks,
Paul
All Replies
-
Wednesday, December 30, 2009 6:35 PMModeratorI don't understand why would GetFunctionInfo2 return different output for 2 subsequent calls. Did you by any chance change the input parameter (in the non-generic case)? Are you sure that the FunctionID is the same both times?
-Karel -
Wednesday, December 30, 2009 10:11 PM
Sorry for not making my question clear. I am not getting different output for the same input. What I mean is that there appears to be a difference in behavior between the generic and the non-generic case.
GetFunctionInfo2 is called from the function enter callback. In the above example, that callback is called 4 times, each time with a different FunctionID. The FunctionID (and COR_PRF_FRAME_INFO) is passed on to GetFunctionInfo2 unmodified. So GetFunctionInfo2 is called 4 times, each time with a different FunctionID. The returned ClassIDs are as follows:
Call to managed method GetFunctionInfo2 returns ClassID of Subclass.method Subclass Superclass.method Superclass Subclass<string>.method Subclass<string> Superclass<string>.method Subclass<string> In the non-generic case, the ClassID of the Superclass is returned for Superclass.method's FunctionID, while in the generic case, the ClassID of the Subclass<string> is returned for Superclass<string>.method's FunctionID. Are both cases supposed to behave differently, or should they behave the same way?
Thanks,
Paul- Edited by pm42 Wednesday, December 30, 2009 10:14 PM Add borders to table to make it more readable
-
Wednesday, December 30, 2009 11:24 PM
Did you request COR_PRF_ENABLE_FRAME_INFO with SetEventMask?
Thanks,
Shane -
Thursday, December 31, 2009 9:07 AM
Yes, SetEventMask is called as follows:
m_info->SetEventMask(COR_PRF_MONITOR_THREADS | COR_PRF_MONITOR_ENTERLEAVE | COR_PRF_DISABLE_INLINING | COR_PRF_MONITOR_EXCEPTIONS | COR_PRF_MONITOR_GC | COR_PRF_MONITOR_MODULE_LOADS | COR_PRF_MONITOR_CLASS_LOADS | COR_PRF_ENABLE_FUNCTION_ARGS | COR_PRF_ENABLE_FUNCTION_RETVAL | COR_PRF_ENABLE_FRAME_INFO | COR_PRF_MONITOR_OBJECT_ALLOCATED | COR_PRF_ENABLE_OBJECT_ALLOCATED);
Thanks,
Paul -
Friday, January 01, 2010 1:25 AM
Hi Paul,
Yes, it's a bug in GetFunctionInfo2. Thanks for bringing it to us. Unfortunately, it's too late to be fixed in CLR v4. You can work around with the below code snippet. Please note that it's for illustrative purposes only and is provided "AS IS" and with no warranties.
Thanks,
Shanevoid SampleCallbackImpl::Enter2(FunctionID funcId, UINT_PTR clientData, COR_PRF_FRAME_INFO frameInfo, COR_PRF_FUNCTION_ARGUMENT_INFO * pArgInfo) { ClassID classId; mdToken token; HRESULT hr = m_pProfilerInfo->GetFunctionInfo2(funcId, frameInfo, &classId, NULL, &token, 0, NULL, NULL); _ASSERTE(SUCCEEDED(hr)); mdToken funcToken = mdTypeDefNil; IMetaDataImport *pMDImport = NULL; mdTypeDef typeDefToken; ClassID parentClassId; hr = m_pProfilerInfo->GetTokenAndMetaDataFromFunction(funcId, IID_IMetaDataImport, (IUnknown **)&pMDImport, &funcToken); _ASSERTE(SUCCEEDED(hr)); _ASSERTE(token == funcToken); mdTypeDef classToken = mdTypeDefNil; hr = pMDImport->GetMethodProps(funcToken, &classToken, NULL, 0, 0, NULL, NULL, NULL, NULL, NULL); _ASSERTE(SUCCEEDED(hr)); while (classId != NULL) { hr = m_pProfilerInfo->GetClassIDInfo2(classId, NULL, &typeDefToken, &parentClassId, 0, NULL, NULL); _ASSERTE(SUCCEEDED(hr)); if (typeDefToken == classToken) break; classId = parentClassId; } pMDImport->Release();
_ASSERTE(classId != NULL); // Now you have the correct classId for generic classes. }
- Proposed As Answer by Shane Yuan Friday, January 01, 2010 1:25 AM
- Marked As Answer by pm42 Saturday, January 02, 2010 2:30 PM
-
Saturday, January 02, 2010 2:36 PMThe workaround works fine. Thanks!

