summaryrefslogtreecommitdiff
path: root/src/vm/proftoeeinterfaceimpl.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/vm/proftoeeinterfaceimpl.cpp')
-rw-r--r--src/vm/proftoeeinterfaceimpl.cpp368
1 files changed, 336 insertions, 32 deletions
diff --git a/src/vm/proftoeeinterfaceimpl.cpp b/src/vm/proftoeeinterfaceimpl.cpp
index 551b38631a..1aee26dde3 100644
--- a/src/vm/proftoeeinterfaceimpl.cpp
+++ b/src/vm/proftoeeinterfaceimpl.cpp
@@ -585,6 +585,10 @@ COM_METHOD ProfToEEInterfaceImpl::QueryInterface(REFIID id, void ** pInterface)
{
*pInterface = static_cast<ICorProfilerInfo7 *>(this);
}
+ else if (id == IID_ICorProfilerInfo8)
+ {
+ *pInterface = static_cast<ICorProfilerInfo8 *>(this);
+ }
else if (id == IID_IUnknown)
{
*pInterface = static_cast<IUnknown *>(static_cast<ICorProfilerInfo *>(this));
@@ -754,7 +758,7 @@ struct GenerationTable
//---------------------------------------------------------------------------------------
//
-// This is a callback used by the GC when we call GCHeap::DescrGenerationsToProfiler
+// This is a callback used by the GC when we call GCHeapUtilities::DiagDescrGenerations
// (from UpdateGenerationBounds() below). The GC gives us generation information through
// this callback, which we use to update the GenerationDesc in the corresponding
// GenerationTable
@@ -874,8 +878,8 @@ void __stdcall UpdateGenerationBounds()
#endif
// fill in the values by calling back into the gc, which will report
// the ranges by calling GenWalkFunc for each one
- GCHeap *hp = GCHeap::GetGCHeap();
- hp->DescrGenerationsToProfiler(GenWalkFunc, newGenerationTable);
+ IGCHeap *hp = GCHeapUtilities::GetGCHeap();
+ hp->DiagDescrGenerations(GenWalkFunc, newGenerationTable);
// remember the old table and plug in the new one
GenerationTable *oldGenerationTable = s_currentGenerationTable;
@@ -1018,7 +1022,7 @@ ClassID SafeGetClassIDFromObject(Object * pObj)
//---------------------------------------------------------------------------------------
//
-// Callback of type walk_fn used by GCHeap::WalkObject. Keeps a count of each
+// Callback of type walk_fn used by GCHeapUtilities::DiagWalkObject. Keeps a count of each
// object reference found.
//
// Arguments:
@@ -1040,7 +1044,7 @@ BOOL CountContainedObjectRef(Object * pBO, void * context)
//---------------------------------------------------------------------------------------
//
-// Callback of type walk_fn used by GCHeap::WalkObject. Stores each object reference
+// Callback of type walk_fn used by GCHeapUtilities::DiagWalkObject. Stores each object reference
// encountered into an array.
//
// Arguments:
@@ -1113,7 +1117,7 @@ BOOL HeapWalkHelper(Object * pBO, void * pvContext)
if (pMT->ContainsPointersOrCollectible())
{
// First round through calculates the number of object refs for this class
- GCHeap::GetGCHeap()->WalkObject(pBO, &CountContainedObjectRef, (void *)&cNumRefs);
+ GCHeapUtilities::GetGCHeap()->DiagWalkObject(pBO, &CountContainedObjectRef, (void *)&cNumRefs);
if (cNumRefs > 0)
{
@@ -1138,7 +1142,7 @@ BOOL HeapWalkHelper(Object * pBO, void * pvContext)
// Second round saves off all of the ref values
OBJECTREF * pCurObjRef = arrObjRef;
- GCHeap::GetGCHeap()->WalkObject(pBO, &SaveContainedObjectRef, (void *)&pCurObjRef);
+ GCHeapUtilities::GetGCHeap()->DiagWalkObject(pBO, &SaveContainedObjectRef, (void *)&pCurObjRef);
}
}
@@ -1959,7 +1963,7 @@ HRESULT GetFunctionInfoInternal(LPCBYTE ip, EECodeInfo * pCodeInfo)
}
-HRESULT GetFunctionFromIPInternal(LPCBYTE ip, EECodeInfo * pCodeInfo)
+HRESULT GetFunctionFromIPInternal(LPCBYTE ip, EECodeInfo * pCodeInfo, BOOL failOnNoMetadata)
{
CONTRACTL
{
@@ -1979,11 +1983,14 @@ HRESULT GetFunctionFromIPInternal(LPCBYTE ip, EECodeInfo * pCodeInfo)
{
return hr;
}
-
- // never return a method that the user of the profiler API cannot use
- if (pCodeInfo->GetMethodDesc()->IsNoMetadata())
+
+ if (failOnNoMetadata)
{
- return E_FAIL;
+ // never return a method that the user of the profiler API cannot use
+ if (pCodeInfo->GetMethodDesc()->IsNoMetadata())
+ {
+ return E_FAIL;
+ }
}
return S_OK;
@@ -2043,7 +2050,7 @@ HRESULT ProfToEEInterfaceImpl::GetFunctionFromIP(LPCBYTE ip, FunctionID * pFunct
EECodeInfo codeInfo;
- hr = GetFunctionFromIPInternal(ip, &codeInfo);
+ hr = GetFunctionFromIPInternal(ip, &codeInfo, /* failOnNoMetadata */ TRUE);
if (FAILED(hr))
{
return hr;
@@ -2096,7 +2103,7 @@ HRESULT ProfToEEInterfaceImpl::GetFunctionFromIP2(LPCBYTE ip, FunctionID * pFunc
EECodeInfo codeInfo;
- hr = GetFunctionFromIPInternal(ip, &codeInfo);
+ hr = GetFunctionFromIPInternal(ip, &codeInfo, /* failOnNoMetadata */ TRUE);
if (FAILED(hr))
{
return hr;
@@ -4122,7 +4129,6 @@ DWORD ProfToEEInterfaceImpl::GetModuleFlags(Module * pModule)
}
#endif
// Not NGEN or ReadyToRun.
-
if (pPEFile->HasOpenedILimage())
{
PEImage * pILImage = pPEFile->GetOpenedILimage();
@@ -6051,7 +6057,7 @@ HRESULT ProfToEEInterfaceImpl::SetEnterLeaveFunctionHooks3WithInfo(FunctionEnter
// The profiler must call SetEnterLeaveFunctionHooks3WithInfo during initialization, since
// the enter/leave events are immutable and must also be set during initialization.
- PROFILER_TO_CLR_ENTRYPOINT_SET_ELT((LF_CORPROF,
+ PROFILER_TO_CLR_ENTRYPOINT_SET_ELT((LF_CORPROF,
LL_INFO10,
"**PROF: SetEnterLeaveFunctionHooks3WithInfo 0x%p, 0x%p, 0x%p.\n",
pFuncEnter3WithInfo,
@@ -6373,6 +6379,294 @@ HRESULT ProfToEEInterfaceImpl::GetFunctionInfo2(FunctionID funcId,
}
/*
+* IsFunctionDynamic
+*
+* This function takes a functionId that maybe of a metadata-less method like an IL Stub
+* or LCG method and returns true in the pHasNoMetadata if it is indeed a metadata-less
+* method.
+*
+* Parameters:
+* functionId - The function that is being requested.
+* isDynamic - An optional parameter for returning if the function has metadata or not.
+*
+* Returns:
+* S_OK if successful.
+*/
+HRESULT ProfToEEInterfaceImpl::IsFunctionDynamic(FunctionID functionId, BOOL *isDynamic)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ MODE_ANY;
+ EE_THREAD_NOT_REQUIRED;
+
+ // Generics::GetExactInstantiationsOfMethodAndItsClassFromCallInformation eventually
+ // reads metadata which causes us to take a reader lock. However, see
+ // code:#DisableLockOnAsyncCalls
+ DISABLED(CAN_TAKE_LOCK);
+
+ // Asynchronous functions can be called at arbitrary times when runtime
+ // is holding locks that cannot be reentered without causing deadlock.
+ // This contract detects any attempts to reenter locks held at the time
+ // this function was called.
+ CANNOT_RETAKE_LOCK;
+
+ SO_NOT_MAINLINE;
+
+ PRECONDITION(CheckPointer(isDynamic, NULL_OK));
+ }
+ CONTRACTL_END;
+
+ // See code:#DisableLockOnAsyncCalls
+ PERMANENT_CONTRACT_VIOLATION(TakesLockViolation, ReasonProfilerAsyncCannotRetakeLock);
+
+ PROFILER_TO_CLR_ENTRYPOINT_ASYNC_EX(kP2EEAllowableAfterAttach,
+ (LF_CORPROF,
+ LL_INFO1000,
+ "**PROF: IsFunctionDynamic 0x%p.\n",
+ functionId));
+
+ //
+ // Verify parameters.
+ //
+
+ if (functionId == NULL)
+ {
+ return E_INVALIDARG;
+ }
+
+ MethodDesc *pMethDesc = FunctionIdToMethodDesc(functionId);
+
+ if (pMethDesc == NULL)
+ {
+ return E_INVALIDARG;
+ }
+
+ // it's not safe to examine a methoddesc that has not been restored so do not do so
+ if (!pMethDesc->IsRestored())
+ return CORPROF_E_DATAINCOMPLETE;
+
+ //
+ // Fill in the pHasNoMetadata, if desired.
+ //
+ if (isDynamic != NULL)
+ {
+ *isDynamic = pMethDesc->IsNoMetadata();
+ }
+
+ return S_OK;
+}
+
+/*
+* GetFunctionFromIP3
+*
+* This function takes an IP and determines if it is a managed function returning its
+* FunctionID. This method is different from GetFunctionFromIP in that will return
+* FunctionIDs even if they have no associated metadata.
+*
+* Parameters:
+* ip - The instruction pointer.
+* pFunctionId - An optional parameter for returning the FunctionID.
+* pReJitId - The ReJIT id.
+*
+* Returns:
+* S_OK if successful.
+*/
+HRESULT ProfToEEInterfaceImpl::GetFunctionFromIP3(LPCBYTE ip, FunctionID * pFunctionId, ReJITID * pReJitId)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+
+ // Grabbing the rejitid requires entering the rejit manager's hash table & lock,
+ // which can switch us to preemptive mode and trigger GCs
+ GC_TRIGGERS;
+ MODE_ANY;
+ EE_THREAD_NOT_REQUIRED;
+
+ // Grabbing the rejitid requires entering the rejit manager's hash table & lock,
+ CAN_TAKE_LOCK;
+
+ SO_NOT_MAINLINE;
+ }
+ CONTRACTL_END;
+
+ // See code:#DisableLockOnAsyncCalls
+ PERMANENT_CONTRACT_VIOLATION(TakesLockViolation, ReasonProfilerAsyncCannotRetakeLock);
+
+ PROFILER_TO_CLR_ENTRYPOINT_SYNC_EX(
+ kP2EEAllowableAfterAttach | kP2EETriggers,
+ (LF_CORPROF,
+ LL_INFO1000,
+ "**PROF: GetFunctionFromIP3 0x%p.\n",
+ ip));
+
+ HRESULT hr = S_OK;
+
+ EECodeInfo codeInfo;
+
+ hr = GetFunctionFromIPInternal(ip, &codeInfo, /* failOnNoMetadata */ FALSE);
+ if (FAILED(hr))
+ {
+ return hr;
+ }
+
+ if (pFunctionId)
+ {
+ *pFunctionId = MethodDescToFunctionID(codeInfo.GetMethodDesc());
+ }
+
+ if (pReJitId != NULL)
+ {
+ MethodDesc * pMD = codeInfo.GetMethodDesc();
+ *pReJitId = pMD->GetReJitManager()->GetReJitId(pMD, codeInfo.GetStartAddress());
+ }
+
+ return S_OK;
+}
+
+/*
+* GetDynamicFunctionInfo
+*
+* This function takes a functionId that maybe of a metadata-less method like an IL Stub
+* or LCG method and gives information about it without failing like GetFunctionInfo.
+*
+* Parameters:
+* functionId - The function that is being requested.
+* pModuleId - An optional parameter for returning the module of the function.
+* ppvSig - An optional parameter for returning the signature of the function.
+* pbSig - An optional parameter for returning the size of the signature of the function.
+* cchName - A parameter for indicating the size of buffer for the wszName parameter.
+* pcchName - An optional parameter for returning the true size of the wszName parameter.
+* wszName - A parameter to the caller allocated buffer of size cchName
+*
+* Returns:
+* S_OK if successful.
+*/
+HRESULT ProfToEEInterfaceImpl::GetDynamicFunctionInfo(FunctionID functionId,
+ ModuleID *pModuleId,
+ PCCOR_SIGNATURE* ppvSig,
+ ULONG* pbSig,
+ ULONG cchName,
+ ULONG *pcchName,
+ __out_ecount_part_opt(cchName, *pcchName) WCHAR wszName[])
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ MODE_ANY;
+ EE_THREAD_NOT_REQUIRED;
+
+ // Generics::GetExactInstantiationsOfMethodAndItsClassFromCallInformation eventually
+ // reads metadata which causes us to take a reader lock. However, see
+ // code:#DisableLockOnAsyncCalls
+ DISABLED(CAN_TAKE_LOCK);
+
+ // Asynchronous functions can be called at arbitrary times when runtime
+ // is holding locks that cannot be reentered without causing deadlock.
+ // This contract detects any attempts to reenter locks held at the time
+ // this function was called.
+ CANNOT_RETAKE_LOCK;
+
+ SO_NOT_MAINLINE;
+
+ PRECONDITION(CheckPointer(pModuleId, NULL_OK));
+ PRECONDITION(CheckPointer(ppvSig, NULL_OK));
+ PRECONDITION(CheckPointer(pbSig, NULL_OK));
+ PRECONDITION(CheckPointer(pcchName, NULL_OK));
+ }
+ CONTRACTL_END;
+
+ // See code:#DisableLockOnAsyncCalls
+ PERMANENT_CONTRACT_VIOLATION(TakesLockViolation, ReasonProfilerAsyncCannotRetakeLock);
+
+ PROFILER_TO_CLR_ENTRYPOINT_ASYNC_EX(kP2EEAllowableAfterAttach,
+ (LF_CORPROF,
+ LL_INFO1000,
+ "**PROF: GetDynamicFunctionInfo 0x%p.\n",
+ functionId));
+
+ //
+ // Verify parameters.
+ //
+
+ if (functionId == NULL)
+ {
+ return E_INVALIDARG;
+ }
+
+ MethodDesc *pMethDesc = FunctionIdToMethodDesc(functionId);
+
+ if (pMethDesc == NULL)
+ {
+ return E_INVALIDARG;
+ }
+
+ // it's not safe to examine a methoddesc that has not been restored so do not do so
+ if (!pMethDesc->IsRestored())
+ return CORPROF_E_DATAINCOMPLETE;
+
+
+ if (!pMethDesc->IsNoMetadata())
+ return E_INVALIDARG;
+
+ //
+ // Fill in the ModuleId, if desired.
+ //
+ if (pModuleId != NULL)
+ {
+ *pModuleId = (ModuleID)pMethDesc->GetModule();
+ }
+
+ //
+ // Fill in the ppvSig and pbSig, if desired
+ //
+ if (ppvSig != NULL && pbSig != NULL)
+ {
+ pMethDesc->GetSig(ppvSig, pbSig);
+ }
+
+ HRESULT hr = S_OK;
+
+ EX_TRY
+ {
+ if (wszName != NULL)
+ *wszName = 0;
+ if (pcchName != NULL)
+ *pcchName = 0;
+
+ StackSString ss;
+ ss.SetUTF8(pMethDesc->GetName());
+ ss.Normalize();
+ LPCWSTR methodName = ss.GetUnicode();
+
+ ULONG trueLen = (ULONG)(wcslen(methodName) + 1);
+
+ // Return name of method as required.
+ if (wszName && cchName > 0)
+ {
+ if (cchName < trueLen)
+ {
+ hr = HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER);
+ }
+ else
+ {
+ wcsncpy_s(wszName, cchName, methodName, trueLen);
+ }
+ }
+
+ // If they request the actual length of the name
+ if (pcchName)
+ *pcchName = trueLen;
+ }
+ EX_CATCH_HRESULT(hr);
+
+ return (hr);
+}
+
+/*
* GetStringLayout
*
* This function describes to a profiler the internal layout of a string.
@@ -7081,12 +7375,13 @@ Loop:
// GC info will assist us in determining whether this is a non-EBP frame and
// info about pushed arguments.
- PTR_VOID gcInfo = codeInfo.GetGCInfo();
+ GCInfoToken gcInfoToken = codeInfo.GetGCInfoToken();
+ PTR_VOID gcInfo = gcInfoToken.Info;
InfoHdr header;
unsigned uiMethodSizeDummy;
PTR_CBYTE table = PTR_CBYTE(gcInfo);
table += decodeUnsigned(table, &uiMethodSizeDummy);
- table = decodeHeader(table, &header);
+ table = decodeHeader(table, gcInfoToken.Version, &header);
// Ok, GCInfo, can we do a simple EBP walk or what?
@@ -7235,18 +7530,13 @@ HRESULT ProfToEEInterfaceImpl::DoStackSnapshot(ThreadID thread,
ULONG32 contextSize)
{
-#ifdef _TARGET_ARM_
- // DoStackSnapshot is not supported on arm. Profilers can use OS apis to get the call stack.
- return E_NOTIMPL;
-#endif
-
-#if !defined(FEATURE_HIJACK) || !defined(PLATFORM_SUPPORTS_SAFE_THREADSUSPEND)
+#if !defined(FEATURE_HIJACK)
// DoStackSnapshot needs Thread::Suspend/ResumeThread functionality.
// On platforms w/o support for these APIs return E_NOTIMPL.
return E_NOTIMPL;
-#else // !defined(FEATURE_HIJACK) || !defined(PLATFORM_SUPPORTS_SAFE_THREADSUSPEND)
+#else // !defined(FEATURE_HIJACK)
CONTRACTL
{
@@ -7414,6 +7704,10 @@ HRESULT ProfToEEInterfaceImpl::DoStackSnapshot(ThreadID thread,
// First, check "1) Target thread to walk == current thread OR Target thread is suspended"
if (pThreadToSnapshot != pCurrentThread)
{
+#ifndef PLATFORM_SUPPORTS_SAFE_THREADSUSPEND
+ hr = E_NOTIMPL;
+ goto Cleanup;
+#else
// Walking separate thread, so it must be suspended. First, ensure that
// target thread exists.
//
@@ -7449,6 +7743,7 @@ HRESULT ProfToEEInterfaceImpl::DoStackSnapshot(ThreadID thread,
hr = CORPROF_E_STACKSNAPSHOT_UNSAFE;
goto Cleanup;
}
+#endif // !PLATFORM_SUPPORTS_SAFE_THREADSUSPEND
}
hostCallPreference =
@@ -7481,7 +7776,10 @@ HRESULT ProfToEEInterfaceImpl::DoStackSnapshot(ThreadID thread,
// (Note that this whole block is skipped if pThreadToSnapshot is in preemptive mode (the IF
// above), as the context is unused in such a case--the EE Frame chain is used
// to seed the walk instead.)
-
+#ifndef PLATFORM_SUPPORTS_SAFE_THREADSUSPEND
+ hr = E_NOTIMPL;
+ goto Cleanup;
+#else
if (!pThreadToSnapshot->GetSafelyRedirectableThreadContext(Thread::kDefaultChecks, &ctxCurrent, &rd))
{
LOG((LF_CORPROF, LL_INFO100, "**PROF: GetSafelyRedirectableThreadContext failure leads to CORPROF_E_STACKSNAPSHOT_UNSAFE.\n"));
@@ -7542,6 +7840,7 @@ HRESULT ProfToEEInterfaceImpl::DoStackSnapshot(ThreadID thread,
{
pctxSeed = &ctxCurrent;
}
+#endif // !PLATFORM_SUPPORTS_SAFE_THREADSUSPEND
}
// Second, check "2) Target thread to walk is currently executing JITted / NGENd code"
@@ -7588,6 +7887,10 @@ HRESULT ProfToEEInterfaceImpl::DoStackSnapshot(ThreadID thread,
//
if (pThreadToSnapshot != pCurrentThread)
{
+#ifndef PLATFORM_SUPPORTS_SAFE_THREADSUSPEND
+ hr = E_NOTIMPL;
+ goto Cleanup;
+#else
if (pctxSeed == NULL)
{
if (pThreadToSnapshot->GetSafelyRedirectableThreadContext(Thread::kDefaultChecks, &ctxCurrent, &rd))
@@ -7604,9 +7907,9 @@ HRESULT ProfToEEInterfaceImpl::DoStackSnapshot(ThreadID thread,
}
}
}
+#endif // !PLATFORM_SUPPORTS_SAFE_THREADSUSPEND
}
-#endif
-
+#endif //_DEBUG
// Third, verify the target thread is seeded or not in the midst of an unwind.
if (pctxSeed == NULL)
{
@@ -7671,12 +7974,13 @@ HRESULT ProfToEEInterfaceImpl::DoStackSnapshot(ThreadID thread,
INDEBUG(if (pCurrentThread) pCurrentThread->m_ulForbidTypeLoad = ulForbidTypeLoad;)
-
Cleanup:
+#if defined(PLATFORM_SUPPORTS_SAFE_THREADSUSPEND)
if (fResumeThread)
{
pThreadToSnapshot->ResumeThread();
}
+#endif // PLATFORM_SUPPORTS_SAFE_THREADSUSPEND
if (fResetSnapshotThreadExternalCount)
{
pThreadToSnapshot->DecExternalCountDANGEROUSProfilerOnly();
@@ -7684,7 +7988,7 @@ Cleanup:
return hr;
-#endif // !defined(FEATURE_HIJACK) || !defined(PLATFORM_SUPPORTS_SAFE_THREADSUSPEND)
+#endif // !defined(FEATURE_HIJACK)
}
@@ -8439,7 +8743,7 @@ HRESULT ProfToEEInterfaceImpl::RequestProfilerDetach(DWORD dwExpectedCompletionM
typedef struct _COR_PRF_ELT_INFO_INTERNAL
{
// Point to a platform dependent structure ASM helper push on the stack
- void * platformSpecificHandle;
+ void * platformSpecificHandle;
// startAddress of COR_PRF_FUNCTION_ARGUMENT_RANGE structure needs to point
// TO the argument value, not BE the argument value. So, when the argument
@@ -9461,7 +9765,7 @@ FCIMPL2(void, ProfilingFCallHelper::FC_RemotingClientSendingMessage, GUID *pId,
// it is a value class declared on the stack and so GC doesn't
// know about it.
- _ASSERTE (!GCHeap::GetGCHeap()->IsHeapPointer(pId)); // should be on the stack, not in the heap
+ _ASSERTE (!GCHeapUtilities::GetGCHeap()->IsHeapPointer(pId)); // should be on the stack, not in the heap
HELPER_METHOD_FRAME_BEGIN_NOPOLL();
{