Locked SetEnterLeaveFunctionHooks3WithInfo not working

  • Wednesday, June 01, 2011 8:10 AM
     
      Has Code

     

    Hi, 

    I am trying to use SetEnterLeaveFunctionHooks3WithInfo and its callback. But I am not able to proceed further. Please help.. This is what I do. I have a couple of questions regarding this as well 

    1. In the initialize method I call 

     m_pCorProfilerInfo3->SetEnterLeaveFunctionHooks3WithInfo(FunctionEnterCallback3, FunctionLeaveCallback3, FunctionTailCallCallback3);

    my callback is this

    void __stdcall FunctionEnterCallback3(FunctionIDOrClientID functionIDOrClientID, COR_PRF_ELT_INFO eltInfo){
      __asm {
    //Set up a stack frame and preserve registers
        push  ebp
        mov   ebp,esp
        pushad
        mov   eax,[ebp+0x14]   //eltInfo
        push  eax
        mov   ecx,[ebp+0x10]   //functionIDOrClientID
        push  ecx
        call  FunctionEnterGlobal
    
    //Restore registers and pop the stack frame
        popad
        pop   ebp
        ret   12
      }
    }
    
    void __stdcall FunctionEnterGlobal(FunctionIDOrClientID functionIDOrClientID, COR_PRF_ELT_INFO eltInfo)
    {
     CProfiler* pProf = reinterpret_cast<CProfiler*>(functionIDOrClientID.clientID);
    
    if(pProf != NULL)
    {
    pProf->Enter3(functionIDOrClientID.functionID, eltInfo);
    }
    }

    I get this error 

    Error 1 error C2664: 'ICorProfilerInfo3::SetEnterLeaveFunctionHooks3WithInfo' : cannot convert parameter 1 from 'void (__stdcall *)(FunctionIDOrClientID,COR_PRF_ELT_INFO)' to 'FunctionEnter3WithInfo (__cdecl *)'

    The MSDN documentation http://msdn.microsoft.com/en-us/library/dd490895.aspx says to __stdcall but I get error.. why? Am I missing something which is basic?

    2. in FunctionIDOrClientID has FunctionID as well as clientID, What is clientID? Is it the client data which is set in SetFunctionIDMapper2? (I am not able to debug because I can't compile)

    3. Also is the assembly code right? (I have my doubts)

     


    ..JPJ..

    • Edited by Jasper paul Wednesday, June 01, 2011 8:13 AM Code alignment
    •  

All Replies

  • Thursday, June 02, 2011 4:16 AM
     
      Has Code

    I haven't used the Info3's yet but my Info2's look like this

    // http://msdn.microsoft.com/en-us/library/aa964981.aspx
    void __stdcall FunctionEnter2Global(
        /*[in]*/FunctionID funcID,
        /*[in]*/UINT_PTR clientData,
        /*[in]*/COR_PRF_FRAME_INFO func,
        /*[in]*/COR_PRF_FUNCTION_ARGUMENT_INFO *argumentInfo)
    {
    }

    First is 32, latter is for 64


    void _declspec(naked) _FunctionEnter2(
        FunctionID funcID,
        UINT_PTR clientData,
        COR_PRF_FRAME_INFO func,
        COR_PRF_FUNCTION_ARGUMENT_INFO *argumentInfo)
    {
        __asm
        {
            push ebp
            mov ebp,esp
            pushad

            mov eax,[ebp+0x14] //argumentInfo
            push eax
            mov ecx,[ebp+0x10] //func
            push ecx
            mov edx,[ebp+0x0C] //clientData
            push edx
            mov eax,[ebp+0x08] //funcID
            push eax
            call FunctionEnter2Global

            popad
            pop ebp
            ret 16
        }
    }

    void _FunctionEnter2(
        FunctionID funcID,
        UINT_PTR clientData,
        COR_PRF_FRAME_INFO func,
        COR_PRF_FUNCTION_ARGUMENT_INFO *argumentInfo)
    {
        FunctionEnter2Global(funcID, clientData, func, argumentInfo);
    }

    The FunctionIDOrClientID is the ID returned from your FunctionIDMapper(2) - some developers return a different ID that can be used to look up in a list or array to access information gathered earlier.


  • Friday, June 03, 2011 6:15 AM
     
     
    Thanks Shaun... I use the same too... and it works very well too... 
    I was trying to use the latest version of the profiler API callback interface. In which the version 3's enter, leave, tailcall with info is available. 
    1. The online doc is wrong I think it gives wrong information.. It says 
    void __stdcall FunctionEnter3WithInfo([in] FunctionIDOrClientID functionIDOrClientID, [in] COR_PRF_ELT_INFO eltInfo);
    but __stdcall gives error. Only _declspec(naked) work with out compilation errors. 
    2. FunctionIDOrClientID which is
    struct
    {
        FunctionID funcId;
        UINT_PTR ClientID;
    }FunctionIDOrClientID;
    Here both funcId and clientId give same value which is clientData from FunctionIDMapper2's client data.  
    I am not able to get the functionId to search my map. Which is weird actually. No functionId is Enter callback.. :)
    There is no example on that anywhere in the net. The closest I could get is a another forum post on the same SetEnterLeaveFunctionHook3WithInfo http://social.msdn.microsoft.com/Forums/en-NZ/netfxtoolsdev/thread/f797056f-8355-4f85-8206-d8898dde3a32. But that did not give me the answer i was looking for. 
    May be Shaun you can try the Info3's if you have time... and post ur experience. :)
    Any help is appreciated... thanks.. 

    ..JPJ..
  • Friday, June 03, 2011 9:03 AM
     
     Answered Has Code

    1)

    The calling convention (in your example __stdcall) and the storage-class information (__declspec) are two different things. The calling convention specifies how the parameters are passed to the function and must be specified in the declaration. The naked storage-class specifier must be defined on the definition of the function - it does not alter any code so it is not considered part of the function's type. 

    So the docs are correct since the method must have the standard calling convention and be implemented with the naked specifier. That is why you should define your function with:

    void __stdcall

    and declare it with:

    void _declspec(naked)

    2) 

     It is difficult to make any observations here unless you post the code you use to implement the FunctionIDMapper2 function. What I observe from own experimentation is that the functions work as expected - I get the value I return from my implementation of the FunctionIDMapper2 function in the functionIdOrClientID parameter of the ELT functions. 

     

    Regarding your assembly. If you are not comfortable using it I suggest you shortcut the code to take advantage of the fact that you are inlining assembly:

    void __stdcall FunctionEnterCallback3(FunctionIDOrClientID functionIDOrClientID, COR_PRF_ELT_INFO eltInfo){
     __asm {
    //Set up a stack frame and preserve registers
      push ebp
      mov  ebp,esp
      pushad
      }
     
     FunctionEnterGlobal( functionIDOrClientID, eltInfo);
    
     __asm {
    //Restore registers and pop the stack frame
      popad
      pop  ebp
      ret  SIZE functionIDOrClientID + SIZE eltInfo
     }
    }
    
    
    Using this approach you don't have to push the parameters onto the stack yourself. Also the 'ret' you have specified in not correct - best to not use magic numbers and just calculate it. This of course only works for 32-bit, for 64-bit you can not use inline assembly and have to implement the entire function in assembly. 

     

    • Proposed As Answer by Paul ZhouModerator Monday, June 06, 2011 3:12 AM
    • Unproposed As Answer by Jasper paul Monday, June 06, 2011 5:26 AM
    • Marked As Answer by Jasper paul Monday, June 06, 2011 5:27 AM
    •  
  • Friday, June 03, 2011 1:06 PM
     
      Has Code

    Thank you Skov for the to-the-point reply. 

    Point 1: Clear.. 

    Point 2: My FunctionIdMapper2 function looks something like this

     

    UINT_PTR CProfiler::FunctionIDMapper2(FunctionID functionId, void* clientData, BOOL *pbHookFunction)
    {
    	UINT_PTR retVal = functionId;
    	CProfiler* pProf = static_cast<CProfiler*>(clientData);
    	assert(pProf != NULL);
    	if(pProf == NULL){
    		return retVal;
    	}
    
    
    //Mapfunction Returns 'this' if successful, other wise returns NULL
    
    retVal = pProf->MapFunction(functionId, true, pbHookFunction); 
    <br/>
    
    	if(retVal == NULL)
    	{
    		if(*pbHookFunction)
    			*pbHookFunction = false;
    		return retVal; //return NULL
    	}
    
    	return retVal;
    }
    

     

    Please let me know if it is ok to implement like this in my FunctionMapper2 callback.

    about my asm: Thanks for clarifying... I'll do as u have suggested. 

     

     


    ..JPJ..

  • Saturday, June 04, 2011 12:30 PM
     
     

    I think you have misunderstood the purpose of FunctionIDMapper2. You use the function to indicate that you want to hook a given function and that you want said function to be re-mapped to an ID of your choosing (optional).

    If you return an opaque pointer to your profiler instance, then that pointer is what you get back in the ELT functions - in the FunctionIDOrClientID parameter. By doing this you effectively loose the information about which function is being called. 

    Is that your intention?


  • Sunday, June 05, 2011 4:05 AM
     
      Has Code

    this line may case confusion

    //Mapfunction Returns 'this' if successful, other wise returns NULL
    
    retVal = pProf->MapFunction(functionId, true, pbHookFunction); 

    by this I assume you don't mean the profiler instance but a pointer to an entity. As S.SKov points out you will loose any context information about your function. Personally I use unique IDs (InterlockedIncrement) for each function I wish to monitor and use that as a lookup in a map/list/array or whatever is appropriate to my needs.

     

  • Monday, June 06, 2011 5:11 AM
     
     

    Thanks for your clarification Skov... 

    My intention to returns the profiler (this) in FunctionIDMapper2 is this

    1. I am not using a global profiler instance. So I can't use the global profiler pointer in the naked callbacks.

    2. I have Custom Enter, Leave and TailCall functions in my profiler, which I will call from the FunctionHook callbacks (the FunctionEnterGlobal).

    Hence I need the profiler instance to make the calls to these functions.

    In SetEnterLeaveFunctionHooks2 there was no problem, because

    the callback methods had signatures which were necessary for me

     

    typedef void FunctionEnter2(FunctionID funcId, UINT_PTR clientData, COR_PRF_FRAME_INFO func,     COR_PRF_FUNCTION_ARGUMENT_INFO *argumentInfo);

     

    - clientData ---> where I will get my profiler &

    - functionID --> where I will get my functionID as well separately.

    but in the SetEnterLeaveFunctionHooks3WithInfo I don't have that option. Hence my doubts.

    This is where i am stuck. 

    I can very well re-map my the said function from FunctionMapper2 but I need the profiler instance to make call to my custom Enter, leave, tailcall functions where I manipulate the methods with my container (map/array/list or whatever).

    Any suggestion or help is greatly appreciated, and thanks for helping thus far. 

     

     

     


    ..JPJ..
  • Monday, June 06, 2011 2:05 PM
     
     

    There is no way around it if you want to use the v3 of the interface - you need some sort of global state which can be used from the global ELT functions. 

    If you do not plan on supporting SxS then I would suggest that you simply store your profiler instance in a global variable which is available from the ELT functions. If you plan on supporting SxS then you could encode information in the mapped functionID which can serve as a lookup into a global structure of profiler instances.