diff options
Diffstat (limited to 'src/vm/ibclogger.cpp')
-rw-r--r-- | src/vm/ibclogger.cpp | 1197 |
1 files changed, 1197 insertions, 0 deletions
diff --git a/src/vm/ibclogger.cpp b/src/vm/ibclogger.cpp new file mode 100644 index 0000000000..c71921a23f --- /dev/null +++ b/src/vm/ibclogger.cpp @@ -0,0 +1,1197 @@ +// 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. +// IBClogger.CPP +// + +// +// Infrastructure for recording touches of EE data structures +// +// + + +#include "common.h" +#ifdef IBCLOGGER_ENABLED +#include "method.hpp" +#include "corbbtprof.h" +#include "metadatatracker.h" +#include "field.h" +#include "typekey.h" +#include "ibclogger.h" + +//#ifdef _DEBUG +//#define DEBUG_IBCLOGGER +//#endif + +#ifdef DEBUG_IBCLOGGER + +#define DEBUG_PRINTF1(a) printf(a) +#define DEBUG_PRINTF2(a,b) printf(a,b) +#define DEBUG_PRINTF3(a,b,c) printf(a,b,c) +#define DEBUG_PRINTF4(a,b,c,d) printf(a,b,c,d) +#define DEBUG_PRINTF5(a,b,c,d,e) printf(a,b,c,d,e) +#else +#define DEBUG_PRINTF1(a) +#define DEBUG_PRINTF2(a,b) +#define DEBUG_PRINTF3(a,b,c) +#define DEBUG_PRINTF4(a,b,c,d) +#define DEBUG_PRINTF5(a,b,c,d,e) +#endif + +DWORD dwIBCLogCount = 0; + +#ifdef _DEBUG +/*static*/ unsigned IbcCallback::s_highestId = 0; +#endif + +IBCLoggingDisabler::IBCLoggingDisabler() +{ + m_pInfo = NULL; + m_fDisabled = false; + + if (g_IBCLogger.InstrEnabled()) + { + m_pInfo = GetThread()->GetIBCInfo(); + if (m_pInfo != NULL) + { + m_fDisabled = m_pInfo->DisableLogging(); + } + } +} + +IBCLoggingDisabler::IBCLoggingDisabler(bool ignore) +{ + m_pInfo = NULL; + m_fDisabled = false; + + if (ignore == false) + { + if (g_IBCLogger.InstrEnabled()) + { + m_pInfo = GetThread()->GetIBCInfo(); + if (m_pInfo != NULL) + { + m_fDisabled = m_pInfo->DisableLogging(); + } + } + } +} + +IBCLoggingDisabler::IBCLoggingDisabler(ThreadLocalIBCInfo* pInfo) +{ + LIMITED_METHOD_CONTRACT; + m_pInfo = pInfo; + + if (m_pInfo != NULL) + { + m_fDisabled = m_pInfo->DisableLogging(); + } + else + { + m_fDisabled = false; + } +} + +IBCLoggingDisabler::~IBCLoggingDisabler() +{ + LIMITED_METHOD_CONTRACT; + if (m_fDisabled) + m_pInfo->EnableLogging(); +} + +IBCLoggerAwareAllocMemTracker::~IBCLoggerAwareAllocMemTracker() +{ + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + MODE_ANY; + } + CONTRACTL_END; + + if (!m_fReleased) + { + GetThread()->FlushIBCInfo(); + } +} + +IBCLogger::IBCLogger() + : dwInstrEnabled(0) + , m_sync(NULL) +{ LIMITED_METHOD_CONTRACT;} + +IBCLogger::~IBCLogger() +{ + WRAPPER_NO_CONTRACT; + + if (m_sync) + { + delete m_sync; + m_sync = NULL; + } +} + +void IBCLogger::LogAccessThreadSafeHelperStatic(const void * p, pfnIBCAccessCallback callback) +{ + WRAPPER_NO_CONTRACT; + /* To make the logging callsite as small as possible keep the part that passes extra */ + /* argument to LogAccessThreadSafeHelper in separate non-inlined function */ + g_IBCLogger.LogAccessThreadSafeHelper(p, callback); +} + +void IBCLogger::LogAccessThreadSafeHelper(const void * p, pfnIBCAccessCallback callback) +{ + WRAPPER_NO_CONTRACT; + SO_NOT_MAINLINE_FUNCTION; + CONTRACT_VIOLATION( HostViolation ); + + /* For the Global Class we may see p == NULL */ + if (p == NULL) + return; + + Thread * pThread = GetThread(); + + /* This could be called by the concurrent GC thread*/ + /* where GetThread() returns NULL. In such cases,*/ + /* we want to log data accessed by the GC, but we will just ignore it for now.*/ + if (pThread == NULL) + return; + + ThreadLocalIBCInfo* pInfo = pThread->GetIBCInfo(); + if (pInfo == NULL) + { + CONTRACT_VIOLATION( ThrowsViolation | FaultViolation); + pInfo = new ThreadLocalIBCInfo(); + pThread->SetIBCInfo(pInfo); + } + + // + // During certain events we disable IBC logging. + // This may be to prevent deadlocks or we might + // not want to have IBC logging during these events. + // + if ( !pInfo->IsLoggingDisabled() ) + { + CONTRACT_VIOLATION( ThrowsViolation | TakesLockViolation | FaultViolation); + pInfo->CallbackHelper(p, callback); + } +} + +Crst * IBCLogger::GetSync() +{ + CONTRACTL + { + THROWS; + GC_NOTRIGGER; + MODE_ANY; + SO_NOT_MAINLINE; + } + CONTRACTL_END; + + if (!m_sync) + { + Crst * pNewSync = new Crst(CrstIbcProfile, CrstFlags(CRST_UNSAFE_ANYMODE | CRST_REENTRANCY | CRST_DEBUGGER_THREAD)); + if (FastInterlockCompareExchangePointer(m_sync.GetPointer(), pNewSync, NULL) != NULL) + { + // We lost the race + delete pNewSync; + } + } + MemoryBarrier(); + + return m_sync; +} + +void IBCLogger::DelayedCallbackPtr(pfnIBCAccessCallback callback, const void * pValue1, const void * pValue2 /*=NULL*/) +{ + WRAPPER_NO_CONTRACT; + + ThreadLocalIBCInfo* pInfo = GetThread()->GetIBCInfo(); + + // record that we could not currently resolve this callback + pInfo->SetCallbackFailed(); + + // If we are processing the delayed list then we don't want or need to + // add this pair <callback, pValue> to the delay list. + if (pInfo->ProcessingDelayedList()) + { + return; + } + + // We could throw an out of memory exception + CONTRACT_VIOLATION( ThrowsViolation ); + + // Get our thread local hashtable + DelayCallbackTable * pTable = pInfo->GetPtrDelayList(); + + // Create IbcCallback in our stack frame to use as a key for the Lookup + IbcCallback key(callback, pValue1, pValue2); + + // Perform lookup of this key in our hashtable + IbcCallback * pEntry = pTable->Lookup(&key); + + // If we already have this pair <callback, pValue> in our table + // then just return, because we don't need to add a duplicate + if (pEntry != NULL) + { + // Print out a debug message if we are debugging this + DEBUG_PRINTF4("Did not add duplicate delayed ptr callback: pfn=0x%08x, pValue1=0x%8p, pValue2=0x%8p\n", + pEntry->GetPfn(), pEntry->GetValue1(), pEntry->GetValue2()); + return; + } + // Now that we know that we will add a new entry into our hashtable + // We create a new IbcCallback in the heap to use as a persisted key + pEntry = new IbcCallback(callback, pValue1, pValue2); + + // Mark this key as new valid IbcCallback + pEntry->SetValid(); + + // Add the entry into our hashtable. + pTable->Add(pEntry); + + // Print out a debug message if we are debugging this + DEBUG_PRINTF4("Added a new delayed ptr callback: pfn=0x%08x, pValue1=0x%8p, pValue2=0x%8p\n", + key.GetPfn(), key.GetValue1(), key.GetValue2()); +} + +// some of IBC probes never complete successfully at all. +// and there is no point for them to stay in the delay list forever, +// because it significantly slows down the IBC instrumentation. +// c_maxRetries: the maximun number of times the unsuccessful IBC probe is tried +// c_minCount: is the minimum number of entries in the delay list that we +// need before we will call ProcessDelayedCallbacks() +// c_minCountIncr: is the minimum number of entries in the delay list that we +// need to add before we will call ProcessDelayedCallbacks() again +// +static const int c_maxRetries = 10; +static const int c_minCount = 8; +static const int c_minCountIncr = 8; + +ThreadLocalIBCInfo::ThreadLocalIBCInfo() +{ + LIMITED_METHOD_CONTRACT; + SO_NOT_MAINLINE_FUNCTION; + + m_fCallbackFailed = false; + m_fProcessingDelayedList = false; + m_fLoggingDisabled = false; + m_iMinCountToProcess = c_minCount; + m_pDelayList = NULL; +} + +ThreadLocalIBCInfo:: ~ThreadLocalIBCInfo() +{ + WRAPPER_NO_CONTRACT; + + if (m_pDelayList != NULL) + { + // We have one last call to the CallbackHelper to + // flush out any remaining items on our delay list + // + // CONTRACT_VIOLATION( ThrowsViolation | TakesLockViolation ); + // CallbackHelper(NULL, NULL); + + DeleteDelayedCallbacks(); + } +} + +void ThreadLocalIBCInfo::DeleteDelayedCallbacks() +{ + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + MODE_ANY; + SO_NOT_MAINLINE; + } + CONTRACTL_END; + + for (DelayCallbackTable::Iterator elem = m_pDelayList->Begin(), + end = m_pDelayList->End(); + (elem != end); elem++) + { + IbcCallback * pCallback = const_cast<IbcCallback *>(*elem); + + _ASSERTE(pCallback->IsValid()); + + // free up each of the IbcCallback pointers that we allocated + pCallback->Invalidate(); + delete pCallback; + } + + delete m_pDelayList; + m_pDelayList = NULL; +} + +void ThreadLocalIBCInfo::FlushDelayedCallbacks() +{ + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + MODE_ANY; + SO_NOT_MAINLINE; + } + CONTRACTL_END; + + if (m_pDelayList != NULL) + { + CONTRACT_VIOLATION( ThrowsViolation ); + CallbackHelper(NULL, NULL); + + DeleteDelayedCallbacks(); + } +} + +DelayCallbackTable * ThreadLocalIBCInfo::GetPtrDelayList() +{ + CONTRACTL + { + THROWS; + GC_NOTRIGGER; + MODE_ANY; + SO_NOT_MAINLINE; + } + CONTRACTL_END; + + if (m_pDelayList == NULL) + { + m_pDelayList = new DelayCallbackTable; + } + + return m_pDelayList; +} + +int ThreadLocalIBCInfo::ProcessDelayedCallbacks() +{ + CONTRACTL + { + THROWS; + GC_NOTRIGGER; + MODE_ANY; + SO_NOT_MAINLINE; + } + CONTRACTL_END; + + int removedCount = 0; // Our return result + + _ASSERTE(m_pDelayList != NULL); + _ASSERTE(m_fProcessingDelayedList == false); + + m_fProcessingDelayedList = true; + + // Processing Delayed Callback list + DEBUG_PRINTF2("Processing Delayed Callback list: GetCount()=%d\n", m_pDelayList->GetCount()); + + // try callbacks in the list + for (DelayCallbackTable::Iterator elem = m_pDelayList->Begin(), + end = m_pDelayList->End(); + (elem != end); elem++) + { + IbcCallback * pCallback = const_cast<IbcCallback *>(*elem); + + _ASSERTE(pCallback->IsValid()); + + // For each callback that we process we use the + // field m_fCallbackFailed to record wheather we + // failed or succeeded in resolving the callback + // + m_fCallbackFailed = false; + + pCallback->Invoke(); + + if (m_fCallbackFailed == false) + { + // Successfully proccessed a delayed callback + DEBUG_PRINTF5("Successfully processed a delayed callback: pfn=0x%08x, value1=0x%8p, value2=0x%8p, retries=%d\n", + pCallback->GetPfn(), pCallback->GetValue1(), pCallback->GetValue2(), pCallback->GetTryCount()); + + m_pDelayList->Remove(pCallback); + pCallback->Invalidate(); + delete pCallback; + removedCount++; + } + else if (pCallback->IncrementTryCount() > c_maxRetries) + { + // Failed a delayed callback by hitting c_maxRetries + DEBUG_PRINTF4("Failed a delayed callback by hitting c_maxRetries: pfn=0x%08x, value1=0x%8p, value2=0x%8p\n", + pCallback->GetPfn(), pCallback->GetValue1(), pCallback->GetValue2()); + + m_pDelayList->Remove(pCallback); + pCallback->Invalidate(); + delete pCallback; + removedCount++; + } + } + + // Done Processing Delayed Callback list + DEBUG_PRINTF3("Done Processing Delayed Callback list: removed %d items, %d remain\n", + removedCount, m_pDelayList->GetCount()); + + _ASSERTE(m_fProcessingDelayedList == true); + m_fProcessingDelayedList = false; + + return removedCount; +} + +void ThreadLocalIBCInfo::CallbackHelper(const void * p, pfnIBCAccessCallback callback) +{ + CONTRACTL + { + THROWS; + GC_NOTRIGGER; + MODE_ANY; + CAN_TAKE_LOCK; + SO_NOT_MAINLINE; + } + CONTRACTL_END; + + // Acquire the Crst lock before creating the IBCLoggingDisabler object. + // Only one thread at a time can be processing an IBC logging event. + CrstHolder lock(g_IBCLogger.GetSync()); + { + // @ToDo: methods called from here should assert that they have the lock that we just took + + IBCLoggingDisabler disableLogging( this ); // runs IBCLoggingDisabler::DisableLogging + + // Just in case the processing of delayed list was terminated with exception + m_fProcessingDelayedList = false; + + if (callback != NULL) + { + _ASSERTE(p != NULL); + + // For each callback that we process we use the + // field m_fCallbackFailed to record whether we + // failed or succeeded in resolving the callback + // + m_fCallbackFailed = false; + + callback(&g_IBCLogger, p, NULL); + + if (m_fCallbackFailed == false) + { + // If we were able to successfully process this ibc probe then + // the chances are good that the delayed probes will succeed too. + // Thus it may be worth proccessing the delayed call back list. + // We will process this list if it currently has at least + // MinCountToProcess items in the delay list. + // + int delayListAfter = (m_pDelayList == NULL) ? 0 : m_pDelayList->GetCount(); + if (delayListAfter >= GetMinCountToProcess()) + { + int numRemoved = ProcessDelayedCallbacks(); + if (numRemoved > 0) + { + // Reset the min count back down to the number that we still have remaining + m_iMinCountToProcess = m_pDelayList->GetCount(); + } + + // we increase the minCount by the min count increment so + // that we have to add a few new items to the delay list + // before we retry ProcessDelayedCallbacks() again. + IncMinCountToProcess(c_minCountIncr); + } + } + } + else // (callback == NULL) -- This is a special case + { + _ASSERTE(p == NULL); + + // We just need to call ProcessDelayedCallbacks() unconditionally + if (m_pDelayList->GetCount() > 0) + { + ProcessDelayedCallbacks(); + } + } + + // runs IBCLoggingDisabler::~IBCLoggingDisabler + // which runs IBCLoggingDisabler::EnableLogging + } +} + + +void IBCLogger::LogMethodAccessHelper(const MethodDesc* pMD, ULONG flagNum) +{ + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + MODE_ANY; + SO_NOT_MAINLINE; + PRECONDITION(g_IBCLogger.InstrEnabled()); + } + CONTRACTL_END; + + { + // Don't set the ReadMethodCode flag for EE implemented methods such as Invoke + if ((flagNum == ReadMethodCode) && pMD->IsEEImpl()) + return; + + // we cannot log before the ObjectClass or StringClass are loaded + if (g_pObjectClass == NULL || g_pStringClass == NULL) + goto DelayCallback; + + RelativeFixupPointer<PTR_MethodTable> * ppMT = pMD->GetMethodTablePtr(); + if (ppMT->IsNull()) + goto DelayCallback; + + TADDR pMaybeTaggedMT = ppMT->GetValueMaybeTagged((TADDR)ppMT); + if (CORCOMPILE_IS_POINTER_TAGGED(pMaybeTaggedMT)) + goto DelayCallback; + + MethodTable *pMT = (MethodTable *)pMaybeTaggedMT; + if (!pMT->IsRestored_NoLogging()) + goto DelayCallback; + + LogMethodTableAccessHelper(pMT); + + Module *pModule = pMT->GetModule(); + + if (MethodDescAccessInstrEnabled()) + { + mdToken token; + if ( pMD->HasClassOrMethodInstantiation_NoLogging() ) + { + // We will need to defer the Logging if we cannot compute the PreferredZapModule + + // + // If we are creating a generic type or method we can have null TypeHandle args + // TFS: 749998 + // We can also have unrestored MethodTables in our Instantiation args during FixupNativeEntry + // + Instantiation classInst = pMD->GetClassInstantiation(); + Instantiation methodInst = pMD->GetMethodInstantiation(); + for (DWORD i = 0; i < classInst.GetNumArgs(); i++) + { + TypeHandle thArg = classInst[i]; + if (thArg.IsNull() || thArg.IsEncodedFixup() || !thArg.IsRestored_NoLogging()) + goto DelayCallback; + } + for (DWORD i = 0; i < methodInst.GetNumArgs(); i++) + { + TypeHandle thArg = methodInst[i]; + if (thArg.IsNull() || thArg.IsEncodedFixup() || !thArg.IsRestored_NoLogging()) + goto DelayCallback; + } + + Module *pPZModule = Module::GetPreferredZapModuleForMethodDesc(pMD); + token = pPZModule->LogInstantiatedMethod(pMD, flagNum); + if (!IsNilToken(token)) + { + pPZModule->LogTokenAccess(token, MethodProfilingData, flagNum); + } + } + else + { + token = pMD->GetMemberDef_NoLogging(); + pModule->LogTokenAccess(token, MethodProfilingData, flagNum); + } + } + return; + } + +DelayCallback: + DelayedCallbackPtr(LogMethodAccessWrapper, pMD, (void *)(SIZE_T)flagNum); +} + +void IBCLogger::LogMethodAccessWrapper(IBCLogger* pLogger, const void * pValue1, const void * pValue2) +{ + WRAPPER_NO_CONTRACT; + pLogger->LogMethodAccessHelper((MethodDesc *)pValue1, (ULONG)(SIZE_T)pValue2); +} + +void IBCLogger::LogMethodDescAccessHelper(const MethodDesc *pMD) +{ + WRAPPER_NO_CONTRACT; + SO_NOT_MAINLINE_FUNCTION; + + LogMethodAccessHelper(pMD, ReadMethodDesc); +} + +void IBCLogger::LogMethodDescWriteAccessHelper(MethodDesc *pMD) +{ + WRAPPER_NO_CONTRACT; + SO_NOT_MAINLINE_FUNCTION; + + LogMethodAccessHelper(pMD, ReadMethodDesc); + LogMethodAccessHelper(pMD, WriteMethodDesc); +} + +void IBCLogger::LogMethodPrecodeAccessHelper(MethodDesc *pMD) +{ + WRAPPER_NO_CONTRACT; + SO_NOT_MAINLINE_FUNCTION; + + LogMethodAccessHelper(pMD, ReadMethodPrecode); +} + +void IBCLogger::LogMethodPrecodeWriteAccessHelper(MethodDesc *pMD) +{ + WRAPPER_NO_CONTRACT; + SO_NOT_MAINLINE_FUNCTION; + + LogMethodAccessHelper(pMD, ReadMethodPrecode); + LogMethodAccessHelper(pMD, WriteMethodPrecode); +} + +// Log access to method code or method header +void IBCLogger::LogMethodCodeAccessHelper(MethodDesc *pMD) +{ + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + MODE_ANY; + SO_NOT_MAINLINE; + PRECONDITION(g_IBCLogger.InstrEnabled()); + } + CONTRACTL_END; + + LogMethodAccessHelper(pMD, ReadMethodCode); +} + +// Log access to the method code and method header for NDirect calls +void IBCLogger::LogNDirectCodeAccessHelper(MethodDesc *pMD) +{ + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + MODE_ANY; + SO_NOT_MAINLINE; + PRECONDITION(g_IBCLogger.InstrEnabled()); + } + CONTRACTL_END; + + LogMethodAccessHelper(pMD, ReadMethodDesc); + LogMethodAccessHelper(pMD, ReadMethodCode); +} + + +// Log access to method gc info +void IBCLogger::LogMethodGCInfoAccessHelper(MethodDesc *pMD) +{ + WRAPPER_NO_CONTRACT; + SO_NOT_MAINLINE_FUNCTION; + + _ASSERTE(InstrEnabled()); + + LogMethodAccessHelper(pMD, ReadGCInfo); + LogMethodAccessHelper(pMD, CommonReadGCInfo); +} + +// Log access to method table +void IBCLogger::LogMethodTableAccessHelper(MethodTable const * pMT) +{ + WRAPPER_NO_CONTRACT; + SO_NOT_MAINLINE_FUNCTION; + + LogTypeAccessHelper(pMT, ReadMethodTable); +} + +// Log access to method table +void IBCLogger::LogTypeMethodTableAccessHelper(const TypeHandle *th) +{ + WRAPPER_NO_CONTRACT; + SO_NOT_MAINLINE_FUNCTION; + + LogTypeAccessHelper(*th, ReadMethodTable); +} + +// Log write access to method table +void IBCLogger::LogTypeMethodTableWriteableAccessHelper(const TypeHandle *th) +{ + WRAPPER_NO_CONTRACT; + SO_NOT_MAINLINE_FUNCTION; + + LogTypeAccessHelper(*th, ReadTypeDesc); + LogTypeAccessHelper(*th, WriteTypeDesc); +} + +// Log access via method table, to a token-based type or an instantiated type. +void IBCLogger::LogTypeAccessHelper(TypeHandle th, ULONG flagNum) +{ + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + MODE_ANY; + SO_NOT_MAINLINE; + PRECONDITION(g_IBCLogger.InstrEnabled()); + } + CONTRACTL_END; + + CONTRACT_VIOLATION( ThrowsViolation ); + + idTypeSpec token = idTypeSpecNil; + Module* pPreferredZapModule = NULL; + + if (th.IsNull() || th.IsEncodedFixup()) + return; + + // we cannot do any logging before the ObjectClass and StringClass are loaded + if (g_pObjectClass == NULL || g_pStringClass == NULL) + goto DelayCallback; + + if (!th.IsRestored_NoLogging()) + goto DelayCallback; + + // + // We assign the pPreferredZapModule and the token, then fall out to the LogTokenAccess + // + // Logging accesses to TypeDescs is done by blob and we create a special IBC token for the blob + if (th.IsTypeDesc()) + { + pPreferredZapModule = Module::GetPreferredZapModuleForTypeHandle(th); + + token = pPreferredZapModule->LogInstantiatedType(th, flagNum); + } + else + { + MethodTable *pMT = th.AsMethodTable(); + + if (pMT->IsArray()) + { + pPreferredZapModule = Module::GetPreferredZapModuleForMethodTable(pMT); + + token = pPreferredZapModule->LogInstantiatedType(th, flagNum); + } + else + { + Module* pModule = pMT->GetModule(); + + // Instantiations of generic types (like other parameterized types like arrays) + // need to be handled specially. Generic instantiations do not have a ready-made token + // in the loader module and need special handling + // + if (pMT->HasInstantiation() && // Is this any of List<T>, List<Blah<T>>, or List<String>? + !pMT->IsGenericTypeDefinition() && // Ignore the type definition (List<T>) as it corresponds to the typeDef token + !pMT->ContainsGenericVariables()) // We more or less don't save these anyway, apart from the GenericTypeDefinition + { + Instantiation inst = pMT->GetInstantiation(); + + // This function can get called from BuildMethodTableThrowing(). The instantiation info is not yet set then + if (!inst.IsEmpty() && !inst[0].IsNull()) + { + pPreferredZapModule = Module::GetPreferredZapModuleForMethodTable(pMT); + + token = pPreferredZapModule->LogInstantiatedType(th, flagNum); + } + } + else + { + pPreferredZapModule = pModule; + token = pMT->GetCl_NoLogging(); + } + } + } + + if (!IsNilToken(token)) + pPreferredZapModule->LogTokenAccess(token, TypeProfilingData, flagNum); + + return; + +DelayCallback: + DelayedCallbackPtr(LogTypeAccessWrapper, th.AsPtr(), (void *)(SIZE_T)flagNum); +} + +void IBCLogger::LogTypeAccessWrapper(IBCLogger* pLogger, const void * pValue, const void * pValue2) +{ + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + SO_TOLERANT; + MODE_ANY; + } + CONTRACTL_END; + pLogger->LogTypeAccessHelper(TypeHandle::FromPtr((void *)pValue), (ULONG)(SIZE_T)pValue2); +} + +// Log access to method tables which are private (i.e. methodtables that are updated in the ngen image) +void IBCLogger::LogMethodTableWriteableDataAccessHelper(MethodTable const * pMT) +{ + WRAPPER_NO_CONTRACT; + SO_NOT_MAINLINE_FUNCTION; + + LogTypeAccessHelper(pMT, ReadMethodTable); + LogTypeAccessHelper(pMT, ReadMethodTableWriteableData); +} + +// Log access to method tables which are private (i.e. methodtables that are updated in the ngen image) +void IBCLogger::LogMethodTableWriteableDataWriteAccessHelper(MethodTable *pMT) +{ + WRAPPER_NO_CONTRACT; + SO_NOT_MAINLINE_FUNCTION; + + LogTypeAccessHelper(pMT, ReadMethodTable); + LogTypeAccessHelper(pMT, WriteMethodTableWriteableData); +} + +void IBCLogger::LogMethodTableNonVirtualSlotsAccessHelper(MethodTable const * pMT) +{ + WRAPPER_NO_CONTRACT; + SO_NOT_MAINLINE_FUNCTION; + + LogTypeAccessHelper(pMT, ReadMethodTable); + LogTypeAccessHelper(pMT, ReadNonVirtualSlots); +} + +// Log access to EEClass +void IBCLogger::LogEEClassAndMethodTableAccessHelper(MethodTable * pMT) +{ + WRAPPER_NO_CONTRACT; + SO_NOT_MAINLINE_FUNCTION; + + if (pMT == NULL) + return; + + LogTypeAccessHelper(pMT, ReadMethodTable); + + if (!pMT->IsCanonicalMethodTable()) { + pMT = pMT->GetCanonicalMethodTable(); + LogTypeAccessHelper(pMT, ReadMethodTable); + } + + LogTypeAccessHelper(pMT, ReadEEClass); +} + +// Log write to EEClass +void IBCLogger::LogEEClassCOWTableAccessHelper(MethodTable * pMT) +{ + WRAPPER_NO_CONTRACT; + SO_NOT_MAINLINE_FUNCTION; + + if (pMT == NULL) + return; + + LogTypeAccessHelper(pMT, ReadMethodTable); + + if (!pMT->IsCanonicalMethodTable()) { + pMT = pMT->GetCanonicalMethodTable(); + LogTypeAccessHelper(pMT, ReadMethodTable); + } + + LogTypeAccessHelper(pMT, ReadEEClass); + LogTypeAccessHelper(pMT, WriteEEClass); +} + +// Log access to FieldDescs list in EEClass +void IBCLogger::LogFieldDescsAccessHelper(FieldDesc * pFD) +{ + WRAPPER_NO_CONTRACT; + SO_NOT_MAINLINE_FUNCTION; + + MethodTable * pMT = pFD->GetApproxEnclosingMethodTable_NoLogging(); + + LogTypeAccessHelper(pMT, ReadMethodTable); + + if (!pMT->IsCanonicalMethodTable()) { + pMT = pMT->GetCanonicalMethodTable(); + LogTypeAccessHelper(pMT, ReadMethodTable); + } + + LogTypeAccessHelper(pMT, ReadFieldDescs); +} + +void IBCLogger::LogDispatchMapAccessHelper(MethodTable *pMT) +{ + WRAPPER_NO_CONTRACT; + SO_NOT_MAINLINE_FUNCTION; + + LogTypeAccessHelper(pMT, ReadMethodTable); + LogTypeAccessHelper(pMT, ReadDispatchMap); +} + +void IBCLogger::LogDispatchTableAccessHelper(MethodTable *pMT) +{ + WRAPPER_NO_CONTRACT; + SO_NOT_MAINLINE_FUNCTION; + + LogTypeAccessHelper(pMT, ReadMethodTable); + LogTypeAccessHelper(pMT, ReadDispatchMap); + LogTypeAccessHelper(pMT, ReadDispatchTable); +} + +void IBCLogger::LogDispatchTableSlotAccessHelper(DispatchSlot *pDS) +{ + WRAPPER_NO_CONTRACT; + SO_NOT_MAINLINE_FUNCTION; + + if (pDS->IsNull()) + return; + + MethodDesc *pMD = MethodTable::GetMethodDescForSlotAddress(pDS->GetTarget()); + MethodTable *pMT = pMD->GetMethodTable_NoLogging(); + LogDispatchTableAccessHelper(pMT); +} + +// Log write to EEClass +void IBCLogger::LogFieldMarshalersReadAccessHelper(MethodTable * pMT) +{ + WRAPPER_NO_CONTRACT; + SO_NOT_MAINLINE_FUNCTION; + + if (pMT == NULL) + return; + + LogTypeAccessHelper(pMT, ReadMethodTable); + + if (!pMT->IsCanonicalMethodTable()) { + pMT = pMT->GetCanonicalMethodTable(); + LogTypeAccessHelper(pMT, ReadMethodTable); + } + + LogTypeAccessHelper(pMT, ReadEEClass); + LogTypeAccessHelper(pMT, ReadFieldMarshalers); +} + +// Log access to cctor info table +void IBCLogger::LogCCtorInfoReadAccessHelper(MethodTable *pMT) +{ + WRAPPER_NO_CONTRACT; + SO_NOT_MAINLINE_FUNCTION; + LogTypeAccessHelper(pMT, ReadCCtorInfo); +} + + +void IBCLogger::LogTypeHashTableAccessHelper(const TypeHandle *th) +{ + WRAPPER_NO_CONTRACT; + SO_NOT_MAINLINE_FUNCTION; + + LogTypeAccessHelper(*th, ReadTypeHashTable); +} + +// Log access to class hash table +void IBCLogger::LogClassHashTableAccessHelper(EEClassHashEntry *pEntry) +{ + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + MODE_ANY; + SO_NOT_MAINLINE; + PRECONDITION(g_IBCLogger.InstrEnabled()); + } + CONTRACTL_END; + + // ExecutionManager::FindZapModule may enter the host (if we were hosted), but it's + // ok since we're just logging IBC data. + CONTRACT_VIOLATION( HostViolation ); + + Module *pModule = ExecutionManager::FindZapModule(dac_cast<TADDR>(pEntry)); + if (pModule == NULL) + { + // if FindZapModule returns NULL, it always will return NULL + // so there is no point in adding a DelayedCallback here. + return; + } + + // we cannot log before the ObjectClass or StringClass are loaded + if (g_pObjectClass == NULL || g_pStringClass == NULL) + goto DelayCallback; + + HashDatum datum; + datum = pEntry->GetData(); + mdToken token; + if ((((ULONG_PTR) datum) & EECLASSHASH_TYPEHANDLE_DISCR) == 0) + { + TypeHandle t = TypeHandle::FromPtr(datum); + _ASSERTE(!t.IsNull()); + MethodTable *pMT = t.GetMethodTable(); + if (pMT == NULL) + goto DelayCallback; + + token = pMT->GetCl_NoLogging(); + } + else if (((ULONG_PTR)datum & EECLASSHASH_MDEXPORT_DISCR) == 0) + { + DWORD dwDatum = (DWORD)(DWORD_PTR)(datum); // <TODO> WIN64 - Pointer Truncation</TODO> + token = ((dwDatum >> 1) & 0x00ffffff) | mdtTypeDef; + } + else + return; + + pModule->LogTokenAccess(token, TypeProfilingData, ReadClassHashTable); + return; + +DelayCallback: + DelayedCallbackPtr(LogClassHashTableAccessWrapper, pEntry); +} + +// Log access to meta data +void IBCLogger::LogMetaDataAccessHelper(const void * addr) +{ + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + MODE_ANY; + SO_NOT_MAINLINE; + PRECONDITION(g_IBCLogger.InstrEnabled()); + } + CONTRACTL_END; + + // ExecutionManager::FindZapModule may enter the host (if we were hosted), but it's + // ok since we're just logging IBC data. + CONTRACT_VIOLATION( HostViolation ); + +#if METADATATRACKER_ENABLED + if (Module *pModule = ExecutionManager::FindZapModule(dac_cast<TADDR>(addr))) + { + mdToken token = MetaDataTracker::MapAddrToToken(addr); + + pModule->LogTokenAccess(token, ProfilingFlags_MetaData); + pModule->LogTokenAccess(token, CommonMetaData); + return; + } +#endif //METADATATRACKER_ENABLED + + // if FindZapModule returns NULL, it always will return NULL + // so there is no point in adding a DelayedCallback here. +} + +// Log a search to meta data +// See the comment above CMiniMdRW::GetHotMetadataTokensSearchAware +void IBCLogger::LogMetaDataSearchAccessHelper(const void * result) +{ + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + MODE_ANY; + SO_NOT_MAINLINE; + PRECONDITION(g_IBCLogger.InstrEnabled()); + } + CONTRACTL_END; + + // ExecutionManager::FindZapModule may enter the host (if we were hosted), but it's + // ok since we're just logging IBC data. + CONTRACT_VIOLATION( HostViolation ); + +#if METADATATRACKER_ENABLED + if (Module *pModule = ExecutionManager::FindZapModule(dac_cast<TADDR>(result))) + { + mdToken token = MetaDataTracker::MapAddrToToken(result); + + pModule->LogTokenAccess(token, ProfilingFlags_MetaData); + pModule->LogTokenAccess(token, CommonMetaData); + pModule->LogTokenAccess(token, ProfilingFlags_MetaDataSearch); + return; + } +#endif //METADATATRACKER_ENABLED + + // if FindZapModule returns NULL, it always will return NULL + // so there is no point in adding a DelayedCallback here. +} + +// Log access to method list associated with a CER +void IBCLogger::LogCerMethodListReadAccessHelper(MethodDesc *pMD) +{ + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + MODE_ANY; + SO_NOT_MAINLINE; + PRECONDITION(g_IBCLogger.InstrEnabled()); + } + CONTRACTL_END; + + LogMethodAccessHelper(pMD, ReadCerMethodList); +} + +void IBCLogger::LogRidMapAccessHelper( RidMapLogData data ) +{ + WRAPPER_NO_CONTRACT; + SO_NOT_MAINLINE_FUNCTION; + + data.First()->LogTokenAccess( data.Second(), RidMap ); +} + +// Log access to RVA data +void IBCLogger::LogRVADataAccessHelper(FieldDesc *pFD) +{ + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + MODE_ANY; + SO_NOT_MAINLINE; + PRECONDITION(g_IBCLogger.InstrEnabled()); + } + CONTRACTL_END; + + // we cannot log before the ObjectClass or StringClass are loaded + if (g_pObjectClass == NULL || g_pStringClass == NULL) + goto DelayCallback; + + if (CORCOMPILE_IS_POINTER_TAGGED(SIZE_T(pFD))) + return; + + MethodTable * pMT; + pMT = pFD->GetApproxEnclosingMethodTable(); + + if (!pMT->IsRestored_NoLogging()) + goto DelayCallback; + + if (pMT->HasInstantiation()) + return; + + pMT->GetModule()->LogTokenAccess(pFD->GetMemberDef(), TypeProfilingData, RVAFieldData); + return; + +DelayCallback: + DelayedCallbackPtr(LogRVADataAccessWrapper, pFD); +} + + +#define LOADORDER_INSTR 0x00000001 +#define RID_ACCESSORDER_INSTR 0x00000002 +#define METHODDESC_ACCESS_INSTR 0x00000004 +#define ALL_INSTR (LOADORDER_INSTR | RID_ACCESSORDER_INSTR | METHODDESC_ACCESS_INSTR) + +void IBCLogger::EnableAllInstr() +{ + LIMITED_METHOD_CONTRACT; +#if METADATATRACKER_ENABLED + MetaDataTracker::Enable(); + MetaDataTracker::s_IBCLogMetaDataAccess = IBCLogger::LogMetaDataAccessStatic; + MetaDataTracker::s_IBCLogMetaDataSearch = IBCLogger::LogMetaDataSearchAccessStatic; +#endif //METADATATRACKER_ENABLED + dwInstrEnabled = ALL_INSTR; +} + +void IBCLogger::DisableAllInstr() +{ + LIMITED_METHOD_CONTRACT; + dwInstrEnabled = 0; +} + +void IBCLogger::DisableRidAccessOrderInstr() +{ + LIMITED_METHOD_CONTRACT; + dwInstrEnabled &= (~RID_ACCESSORDER_INSTR); +} + +void IBCLogger::DisableMethodDescAccessInstr() +{ + LIMITED_METHOD_CONTRACT; + dwInstrEnabled &= (~METHODDESC_ACCESS_INSTR); +} + +BOOL IBCLogger::MethodDescAccessInstrEnabled() +{ + LIMITED_METHOD_CONTRACT; + return (dwInstrEnabled & METHODDESC_ACCESS_INSTR); +} + +BOOL IBCLogger::RidAccessInstrEnabled() +{ + LIMITED_METHOD_CONTRACT; + return (dwInstrEnabled & RID_ACCESSORDER_INSTR); +} + +#endif // IBCLOGGER_ENABLED |