diff options
Diffstat (limited to 'src/vm/amd64/profiler.cpp')
-rw-r--r-- | src/vm/amd64/profiler.cpp | 367 |
1 files changed, 367 insertions, 0 deletions
diff --git a/src/vm/amd64/profiler.cpp b/src/vm/amd64/profiler.cpp new file mode 100644 index 0000000000..e88cbba9ee --- /dev/null +++ b/src/vm/amd64/profiler.cpp @@ -0,0 +1,367 @@ +// 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<PROFILE_PLATFORM_SPECIFIC_DATA *>(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 + |