diff options
Diffstat (limited to 'src/vm/proftoeeinterfaceimpl.cpp')
-rw-r--r-- | src/vm/proftoeeinterfaceimpl.cpp | 368 |
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(); { |