summaryrefslogtreecommitdiff
path: root/src/vm/amd64/profiler.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/vm/amd64/profiler.cpp')
-rw-r--r--src/vm/amd64/profiler.cpp367
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
+