1 // Licensed to the .NET Foundation under one or more agreements.
2 // The .NET Foundation licenses this file to you under the MIT license.
3 // See the LICENSE file in the project root for more information.
8 // Infrastructure for recording touches of EE data structures
22 // The IBCLogger class records touches of EE data structures. It is important to
23 // minimize the overhead of IBC recording on non-recording scenarios. Our goal is
24 // for all public methods to be inlined, and that the cost of doing the instrumentation
25 // check does not exceed one comparison and one branch.
34 struct EEClassHashEntry;
37 extern IBCLogger g_IBCLogger;
39 typedef PTR_VOID HashDatum;
41 typedef Pair< Module*, mdToken > RidMapLogData;
43 #if defined(FEATURE_PREJIT) && !defined(DACCESS_COMPILE) && !defined(CROSSGEN_COMPILE)
44 #define IBCLOGGER_ENABLED
47 #ifdef IBCLOGGER_ENABLED
49 // Base class for IBC probe callback
51 typedef void (* const pfnIBCAccessCallback)(IBCLogger* pLogger, const void * pValue, const void * pValue2);
56 IbcCallback(pfnIBCAccessCallback pCallback, const void * pValue1, const void * pValue2)
57 : m_pCallback(pCallback),
64 { LIMITED_METHOD_CONTRACT; }
70 m_pCallback(&g_IBCLogger, m_pValue1, m_pValue2);
75 LIMITED_METHOD_CONTRACT;
77 return (SIZE_T) m_pCallback;
80 pfnIBCAccessCallback GetCallback() const
82 LIMITED_METHOD_CONTRACT;
87 const void * GetValue1() const
89 LIMITED_METHOD_CONTRACT;
94 const void * GetValue2() const
96 LIMITED_METHOD_CONTRACT;
103 LIMITED_METHOD_CONTRACT;
105 m_id = ++s_highestId;
111 LIMITED_METHOD_CONTRACT;
122 return (m_id > 0) && (m_id <= s_highestId);
128 int IncrementTryCount()
133 int GetTryCount() const
139 pfnIBCAccessCallback m_pCallback;
140 const void * m_pValue1;
141 const void * m_pValue2;
147 static unsigned s_highestId;
151 class DelayCallbackTableTraits : public DefaultSHashTraits< IbcCallback * >
154 typedef IbcCallback * key_t;
156 static key_t GetKey(element_t e)
158 LIMITED_METHOD_CONTRACT;
162 static BOOL Equals(key_t k1, key_t k2)
164 LIMITED_METHOD_CONTRACT;
166 return (k1->GetCallback() == k2->GetCallback()) &&
167 (k1->GetValue1() == k2->GetValue1()) &&
168 (k1->GetValue2() == k2->GetValue2());
171 static count_t Hash(key_t k)
173 LIMITED_METHOD_CONTRACT;
175 SIZE_T hashLarge = (SIZE_T)k->GetCallback() ^
176 (SIZE_T)k->GetValue1() ^
177 (SIZE_T)k->GetValue2();
179 #if POINTER_BITS == 32
180 // sizeof(SIZE_T) == sizeof(COUNT_T)
183 // xor in the upper half as well.
184 count_t hash = *(count_t *)(&hashLarge);
185 for (unsigned int i = 1; i < POINTER_BITS / 32; i++)
187 hash ^= ((count_t *)&hashLarge)[i];
191 #endif // POINTER_BITS
194 static const element_t Null()
200 static bool IsNull(element_t e)
202 LIMITED_METHOD_CONTRACT;
206 static const element_t Deleted()
209 return (element_t)-1;
212 static bool IsDeleted(const element_t e)
214 LIMITED_METHOD_CONTRACT;
215 return e == (element_t)-1;
219 typedef SHash< DelayCallbackTableTraits > DelayCallbackTable;
221 class ThreadLocalIBCInfo
224 ThreadLocalIBCInfo();
225 ~ThreadLocalIBCInfo();
227 // BOOL IsLoggingDisable()
228 // This indicates that logging is currently disabled for this thread
229 // This is used to prevent the logging functionality from
230 // triggerring more logging (and thus causing a deadlock)
231 // It is also used to prevent IBC logging whenever a IBCLoggingDisabler
232 // object is used. For example we use this to disable IBC profiling
233 // whenever a thread starts a JIT compile event. That is because we
234 // don't want to "pollute" the IBC data gathering for the things
235 // that the JIT compiler touches.
236 // Finally since our IBC logging will need to allocate unmanaged memory
237 // we also disable IBC logging when we are inside a "can't alloc region"
238 // Typically this occurs when a thread is performing a GC.
239 BOOL IsLoggingDisabled()
241 LIMITED_METHOD_CONTRACT;
242 return m_fLoggingDisabled || IsInCantAllocRegion();
245 // We want to disable IBC logging, any further log calls are to be ignored until
246 // we call EnableLogging()
248 // This method returns true if it changed the value of m_fLoggingDisabled from false to true
249 // it returns false if the value of m_fLoggingDisabled was already set to true
250 // after this method executes the value of m_fLoggingDisabled will be true
251 bool DisableLogging()
253 LIMITED_METHOD_CONTRACT;
255 bool result = (m_fLoggingDisabled == false);
256 m_fLoggingDisabled = true;
261 // We want to re-enable IBC logging
264 LIMITED_METHOD_CONTRACT;
266 _ASSERTE(m_fLoggingDisabled == true);
268 m_fLoggingDisabled = false;
271 bool ProcessingDelayedList()
273 LIMITED_METHOD_CONTRACT;
274 return m_fProcessingDelayedList;
277 void SetCallbackFailed()
279 LIMITED_METHOD_CONTRACT;
280 m_fCallbackFailed = true;
283 int GetMinCountToProcess()
285 LIMITED_METHOD_CONTRACT;
286 return m_iMinCountToProcess;
289 void IncMinCountToProcess(int increment)
291 LIMITED_METHOD_CONTRACT;
292 m_iMinCountToProcess += increment;
295 DelayCallbackTable * GetPtrDelayList();
297 void DeleteDelayedCallbacks();
299 void FlushDelayedCallbacks();
301 int ProcessDelayedCallbacks();
303 void CallbackHelper(const void * p, pfnIBCAccessCallback callback);
306 bool m_fProcessingDelayedList;
307 bool m_fCallbackFailed;
308 bool m_fLoggingDisabled;
310 int m_iMinCountToProcess;
312 DelayCallbackTable * m_pDelayList;
315 class IBCLoggingDisabler
318 IBCLoggingDisabler();
319 IBCLoggingDisabler(bool ignore); // When ignore is true we treat this as a nop
320 IBCLoggingDisabler(ThreadLocalIBCInfo* pInfo);
321 ~IBCLoggingDisabler();
324 ThreadLocalIBCInfo* m_pInfo;
325 bool m_fDisabled; // true if this holder actually disable the logging
326 // false when this is a nested occurance and logging was already disabled
330 // IBCLoggerAwareAllocMemTracker should be used for allocation of IBC tracked structures during type loading.
332 // If type loading fails, the delayed IBC callbacks may contain pointers to the failed type or method.
333 // IBCLoggerAwareAllocMemTracker will ensure that the delayed IBC callbacks are flushed before the memory of
334 // the failed type or method is reclaimed. Otherwise, there would be stale pointers in the delayed IBC callbacks
335 // that would cause crashed during IBC logging.
337 class IBCLoggerAwareAllocMemTracker : public AllocMemTracker
340 IBCLoggerAwareAllocMemTracker()
345 ~IBCLoggerAwareAllocMemTracker();
348 #else // IBCLOGGER_ENABLED
350 typedef const void * pfnIBCAccessCallback;
352 class IBCLoggingDisabler
359 ~IBCLoggingDisabler()
364 class ThreadLocalIBCInfo
371 ~ThreadLocalIBCInfo()
376 class IBCLoggerAwareAllocMemTracker : public AllocMemTracker
379 IBCLoggerAwareAllocMemTracker()
383 ~IBCLoggerAwareAllocMemTracker()
388 #endif // IBCLOGGER_ENABLED
391 // IBCLogger is responsible for collecting profile data. Logging is turned on by the
392 // COMPlus_ZapBBInstr environment variable, and the actual writing to the file
393 // occurs in code:Module.WriteMethodProfileDataLogFile
397 // Methods for logging EE data structure accesses. All methods should be defined
398 // using the LOGACCESS macros, which creates the wrapper method that calls the
399 // helper when instrumentation is enabled. The public name of these methods should
400 // be of the form Log##name##Access where name describes the type of access to be
401 // logged. The private helpers are implemented in IBClogger.cpp.
404 #ifdef IBCLOGGER_ENABLED
406 #define LOGACCESS_PTR(name, type) \
407 LOGACCESS(name, type*, (type*), (const void *));
409 #define LOGACCESS_VALUE(name, type) \
410 LOGACCESS(name, type, *(type*), (const void *)&);
412 #define LOGACCESS(name, type, totype, toptr) \
414 __forceinline void Log##name##Access(type p) \
416 WRAPPER_NO_CONTRACT; \
417 /* We expect this to get inlined, so that it */ \
418 /* has low overhead when not instrumenting. */ \
419 /* So keep the function really small */ \
420 if ( InstrEnabled() ) \
421 Log##name##AccessStatic(toptr p); \
425 __declspec(noinline) static void Log##name##AccessStatic(const void * p) \
427 WRAPPER_NO_CONTRACT; \
428 /* To make the logging callsite as small as */ \
429 /* possible keep the part that passes extra */ \
430 /* argument to LogAccessThreadSafeHelper */ \
431 /* in separate non-inlined static functions */ \
432 LogAccessThreadSafeHelperStatic(p, Log##name##AccessWrapper); \
435 static void Log##name##AccessWrapper(IBCLogger* pLogger, const void * pValue1, const void * pValue2) \
437 WRAPPER_NO_CONTRACT; \
438 return pLogger->Log##name##AccessHelper(totype pValue1); \
440 void Log##name##AccessHelper(type p); \
443 static void LogAccessThreadSafeHelperStatic( const void * p, pfnIBCAccessCallback callback);
444 void LogAccessThreadSafeHelper( const void * p, pfnIBCAccessCallback callback);
446 void DelayedCallbackPtr(pfnIBCAccessCallback callback, const void * pValue1, const void * pValue2 = NULL);
448 #else // FEATURE_PREJIT && !DACCESS_COMPILE
450 #define LOGACCESS_PTR(name,type) \
452 void Log##name##Access(type* p) { SUPPORTS_DAC; } \
454 #define LOGACCESS_VALUE(name, type) \
456 void Log##name##Access(type p) { SUPPORTS_DAC; } \
458 #endif // FEATURE_PREJIT && !DACCESS_COMPILE
460 // Log access to method desc (which adds the method desc to the required list)
461 // Implemented by : code:IBCLogger.LogMethodDescAccessHelper
462 LOGACCESS_PTR(MethodDesc, const MethodDesc)
464 // Log access to method code or method header
465 // Implemented by : code:IBCLogger.LogMethodCodeAccessHelper
466 LOGACCESS_PTR(MethodCode, MethodDesc)
468 // Log access to the NDirect data stored for a MethodDesc
469 // also implies that the IL_STUB for the NDirect method is executed
470 // Implemented by : code:IBCLogger.LogNDirectCodeAccessHelper
471 LOGACCESS_PTR(NDirectCode,MethodDesc)
473 // Log access to method desc (which addes the method desc to the required list)
474 // Implemented by : code:IBCLogger.LogMethodDescWriteAccessHelper
475 LOGACCESS_PTR(MethodDescWrite,MethodDesc)
477 // Log access to method desc (which adds the method desc to the required list)
478 // Implemented by : code:IBCLogger.LogMethodPrecodeAccessHelper
479 LOGACCESS_PTR(MethodPrecode, MethodDesc)
481 // Log access to method desc (which addes the method desc to the required list)
482 // Implemented by : code:IBCLogger.LogMethodPrecodeWriteAccessHelper
483 LOGACCESS_PTR(MethodPrecodeWrite,MethodDesc)
485 // Log access to gc info
486 // Implemented by : code:IBCLogger.LogMethodGCInfoAccessHelper
487 LOGACCESS_PTR(MethodGCInfo, MethodDesc)
489 // Log access to method table
490 // Implemented by : code:IBCLogger.LogMethodTableAccessHelper
491 LOGACCESS_PTR(MethodTable, MethodTable const)
493 // Log access to method table
494 // Implemented by : code:IBCLogger.LogTypeMethodTableAccessHelper
495 LOGACCESS_PTR(TypeMethodTable, TypeHandle const)
497 // Log write access to method table
498 // Implemented by : code:IBCLogger.LogTypeMethodTableWriteableAccessHelper
499 LOGACCESS_PTR(TypeMethodTableWriteable, TypeHandle const)
501 // Log read access to private (written to) method table area
502 // Macro expands to : code:LogMethodTableWriteableDataAccessHelper
503 LOGACCESS_PTR(MethodTableWriteableData, MethodTable const)
505 // Log write access to private (written to) method table area
506 // Implemented by : code:IBCLogger.LogMethodTableWriteableDataWriteAccessHelper
507 LOGACCESS_PTR(MethodTableWriteableDataWrite,MethodTable)
509 // Log access to method table's NonVirtualSlotsArray
510 // Implemented by : code:IBCLogger.LogMethodTableNonVirtualSlotsAccessHelper
511 LOGACCESS_PTR(MethodTableNonVirtualSlots, MethodTable const)
513 // Log access to EEClass
514 // Implemented by : code:IBCLogger.LogEEClassAndMethodTableAccessHelper
515 LOGACCESS_PTR(EEClassAndMethodTable, MethodTable)
517 // Log access to EEClass COW table
518 // Implemented by : code:IBCLogger.LogEEClassCOWTableAccessHelper
519 LOGACCESS_PTR(EEClassCOWTable, MethodTable)
521 // Log access to the FieldDescs list in the EEClass
522 // Implemented by : code:IBCLogger.LogFieldDescsAccessHelper
523 LOGACCESS_PTR(FieldDescs, FieldDesc)
525 // Log access to the MTs dispatch map
526 // Implemented by : code:IBCLogger.LogDispatchMapAccessHelper
527 LOGACCESS_PTR(DispatchMap,MethodTable)
529 // Log read access to the MTs dispatch implementation table
530 // Implemented by : code:IBCLogger.LogDispatchTableAccessHelper
531 LOGACCESS_PTR(DispatchTable,MethodTable)
533 // Log read access to the MTs dispatch implementation table
534 // Implemented by : code:IBCLogger.LogDispatchTableAccessHelper
535 LOGACCESS_PTR(DispatchTableSlot,DispatchSlot)
537 // Log an update to the field marshalers
538 // Implemented by : code:IBCLogger.LogFieldMarshalersReadAccessHelper
539 LOGACCESS_PTR(FieldMarshalersRead,MethodTable)
541 // Log a lookup in the cctor info table
542 // Implemented by : code:IBCLogger.LogCCtorInfoReadAccessHelper
543 LOGACCESS_PTR(CCtorInfoRead,MethodTable)
545 // Log a lookup in the class hash table
546 // Implemented by : code:IBCLogger.LogClassHashTableAccessHelper
547 LOGACCESS_PTR(ClassHashTable,EEClassHashEntry)
549 // Log a lookup of the method list for a CER
550 // Implemented by : code:IBCLogger.LogCerMethodListReadAccessHelper
551 LOGACCESS_PTR(CerMethodListRead,MethodDesc)
553 // Log a metadata access
554 // Implemented by : code:IBCLogger.LogMetaDataAccessHelper
555 LOGACCESS_PTR(MetaData,const void)
557 // Log a metadata search
558 // Implemented by : code:IBCLogger.LogMetaDataSearchAccessHelper
559 LOGACCESS_PTR(MetaDataSearch,const void)
561 // Log a RVA fielddesc access */
562 // Implemented by : code:IBCLogger.LogRVADataAccessHelper
563 LOGACCESS_PTR(RVAData,FieldDesc)
565 // Log a lookup in the type hash table
566 // Implemented by : code:IBCLogger.LogTypeHashTableAccessHelper
567 LOGACCESS_PTR(TypeHashTable,TypeHandle const)
569 // Log a lookup in the Rid map
570 // Implemented by : code:IBCLogger.LogRidMapAccessHelper
571 LOGACCESS_VALUE( RidMap, RidMapLogData );
575 #ifdef IBCLOGGER_ENABLED
579 // Methods for enabling/disabling instrumentation.
580 void EnableAllInstr();
581 void DisableAllInstr();
582 #else // IBCLOGGER_ENABLED
583 void EnableAllInstr()
587 void DisableAllInstr()
590 #endif // IBCLOGGER_ENABLED
592 #ifndef DACCESS_COMPILE
593 void DisableRidAccessOrderInstr();
594 void DisableMethodDescAccessInstr();
596 inline BOOL InstrEnabled()
599 return (dwInstrEnabled != 0);
602 static CrstStatic * GetSync();
605 void LogMethodAccessHelper(const MethodDesc* pMD, ULONG flagNum);
606 static void LogMethodAccessWrapper(IBCLogger* pLogger, const void * pValue1, const void * pValue2);
608 void LogTypeAccessHelper(TypeHandle th, ULONG flagNum);
609 static void LogTypeAccessWrapper(IBCLogger* pLogger, const void * pValue1, const void * pValue2);
611 BOOL MethodDescAccessInstrEnabled();
612 BOOL RidAccessInstrEnabled();
615 DWORD dwInstrEnabled;
617 static CrstStatic m_sync;
618 #endif // DACCESS_COMPILE
621 #endif // IBCLOGGER_H