summaryrefslogtreecommitdiff
path: root/src/vm/eetoprofinterfaceimpl.h
diff options
context:
space:
mode:
Diffstat (limited to 'src/vm/eetoprofinterfaceimpl.h')
-rw-r--r--src/vm/eetoprofinterfaceimpl.h681
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__