Permalink
Fetching contributors…
Cannot retrieve contributors at this time
359 lines (301 sloc) 10.2 KB
// 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
{
UINT32 r0; // Keep r0 & r1 contiguous to make returning 64-bit results easier
UINT32 r1;
void *R11;
void *Pc;
union // Float arg registers as 32-bit (s0-s15) and 64-bit (d0-d7)
{
UINT32 s[16];
UINT64 d[8];
};
FunctionID functionId;
void *probeSp; // stack pointer of managed function
void *profiledSp; // location of arguments on stack
LPVOID hiddenArg;
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->Pc;
}
/*
* 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. Does almost nothing. Init must be called after construction.
*
*/
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 SP for the profiled method to make sure it matches
// what the JIT gave us
#ifdef _DEBUG
{
/*
Foo() {
Bar();
}
Stack for the above call will look as follows (stack growing downwards):
|
| Stack Args for Foo |
| pre spill r0-r3 |
| LR |
| R11 |
| Locals of Foo |
| Stack Args for Bar |
| pre spill r0-r3 | __________this Sp value is saved in profiledSP
| LR |
| R11 |
| Satck saved in prolog of Bar | _______ call to profiler hook is made here_____this Sp value is saved in probeSP
| |
*/
// setup the context to represent the frame that called ProfileEnterNaked
CONTEXT ctx;
memset(&ctx, 0, sizeof(CONTEXT));
ctx.Sp = (UINT)pData->probeSp;
ctx.R11 = (UINT)pData->R11;
ctx.Pc = (UINT)pData->Pc;
// For some functions which do localloc, sp is saved in r9. In order to perform unwinding for functions r9 must be set in the context.
// r9 is stored at offset (sizeof(PROFILE_PLATFORM_SPECIFIC_DATA) (this also includes the padding done for 8-byte stack alignement) + size required for (r0,r3)) bytes from pData
ctx.R9 = *((UINT*)pData + (sizeof(PROFILE_PLATFORM_SPECIFIC_DATA) + 8)/4);
// walk up a frame to the caller frame (called the managed method which
// called ProfileEnterNaked)
Thread::VirtualUnwindCallFrame(&ctx);
// add the prespill register(r0-r3) size to get the stack pointer of previous function
_ASSERTE(pData->profiledSp == (void*)(ctx.Sp - 4*4));
}
#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->profiledSp + (index * sizeof(SIZE_T)));
}
}
else
{
EECodeInfo codeInfo((PCODE)pData->Pc);
// We want to pass the caller SP here.
pData->hiddenArg = EECodeManager::GetExactGenericsToken((SIZE_T)(pData->profiledSp), &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;
}
if (TransitionBlock::IsFloatArgumentRegisterOffset(argOffset))
{
// Arguments which land up in floating point registers are contained entirely within those
// registers (they're never split onto the stack).
return ((BYTE *)&pData->d) + (argOffset - TransitionBlock::GetOffsetOfFloatArgumentRegisters());
}
// Argument lives in one or more general registers (and possibly overflows onto the stack).
return (LPBYTE)pData->profiledSp + (argOffset - TransitionBlock::GetOffsetOfArgumentRegisters());
}
/*
* 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->profiledSp);
}
}
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;
MethodDesc * pMD = FunctionIdToMethodDesc(pData->functionId);
if (m_argIterator.HasRetBuffArg())
{
return (LPVOID)pData->r0;
}
if (m_argIterator.GetFPReturnSize() != 0)
return &pData->d[0];
if (m_argIterator.GetSig()->GetReturnType() != ELEMENT_TYPE_VOID)
return &pData->r0;
else
return NULL;
}
#endif // PROFILING_SUPPORTED