// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. // // FILE: profiler.cpp // // // // ====================================================================================== #include "common.h" #ifdef PROFILING_SUPPORTED #include "proftoeeinterfaceimpl.h" MethodDesc *FunctionIdToMethodDesc(FunctionID functionID); // TODO: move these to some common.h file // FLAGS #define PROFILE_ENTER 0x1 #define PROFILE_LEAVE 0x2 #define PROFILE_TAILCALL 0x4 typedef struct _PROFILE_PLATFORM_SPECIFIC_DATA { FunctionID functionId; void *rbp; void *probeRsp; void *ip; void *profiledRsp; UINT64 rax; LPVOID hiddenArg; UINT64 flt0; // floats stored as doubles UINT64 flt1; UINT64 flt2; UINT64 flt3; UINT32 flags; } PROFILE_PLATFORM_SPECIFIC_DATA, *PPROFILE_PLATFORM_SPECIFIC_DATA; /* * ProfileGetIPFromPlatformSpecificHandle * * This routine takes the platformSpecificHandle and retrieves from it the * IP value. * * Parameters: * handle - the platformSpecificHandle passed to ProfileEnter/Leave/Tailcall * * Returns: * The IP value stored in the handle. */ UINT_PTR ProfileGetIPFromPlatformSpecificHandle(void *handle) { LIMITED_METHOD_CONTRACT; PROFILE_PLATFORM_SPECIFIC_DATA* pData = (PROFILE_PLATFORM_SPECIFIC_DATA*)handle; return (UINT_PTR)pData->ip; } /* * ProfileSetFunctionIDInPlatformSpecificHandle * * This routine takes the platformSpecificHandle and functionID, and assign * functionID to functionID field of platformSpecificHandle. * * Parameters: * pPlatformSpecificHandle - the platformSpecificHandle passed to ProfileEnter/Leave/Tailcall * functionID - the FunctionID to be assigned * * Returns: * None */ void ProfileSetFunctionIDInPlatformSpecificHandle(void * pPlatformSpecificHandle, FunctionID functionID) { LIMITED_METHOD_CONTRACT; _ASSERTE(pPlatformSpecificHandle != NULL); _ASSERTE(functionID != NULL); PROFILE_PLATFORM_SPECIFIC_DATA * pData = reinterpret_cast(pPlatformSpecificHandle); pData->functionId = functionID; } /* * ProfileArgIterator::ProfileArgIterator * * Constructor. Initializes for arg iteration. * * Parameters: * pMetaSig - The signature of the method we are going iterate over * platformSpecificHandle - the value passed to ProfileEnter/Leave/Tailcall * * Returns: * None. */ ProfileArgIterator::ProfileArgIterator(MetaSig * pSig, void * platformSpecificHandle) : m_argIterator(pSig) { WRAPPER_NO_CONTRACT; _ASSERTE(pSig != NULL); _ASSERTE(platformSpecificHandle != NULL); m_handle = platformSpecificHandle; PROFILE_PLATFORM_SPECIFIC_DATA* pData = (PROFILE_PLATFORM_SPECIFIC_DATA*)m_handle; // unwind a frame and get the Rsp for the profiled method to make sure it matches // what the JIT gave us #ifdef _DEBUG { // setup the context to represent the frame that called ProfileEnterNaked CONTEXT ctx; memset(&ctx, 0, sizeof(CONTEXT)); ctx.Rsp = (UINT64)pData->probeRsp; ctx.Rbp = (UINT64)pData->rbp; ctx.Rip = (UINT64)pData->ip; // walk up a frame to the caller frame (called the managed method which // called ProfileEnterNaked) Thread::VirtualUnwindCallFrame(&ctx); _ASSERTE(pData->profiledRsp == (void*)ctx.Rsp); } #endif // _DEBUG // Get the hidden arg if there is one MethodDesc * pMD = FunctionIdToMethodDesc(pData->functionId); if ( (pData->hiddenArg == NULL) && (pMD->RequiresInstArg() || pMD->AcquiresInstMethodTableFromThis()) ) { // In the enter probe, the JIT may not have pushed the generics token onto the stack yet. // Luckily, we can inspect the registers reliably at this point. if (pData->flags & PROFILE_ENTER) { _ASSERTE(!((pData->flags & PROFILE_LEAVE) || (pData->flags & PROFILE_TAILCALL))); if (pMD->AcquiresInstMethodTableFromThis()) { pData->hiddenArg = GetThis(); } else { // The param type arg comes after the return buffer argument and the "this" pointer. int index = 0; if (m_argIterator.HasThis()) { index++; } if (m_argIterator.HasRetBuffArg()) { index++; } pData->hiddenArg = *(LPVOID*)((LPBYTE)pData->profiledRsp + (index * sizeof(SIZE_T))); } } else { EECodeInfo codeInfo((PCODE)pData->ip); // We want to pass the caller SP here. pData->hiddenArg = EECodeManager::GetExactGenericsToken((SIZE_T)(pData->profiledRsp), &codeInfo); } } } /* * ProfileArgIterator::~ProfileArgIterator * * Destructor, releases all resources. * */ ProfileArgIterator::~ProfileArgIterator() { LIMITED_METHOD_CONTRACT; m_handle = NULL; } /* * ProfileArgIterator::GetNextArgAddr * * After initialization, this method is called repeatedly until it * returns NULL to get the address of each arg. Note: this address * could be anywhere on the stack. * * Returns: * Address of the argument, or NULL if iteration is complete. */ LPVOID ProfileArgIterator::GetNextArgAddr() { WRAPPER_NO_CONTRACT; _ASSERTE(m_handle != NULL); PROFILE_PLATFORM_SPECIFIC_DATA* pData = (PROFILE_PLATFORM_SPECIFIC_DATA*)m_handle; if ((pData->flags & PROFILE_LEAVE) || (pData->flags & PROFILE_TAILCALL)) { _ASSERTE(!"GetNextArgAddr() - arguments are not available in leave and tailcall probes"); return NULL; } int argOffset = m_argIterator.GetNextOffset(); // argOffset of TransitionBlock::InvalidOffset indicates that we're done if (argOffset == TransitionBlock::InvalidOffset) { return NULL; } // stack args are offset against the profiledRsp if (TransitionBlock::IsStackArgumentOffset(argOffset)) { LPVOID pArg = ((LPBYTE)pData->profiledRsp) + (argOffset - TransitionBlock::GetOffsetOfArgs()); if (m_argIterator.IsArgPassedByRef()) pArg = *(LPVOID *)pArg; return pArg; } // if we're here we have an enregistered argument int regStructOfs = (argOffset - TransitionBlock::GetOffsetOfArgumentRegisters()); _ASSERTE(regStructOfs < ARGUMENTREGISTERS_SIZE); CorElementType t = m_argIterator.GetArgType(); _ASSERTE(IS_ALIGNED(regStructOfs, sizeof(SLOT))); if (t == ELEMENT_TYPE_R4 || t == ELEMENT_TYPE_R8) { return (LPBYTE)&pData->flt0 + regStructOfs; } else { // enregistered args (which are really stack homed) are offset against profiledRsp LPVOID pArg = ((LPBYTE)pData->profiledRsp + regStructOfs); if (m_argIterator.IsArgPassedByRef()) pArg = *(LPVOID *)pArg; return pArg; } return NULL; } /* * ProfileArgIterator::GetHiddenArgValue * * Called after initialization, any number of times, to retrieve any * hidden argument, so that resolution for Generics can be done. * * Parameters: * None. * * Returns: * Value of the hidden parameter, or NULL if none exists. */ LPVOID ProfileArgIterator::GetHiddenArgValue(void) { LIMITED_METHOD_CONTRACT; PROFILE_PLATFORM_SPECIFIC_DATA* pData = (PROFILE_PLATFORM_SPECIFIC_DATA*)m_handle; return pData->hiddenArg; } /* * ProfileArgIterator::GetThis * * Called after initialization, any number of times, to retrieve any * 'this' pointer. * * Parameters: * None. * * Returns: * Address of the 'this', or NULL if none exists. */ LPVOID ProfileArgIterator::GetThis(void) { CONTRACTL { NOTHROW; GC_NOTRIGGER; } CONTRACTL_END; PROFILE_PLATFORM_SPECIFIC_DATA* pData = (PROFILE_PLATFORM_SPECIFIC_DATA*)m_handle; MethodDesc * pMD = FunctionIdToMethodDesc(pData->functionId); // We guarantee to return the correct "this" pointer in the enter probe. // For the leave and tailcall probes, we only return a valid "this" pointer if it is the generics token. if (pData->hiddenArg != NULL) { if (pMD->AcquiresInstMethodTableFromThis()) { return pData->hiddenArg; } } if (pData->flags & PROFILE_ENTER) { if (m_argIterator.HasThis()) { return *(LPVOID*)((LPBYTE)pData->profiledRsp); } } return NULL; } /* * ProfileArgIterator::GetReturnBufferAddr * * Called after initialization, any number of times, to retrieve the * address of the return buffer. NULL indicates no return value. * * Parameters: * None. * * Returns: * Address of the return buffer, or NULL if none exists. */ LPVOID ProfileArgIterator::GetReturnBufferAddr(void) { CONTRACTL { NOTHROW; GC_NOTRIGGER; } CONTRACTL_END; PROFILE_PLATFORM_SPECIFIC_DATA* pData = (PROFILE_PLATFORM_SPECIFIC_DATA*)m_handle; if (m_argIterator.HasRetBuffArg()) { // the JIT64 makes sure that in ret-buf-arg cases where the method is being profiled that // rax is setup with the address of caller passed in buffer. this is _questionably_ required // by our calling convention, but is required by our profiler spec. return (LPVOID)pData->rax; } CorElementType t = m_argIterator.GetSig()->GetReturnType(); if (ELEMENT_TYPE_VOID != t) { if (ELEMENT_TYPE_R4 == t || ELEMENT_TYPE_R8 == t) pData->rax = pData->flt0; return &(pData->rax); } else return NULL; } #undef PROFILE_ENTER #undef PROFILE_LEAVE #undef PROFILE_TAILCALL #endif // PROFILING_SUPPORTED