diff options
Diffstat (limited to 'src/vm/eetoprofinterfaceimpl.h')
-rw-r--r-- | src/vm/eetoprofinterfaceimpl.h | 681 |
1 files changed, 681 insertions, 0 deletions
diff --git a/src/vm/eetoprofinterfaceimpl.h b/src/vm/eetoprofinterfaceimpl.h new file mode 100644 index 0000000000..0390f942bb --- /dev/null +++ b/src/vm/eetoprofinterfaceimpl.h @@ -0,0 +1,681 @@ +// 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. +// +// EEToProfInterfaceImpl.h +// + +// +// Declaration of class that wraps calling into the profiler's implementation +// of ICorProfilerCallback* +// + +// ====================================================================================== + + +#ifndef __EETOPROFINTERFACEIMPL_H__ +#define __EETOPROFINTERFACEIMPL_H__ + +#include <stddef.h> +#include "profilepriv.h" +#include "eeprofinterfaces.h" +#include "shash.h" +#include "eventtracebase.h" + +class SimpleRWLock; + +class ProfToEEInterfaceImpl; + +interface IAssemblyBindingClosure; +struct AssemblyReferenceClosureWalkContextForProfAPI; + +const GUID k_guidZero = {0}; + +class EEToProfInterfaceImpl +{ +public: + + // + // Internal initialization / cleanup + // + + EEToProfInterfaceImpl(); + ~EEToProfInterfaceImpl(); + + HRESULT Init( + ProfToEEInterfaceImpl * pProfToEE, + const CLSID * pClsid, + __inout_z LPCWSTR wszClsid, + __in_z LPCWSTR wszProfileDLL, + BOOL fLoadedViaAttach, + DWORD dwConcurrentGCWaitTimeoutInMs); + + BOOL IsCallback3Supported(); + BOOL IsCallback4Supported(); + BOOL IsCallback5Supported(); + BOOL IsCallback6Supported(); + BOOL IsCallback7Supported(); + + HRESULT SetEventMask(DWORD dwEventMask, DWORD dwEventMaskHigh); + + // Used in ProfToEEInterfaceImpl.cpp to set this to the profiler's hook's + // function pointer (see SetFunctionIDMapper). + void SetFunctionIDMapper(FunctionIDMapper * pFunc); + void SetFunctionIDMapper2(FunctionIDMapper2 * pFunc, void * clientData); + + FunctionIDMapper * GetFunctionIDMapper(); + FunctionIDMapper2 * GetFunctionIDMapper2(); + BOOL IsLoadedViaAttach(); + HRESULT EnsureProfilerDetachable(); + void SetUnrevertiblyModifiedILFlag(); + + FunctionEnter * GetEnterHook(); + FunctionLeave * GetLeaveHook(); + FunctionTailcall * GetTailcallHook(); + + FunctionEnter2 * GetEnter2Hook(); + FunctionLeave2 * GetLeave2Hook(); + FunctionTailcall2 * GetTailcall2Hook(); + + FunctionEnter3 * GetEnter3Hook(); + FunctionLeave3 * GetLeave3Hook(); + FunctionTailcall3 * GetTailcall3Hook(); + FunctionEnter3WithInfo * GetEnter3WithInfoHook(); + FunctionLeave3WithInfo * GetLeave3WithInfoHook(); + FunctionTailcall3WithInfo * GetTailcall3WithInfoHook(); + + BOOL IsClientIDToFunctionIDMappingEnabled(); + + UINT_PTR LookupClientIDFromCache(FunctionID functionID); + + HRESULT SetEnterLeaveFunctionHooks( + FunctionEnter * pFuncEnter, + FunctionLeave * pFuncLeave, + FunctionTailcall * pFuncTailcall); + + HRESULT SetEnterLeaveFunctionHooks2( + FunctionEnter2 * pFuncEnter, + FunctionLeave2 * pFuncLeave, + FunctionTailcall2 * pFuncTailcall); + + HRESULT SetEnterLeaveFunctionHooks3( + FunctionEnter3 * pFuncEnter3, + FunctionLeave3 * pFuncLeave3, + FunctionTailcall3 * pFuncTailcall3); + + HRESULT SetEnterLeaveFunctionHooks3WithInfo( + FunctionEnter3WithInfo * pFuncEnter3WithInfo, + FunctionLeave3WithInfo * pFuncLeave3WithInfo, + FunctionTailcall3WithInfo * pFuncTailcall3WithInfo); + + BOOL RequiresGenericsContextForEnterLeave(); + + UINT_PTR EEFunctionIDMapper(FunctionID funcId, BOOL * pbHookFunction); + + // This fills in the non call-specific portions of the cookie GUID. + // This should only be called once at startup if necessary. + HRESULT InitGUID(); + + // This will assign a mostly-unique GUID. If enough calls to GetGUID + // are made from the same thread, then the GUIDs will cycle. + // (Current, it will cycle every 256 calls) + void GetGUID(GUID * pGUID); + + // + // Initialize callback + // + + HRESULT Initialize(); + + HRESULT InitializeForAttach(void * pvClientData, UINT cbClientData); + + HRESULT ProfilerAttachComplete(); + + // + // Thread Events + // + + HRESULT ThreadCreated( + ThreadID threadID); + + HRESULT ThreadDestroyed( + ThreadID threadID); + + HRESULT ThreadAssignedToOSThread(ThreadID managedThreadId, + DWORD osThreadId); + + HRESULT ThreadNameChanged(ThreadID managedThreadId, + ULONG cchName, + __in_ecount_opt(cchName) WCHAR name[]); + + // + // Startup/Shutdown Events + // + + HRESULT Shutdown(); + + // + // JIT/Function Events + // + + HRESULT FunctionUnloadStarted( + FunctionID functionId); + + HRESULT JITCompilationFinished( + FunctionID functionId, + HRESULT hrStatus, + BOOL fIsSafeToBlock); + + HRESULT JITCompilationStarted( + FunctionID functionId, + BOOL fIsSafeToBlock); + + HRESULT JITCachedFunctionSearchStarted( + /* [in] */ FunctionID functionId, + /* [out] */ BOOL * pbUseCachedFunction); + + HRESULT JITCachedFunctionSearchFinished( + /* [in] */ FunctionID functionId, + /* [in] */ COR_PRF_JIT_CACHE result); + + HRESULT JITFunctionPitched(FunctionID functionId); + + HRESULT JITInlining( + /* [in] */ FunctionID callerId, + /* [in] */ FunctionID calleeId, + /* [out] */ BOOL * pfShouldInline); + + HRESULT ReJITCompilationStarted( + /* [in] */ FunctionID functionId, + /* [in] */ ReJITID reJitId, + /* [in] */ BOOL fIsSafeToBlock); + + HRESULT GetReJITParameters( + /* [in] */ ModuleID moduleId, + /* [in] */ mdMethodDef methodId, + /* [in] */ ICorProfilerFunctionControl * + pFunctionControl); + + HRESULT ReJITCompilationFinished( + /* [in] */ FunctionID functionId, + /* [in] */ ReJITID reJitId, + /* [in] */ HRESULT hrStatus, + /* [in] */ BOOL fIsSafeToBlock); + + HRESULT ReJITError( + /* [in] */ ModuleID moduleId, + /* [in] */ mdMethodDef methodId, + /* [in] */ FunctionID functionId, + /* [in] */ HRESULT hrStatus); + + // + // Module Events + // + + HRESULT ModuleLoadStarted( + ModuleID moduleId); + + HRESULT ModuleLoadFinished( + ModuleID moduleId, + HRESULT hrStatus); + + HRESULT ModuleUnloadStarted( + ModuleID moduleId); + + HRESULT ModuleUnloadFinished( + ModuleID moduleId, + HRESULT hrStatus); + + HRESULT ModuleAttachedToAssembly( + ModuleID moduleId, + AssemblyID AssemblyId); + + HRESULT ModuleInMemorySymbolsUpdated( + ModuleID moduleId); + + // + // Class Events + // + + HRESULT ClassLoadStarted( + ClassID classId); + + HRESULT ClassLoadFinished( + ClassID classId, + HRESULT hrStatus); + + HRESULT ClassUnloadStarted( + ClassID classId); + + HRESULT ClassUnloadFinished( + ClassID classId, + HRESULT hrStatus); + + // + // AppDomain Events + // + + HRESULT AppDomainCreationStarted( + AppDomainID appDomainId); + + HRESULT AppDomainCreationFinished( + AppDomainID appDomainId, + HRESULT hrStatus); + + HRESULT AppDomainShutdownStarted( + AppDomainID appDomainId); + + HRESULT AppDomainShutdownFinished( + AppDomainID appDomainId, + HRESULT hrStatus); + + // + // Assembly Events + // + + HRESULT AssemblyLoadStarted( + AssemblyID assemblyId); + + HRESULT AssemblyLoadFinished( + AssemblyID assemblyId, + HRESULT hrStatus); + + HRESULT AssemblyUnloadStarted( + AssemblyID assemblyId); + + HRESULT AssemblyUnloadFinished( + AssemblyID assemblyId, + HRESULT hrStatus); + + // + // Transition Events + // + + HRESULT UnmanagedToManagedTransition( + FunctionID functionId, + COR_PRF_TRANSITION_REASON reason); + + HRESULT ManagedToUnmanagedTransition( + FunctionID functionId, + COR_PRF_TRANSITION_REASON reason); + + // + // Exception Events + // + + HRESULT ExceptionThrown( + ObjectID thrownObjectId); + + HRESULT ExceptionSearchFunctionEnter( + FunctionID functionId); + + HRESULT ExceptionSearchFunctionLeave(); + + HRESULT ExceptionSearchFilterEnter( + FunctionID funcId); + + HRESULT ExceptionSearchFilterLeave(); + + HRESULT ExceptionSearchCatcherFound( + FunctionID functionId); + + HRESULT ExceptionOSHandlerEnter( + FunctionID funcId); + + HRESULT ExceptionOSHandlerLeave( + FunctionID funcId); + + HRESULT ExceptionUnwindFunctionEnter( + FunctionID functionId); + + HRESULT ExceptionUnwindFunctionLeave(); + + HRESULT ExceptionUnwindFinallyEnter( + FunctionID functionId); + + HRESULT ExceptionUnwindFinallyLeave(); + + HRESULT ExceptionCatcherEnter( + FunctionID functionId, + ObjectID objectId); + + HRESULT ExceptionCatcherLeave(); + + // + // CCW Events + // + + HRESULT COMClassicVTableCreated( + /* [in] */ ClassID wrappedClassId, + /* [in] */ REFGUID implementedIID, + /* [in] */ void * pVTable, + /* [in] */ ULONG cSlots); + + HRESULT COMClassicVTableDestroyed( + /* [in] */ ClassID wrappedClassId, + /* [in] */ REFGUID implementedIID, + /* [in] */ void * pVTable); + + // + // Remoting Events + // + + HRESULT RemotingClientInvocationStarted(); + + HRESULT RemotingClientSendingMessage(GUID * pCookie, + BOOL fIsAsync); + + HRESULT RemotingClientReceivingReply(GUID * pCookie, + BOOL fIsAsync); + + HRESULT RemotingClientInvocationFinished(); + + HRESULT RemotingServerReceivingMessage(GUID * pCookie, + BOOL fIsAsync); + + HRESULT RemotingServerInvocationStarted(); + + HRESULT RemotingServerInvocationReturned(); + + HRESULT RemotingServerSendingReply(GUID * pCookie, + BOOL fIsAsync); + + + // + // GC Events + // + + HRESULT RuntimeSuspendStarted(COR_PRF_SUSPEND_REASON suspendReason); + + HRESULT RuntimeSuspendFinished(); + + HRESULT RuntimeSuspendAborted(); + + HRESULT RuntimeResumeStarted(); + + HRESULT RuntimeResumeFinished(); + + HRESULT RuntimeThreadSuspended(ThreadID suspendedThreadId); + + HRESULT RuntimeThreadResumed(ThreadID resumedThreadId); + + HRESULT ObjectAllocated( + /* [in] */ ObjectID objectId, + /* [in] */ ClassID classId); + + HRESULT FinalizeableObjectQueued(BOOL isCritical, ObjectID objectID); + + // + // GC Moved References and RootReferences2 Notification Stuff + // + + HRESULT MovedReference(BYTE * pbMemBlockStart, + BYTE * pbMemBlockEnd, + ptrdiff_t cbRelocDistance, + void * pHeapId, + BOOL fCompacting); + + HRESULT EndMovedReferences(void * pHeapId); + + HRESULT RootReference2(BYTE * objectId, + EtwGCRootKind dwEtwRootKind, + EtwGCRootFlags dwEtwRootFlags, + void * rootID, + void * pHeapId); + + HRESULT EndRootReferences2(void * pHeapId); + + HRESULT ConditionalWeakTableElementReference(BYTE * primaryObjectId, + BYTE * secondaryObjectId, + void * rootID, + void * pHeapId); + + HRESULT EndConditionalWeakTableElementReferences(void * pHeapId); + + // + // GC Root notification stuff + // + + HRESULT AllocByClass(ObjectID objId, ClassID classId, void* pHeapId); + + HRESULT EndAllocByClass(void * pHeapId); + + // + // Heap walk notification stuff + // + HRESULT ObjectReference(ObjectID objId, + ClassID classId, + ULONG cNumRefs, + ObjectID * arrObjRef); + + // + // GC Handle creation / destruction notifications + // + HRESULT HandleCreated(UINT_PTR handleId, ObjectID initialObjectId); + + HRESULT HandleDestroyed(UINT_PTR handleId); + + HRESULT GarbageCollectionStarted(int cGenerations, BOOL generationCollected[], COR_PRF_GC_REASON reason); + + HRESULT GarbageCollectionFinished(); + + // + // Detach + // + HRESULT ProfilerDetachSucceeded(); + + BOOL HasTimedOutWaitingForConcurrentGC(); + + HRESULT GetAssemblyReferences(LPCWSTR wszAssemblyPath, IAssemblyBindingClosure * pClosure, AssemblyReferenceClosureWalkContextForProfAPI * pContext); + +private: + + // + // Generation 0 Allocation by Class notification stuff + // + + // This is for a hashing of ClassID values + struct CLASSHASHENTRY : HASHENTRY + { + ClassID m_clsId; // The class ID (also the key) + size_t m_count; // How many of this class have been counted + }; + + // This is a simple implementation of CHashTable to provide a very simple + // implementation of the Cmp pure virtual function + class CHashTableImpl : public CHashTable + { + public: + CHashTableImpl(ULONG iBuckets); + virtual ~CHashTableImpl(); + + protected: + virtual BOOL Cmp(SIZE_T k1, const HASHENTRY * pc2); + }; + + // This contains the data for storing allocation information + // in terms of numbers of objects sorted by class. + struct AllocByClassData + { + CHashTableImpl * pHashTable; // The hash table + CLASSHASHENTRY * arrHash; // Array that the hashtable uses for linking + ULONG cHash; // The total number of elements in arrHash + ULONG iHash; // Next empty entry in the hash array + ClassID * arrClsId; // Array of ClassIDs for the call to ObjectsAllocatedByClass + ULONG * arrcObjects; // Array of counts for the call to ObjectsAllocatedByClass + size_t cLength; // Length of the above two parallel arrays + }; + + static const UINT kcReferencesMax = 512; + + struct GCReferencesData + { + size_t curIdx; + size_t compactingCount; + BYTE * arrpbMemBlockStartOld[kcReferencesMax]; + BYTE * arrpbMemBlockStartNew[kcReferencesMax]; + union + { + size_t arrMemBlockSize[kcReferencesMax]; + ULONG arrULONG[kcReferencesMax]; + BYTE * arrpbRootId[kcReferencesMax]; + }; + GCReferencesData * pNext; + }; + + // Since this stuff can only be performed by one thread (right now), we don't need + // to make this thread safe and can just have one block we reuse every time around + static AllocByClassData * m_pSavedAllocDataBlock; + + // Pointer to the profiler's implementation of the callback interface(s). + // Profilers MUST support ICorProfilerCallback2. + // Profilers MAY optionally support ICorProfilerCallback3,4,5,6,7 + ICorProfilerCallback2 * m_pCallback2; + ICorProfilerCallback3 * m_pCallback3; + ICorProfilerCallback4 * m_pCallback4; + ICorProfilerCallback5 * m_pCallback5; + ICorProfilerCallback6 * m_pCallback6; + ICorProfilerCallback7 * m_pCallback7; + HMODULE m_hmodProfilerDLL; + + BOOL m_fLoadedViaAttach; + ProfToEEInterfaceImpl * m_pProfToEE; + + // Used in EEToProfInterfaceImpl.cpp to call into the profiler (see EEFunctionIDMapper) + FunctionIDMapper * m_pProfilersFuncIDMapper; + FunctionIDMapper2 * m_pProfilersFuncIDMapper2; + void * m_pProfilersFuncIDMapper2ClientData; + + // This is used as a cookie template for remoting calls + GUID m_GUID; + + // This is an incrementing counter for constructing unique GUIDS from + // m_GUID + LONG m_lGUIDCount; + + // This will contain a list of free ref data structs, so they + // don't have to be re-allocated on every GC + GCReferencesData * m_pGCRefDataFreeList; + + // This is for managing access to the free list above. + CRITSEC_COOKIE m_csGCRefDataFreeList; + + FunctionEnter * m_pEnter; + FunctionLeave * m_pLeave; + FunctionTailcall * m_pTailcall; + + FunctionEnter2 * m_pEnter2; + FunctionLeave2 * m_pLeave2; + FunctionTailcall2 * m_pTailcall2; + + BOOL m_fIsClientIDToFunctionIDMappingEnabled; + + FunctionEnter3 * m_pEnter3; + FunctionLeave3 * m_pLeave3; + FunctionTailcall3 * m_pTailcall3; + + FunctionEnter3WithInfo * m_pEnter3WithInfo; + FunctionLeave3WithInfo * m_pLeave3WithInfo; + FunctionTailcall3WithInfo * m_pTailcall3WithInfo; + + + // Remembers whether the profiler used SetILFunctionBody() which modifies IL in a + // way that cannot be reverted. This prevents a detach from succeeding. + BOOL m_fUnrevertiblyModifiedIL; + + GCReferencesData * AllocateMovedReferencesData(); + + void FreeMovedReferencesData(GCReferencesData * pData); + + HRESULT MovedReferences(GCReferencesData * pData); + + HRESULT RootReferences2(GCReferencesData * pData); + + HRESULT ConditionalWeakTableElementReferences(GCReferencesData * pData); + + HRESULT NotifyAllocByClass(AllocByClassData * pData); + + HRESULT CreateProfiler( + const CLSID * pClsid, + __in_z LPCWSTR wszClsid, + __in_z LPCWSTR wszProfileDLL); + + HRESULT DetermineAndSetEnterLeaveFunctionHooksForJit(); + + HRESULT STDMETHODCALLTYPE SetEnterLeaveFunctionHooksForJit( + FunctionEnter3 * pFuncEnter, + FunctionLeave3 * pFuncLeave, + FunctionTailcall3 * pFuncTailcall); + + struct FunctionIDAndClientID + { + FunctionID functionID; + UINT_PTR clientID; + }; + + class FunctionIDHashTableTraits : public NoRemoveSHashTraits<DefaultSHashTraits<FunctionIDAndClientID> > + { + public: + + static const COUNT_T s_minimum_allocation = 31; + typedef DefaultSHashTraits<FunctionIDAndClientID *>::count_t count_t; + typedef UINT_PTR key_t; + + static key_t GetKey(FunctionIDAndClientID e) + { + LIMITED_METHOD_CONTRACT; + return e.functionID; + } + + static BOOL Equals(key_t k1, key_t k2) + { + LIMITED_METHOD_CONTRACT; + return k1 == k2; + } + + static count_t Hash(key_t k) + { + LIMITED_METHOD_CONTRACT; + return (count_t)k; + } + + static const FunctionIDAndClientID Null() + { + LIMITED_METHOD_CONTRACT; + FunctionIDAndClientID functionIDAndClientID; + functionIDAndClientID.functionID = NULL; + functionIDAndClientID.clientID = NULL; + return functionIDAndClientID; + } + + static bool IsNull(const FunctionIDAndClientID &functionIDAndClientID) + { + LIMITED_METHOD_CONTRACT; + _ASSERTE((functionIDAndClientID.functionID != NULL) || (functionIDAndClientID.clientID == NULL)); + return functionIDAndClientID.functionID == NULL; + } + }; + + typedef SHash<FunctionIDHashTableTraits> FunctionIDHashTable; + + // ELT3 no long keeps track of FunctionID of current managed method. Therefore, a hash table of bookkeeping + // the mapping from FunctionID to clientID is needed to build up ELT2 on top of ELT3. When ELT2 (slow-path + // or fast-path) is registered by the profiler and the profiler's IDFunctionMapper requests to hook up the + // function being loading, the clientID returned by FunctionIDMapper will be saved as the value to be looked + // up by the corresponding FunctionID in the hash table. FunctionIDs can be recycled after an app domain + // that contains the function bodies is unloaded so this hash table needs to replace the existing FunctionID + // with new FunctionID if a duplication is found in the hash table. + FunctionIDHashTable * m_pFunctionIDHashTable; + + // Since the hash table can be read and writen concurrently, a reader-writer lock is used to synchronize + // all accesses to the hash table. + SimpleRWLock * m_pFunctionIDHashTableRWLock; + + // Timeout for wait operation on concurrent GC. Only used for attach scenario + DWORD m_dwConcurrentGCWaitTimeoutInMs; + + // Remember the fact we've timed out when waiting for concurrent GC. Will report the error later + BOOL m_bHasTimedOutWaitingForConcurrentGC; +}; + +#endif // __EETOPROFINTERFACEIMPL_H__ |