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.
4// IBClogger.H
5//
6
7//
8// Infrastructure for recording touches of EE data structures
9//
10//
11
12
13#ifndef IBCLOGGER_H
14#define IBCLOGGER_H
15
16#include <holder.h>
17#include <sarray.h>
18#include <crst.h>
19#include <synch.h>
20#include <shash.h>
21
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.
26//
27
28class MethodDesc;
29class MethodTable;
30class EEClass;
31class TypeHandle;
32struct DispatchSlot;
33class Module;
34struct EEClassHashEntry;
35class IBCLogger;
36
37extern IBCLogger g_IBCLogger;
38
39typedef PTR_VOID HashDatum;
40
41typedef Pair< Module*, mdToken > RidMapLogData;
42
43#if defined(FEATURE_PREJIT) && !defined(DACCESS_COMPILE) && !defined(CROSSGEN_COMPILE)
44#define IBCLOGGER_ENABLED
45#endif
46
47#ifdef IBCLOGGER_ENABLED
48//
49// Base class for IBC probe callback
50//
51typedef void (* const pfnIBCAccessCallback)(IBCLogger* pLogger, const void * pValue, const void * pValue2);
52
53class IbcCallback
54{
55public:
56 IbcCallback(pfnIBCAccessCallback pCallback, const void * pValue1, const void * pValue2)
57 : m_pCallback(pCallback),
58 m_pValue1(pValue1),
59 m_pValue2(pValue2),
60 m_tryCount(0)
61#ifdef _DEBUG
62 , m_id(0)
63#endif
64 { LIMITED_METHOD_CONTRACT; }
65
66 void Invoke() const
67 {
68 WRAPPER_NO_CONTRACT;
69
70 m_pCallback(&g_IBCLogger, m_pValue1, m_pValue2);
71 }
72
73 SIZE_T GetPfn() const
74 {
75 LIMITED_METHOD_CONTRACT;
76
77 return (SIZE_T) m_pCallback;
78 }
79
80 pfnIBCAccessCallback GetCallback() const
81 {
82 LIMITED_METHOD_CONTRACT;
83
84 return m_pCallback;
85 }
86
87 const void * GetValue1() const
88 {
89 LIMITED_METHOD_CONTRACT;
90
91 return m_pValue1;
92 }
93
94 const void * GetValue2() const
95 {
96 LIMITED_METHOD_CONTRACT;
97
98 return m_pValue2;
99 }
100
101 void SetValid()
102 {
103 LIMITED_METHOD_CONTRACT;
104#ifdef _DEBUG
105 m_id = ++s_highestId;
106#endif
107 }
108
109 void Invalidate()
110 {
111 LIMITED_METHOD_CONTRACT;
112#ifdef _DEBUG
113 m_id = 0;
114#endif
115 }
116
117 bool IsValid() const
118 {
119 WRAPPER_NO_CONTRACT;
120
121#ifdef _DEBUG
122 return (m_id > 0) && (m_id <= s_highestId);
123#else
124 return true;
125#endif
126 }
127
128 int IncrementTryCount()
129 {
130 return ++m_tryCount;
131 }
132
133 int GetTryCount() const
134 {
135 return m_tryCount;
136 }
137
138private:
139 pfnIBCAccessCallback m_pCallback;
140 const void * m_pValue1;
141 const void * m_pValue2;
142
143 int m_tryCount;
144
145#ifdef _DEBUG
146 unsigned m_id;
147 static unsigned s_highestId;
148#endif
149};
150
151class DelayCallbackTableTraits : public DefaultSHashTraits< IbcCallback * >
152{
153public:
154 typedef IbcCallback * key_t;
155
156 static key_t GetKey(element_t e)
157 {
158 LIMITED_METHOD_CONTRACT;
159 return e;
160 }
161
162 static BOOL Equals(key_t k1, key_t k2)
163 {
164 LIMITED_METHOD_CONTRACT;
165
166 return (k1->GetCallback() == k2->GetCallback()) &&
167 (k1->GetValue1() == k2->GetValue1()) &&
168 (k1->GetValue2() == k2->GetValue2());
169 }
170
171 static count_t Hash(key_t k)
172 {
173 LIMITED_METHOD_CONTRACT;
174
175 SIZE_T hashLarge = (SIZE_T)k->GetCallback() ^
176 (SIZE_T)k->GetValue1() ^
177 (SIZE_T)k->GetValue2();
178
179#if POINTER_BITS == 32
180 // sizeof(SIZE_T) == sizeof(COUNT_T)
181 return hashLarge;
182#else
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++)
186 {
187 hash ^= ((count_t *)&hashLarge)[i];
188 }
189
190 return hash;
191#endif // POINTER_BITS
192 }
193
194 static element_t Null()
195{
196 WRAPPER_NO_CONTRACT;
197 return NULL;
198 }
199
200 static bool IsNull(element_t e)
201 {
202 LIMITED_METHOD_CONTRACT;
203 return e == NULL;
204 }
205
206 static element_t Deleted()
207 {
208 WRAPPER_NO_CONTRACT;
209 return (element_t)-1;
210 }
211
212 static bool IsDeleted(const element_t e)
213 {
214 LIMITED_METHOD_CONTRACT;
215 return e == (element_t)-1;
216 }
217};
218
219typedef SHash< DelayCallbackTableTraits > DelayCallbackTable;
220
221class ThreadLocalIBCInfo
222{
223public:
224 ThreadLocalIBCInfo();
225 ~ThreadLocalIBCInfo();
226
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()
240 {
241 LIMITED_METHOD_CONTRACT;
242 return m_fLoggingDisabled || IsInCantAllocRegion();
243 }
244
245 // We want to disable IBC logging, any further log calls are to be ignored until
246 // we call EnableLogging()
247 //
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()
252 {
253 LIMITED_METHOD_CONTRACT;
254
255 bool result = (m_fLoggingDisabled == false);
256 m_fLoggingDisabled = true;
257
258 return result;
259 }
260
261 // We want to re-enable IBC logging
262 void EnableLogging()
263 {
264 LIMITED_METHOD_CONTRACT;
265
266 _ASSERTE(m_fLoggingDisabled == true);
267
268 m_fLoggingDisabled = false;
269 }
270
271 bool ProcessingDelayedList()
272 {
273 LIMITED_METHOD_CONTRACT;
274 return m_fProcessingDelayedList;
275 }
276
277 void SetCallbackFailed()
278 {
279 LIMITED_METHOD_CONTRACT;
280 m_fCallbackFailed = true;
281 }
282
283 int GetMinCountToProcess()
284 {
285 LIMITED_METHOD_CONTRACT;
286 return m_iMinCountToProcess;
287 }
288
289 void IncMinCountToProcess(int increment)
290 {
291 LIMITED_METHOD_CONTRACT;
292 m_iMinCountToProcess += increment;
293 }
294
295 DelayCallbackTable * GetPtrDelayList();
296
297 void DeleteDelayedCallbacks();
298
299 void FlushDelayedCallbacks();
300
301 int ProcessDelayedCallbacks();
302
303 void CallbackHelper(const void * p, pfnIBCAccessCallback callback);
304
305private:
306 bool m_fProcessingDelayedList;
307 bool m_fCallbackFailed;
308 bool m_fLoggingDisabled;
309
310 int m_iMinCountToProcess;
311
312 DelayCallbackTable * m_pDelayList;
313};
314
315class IBCLoggingDisabler
316{
317public:
318 IBCLoggingDisabler();
319 IBCLoggingDisabler(bool ignore); // When ignore is true we treat this as a nop
320 IBCLoggingDisabler(ThreadLocalIBCInfo* pInfo);
321 ~IBCLoggingDisabler();
322
323private:
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
327};
328
329//
330// IBCLoggerAwareAllocMemTracker should be used for allocation of IBC tracked structures during type loading.
331//
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.
336//
337class IBCLoggerAwareAllocMemTracker : public AllocMemTracker
338{
339public:
340 IBCLoggerAwareAllocMemTracker()
341 {
342 WRAPPER_NO_CONTRACT;
343 }
344
345 ~IBCLoggerAwareAllocMemTracker();
346};
347
348#else // IBCLOGGER_ENABLED
349
350typedef const void * pfnIBCAccessCallback;
351
352class IBCLoggingDisabler
353{
354public:
355 IBCLoggingDisabler()
356 {
357 }
358
359 ~IBCLoggingDisabler()
360 {
361 }
362};
363
364class ThreadLocalIBCInfo
365{
366public:
367 ThreadLocalIBCInfo()
368 {
369 }
370
371 ~ThreadLocalIBCInfo()
372 {
373 }
374};
375
376class IBCLoggerAwareAllocMemTracker : public AllocMemTracker
377{
378public:
379 IBCLoggerAwareAllocMemTracker()
380 {
381 }
382
383 ~IBCLoggerAwareAllocMemTracker()
384 {
385 }
386};
387
388#endif // IBCLOGGER_ENABLED
389
390
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
394class IBCLogger
395{
396 //
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.
402 //
403
404#ifdef IBCLOGGER_ENABLED
405
406#define LOGACCESS_PTR(name, type) \
407 LOGACCESS(name, type*, (type*), (const void *));
408
409#define LOGACCESS_VALUE(name, type) \
410 LOGACCESS(name, type, *(type*), (const void *)&);
411
412#define LOGACCESS(name, type, totype, toptr) \
413public: \
414 __forceinline void Log##name##Access(type p) \
415 { \
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); \
422 } \
423 \
424private: \
425 __declspec(noinline) static void Log##name##AccessStatic(const void * p) \
426 { \
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); \
433 } \
434 \
435 static void Log##name##AccessWrapper(IBCLogger* pLogger, const void * pValue1, const void * pValue2) \
436 { \
437 WRAPPER_NO_CONTRACT; \
438 return pLogger->Log##name##AccessHelper(totype pValue1); \
439 } \
440 void Log##name##AccessHelper(type p); \
441
442private:
443 static void LogAccessThreadSafeHelperStatic( const void * p, pfnIBCAccessCallback callback);
444 void LogAccessThreadSafeHelper( const void * p, pfnIBCAccessCallback callback);
445
446 void DelayedCallbackPtr(pfnIBCAccessCallback callback, const void * pValue1, const void * pValue2 = NULL);
447
448#else // FEATURE_PREJIT && !DACCESS_COMPILE
449
450#define LOGACCESS_PTR(name,type) \
451public: \
452 void Log##name##Access(type* p) { SUPPORTS_DAC; } \
453
454#define LOGACCESS_VALUE(name, type) \
455public: \
456 void Log##name##Access(type p) { SUPPORTS_DAC; } \
457
458#endif // FEATURE_PREJIT && !DACCESS_COMPILE
459
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)
463
464 // Log access to method code or method header
465 // Implemented by : code:IBCLogger.LogMethodCodeAccessHelper
466 LOGACCESS_PTR(MethodCode, MethodDesc)
467
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)
472
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)
476
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)
480
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)
484
485 // Log access to gc info
486 // Implemented by : code:IBCLogger.LogMethodGCInfoAccessHelper
487 LOGACCESS_PTR(MethodGCInfo, MethodDesc)
488
489 // Log access to method table
490 // Implemented by : code:IBCLogger.LogMethodTableAccessHelper
491 LOGACCESS_PTR(MethodTable, MethodTable const)
492
493 // Log access to method table
494 // Implemented by : code:IBCLogger.LogTypeMethodTableAccessHelper
495 LOGACCESS_PTR(TypeMethodTable, TypeHandle const)
496
497 // Log write access to method table
498 // Implemented by : code:IBCLogger.LogTypeMethodTableWriteableAccessHelper
499 LOGACCESS_PTR(TypeMethodTableWriteable, TypeHandle const)
500
501 // Log read access to private (written to) method table area
502 // Macro expands to : code:LogMethodTableWriteableDataAccessHelper
503 LOGACCESS_PTR(MethodTableWriteableData, MethodTable const)
504
505 // Log write access to private (written to) method table area
506 // Implemented by : code:IBCLogger.LogMethodTableWriteableDataWriteAccessHelper
507 LOGACCESS_PTR(MethodTableWriteableDataWrite,MethodTable)
508
509 // Log access to method table's NonVirtualSlotsArray
510 // Implemented by : code:IBCLogger.LogMethodTableNonVirtualSlotsAccessHelper
511 LOGACCESS_PTR(MethodTableNonVirtualSlots, MethodTable const)
512
513 // Log access to EEClass
514 // Implemented by : code:IBCLogger.LogEEClassAndMethodTableAccessHelper
515 LOGACCESS_PTR(EEClassAndMethodTable, MethodTable)
516
517 // Log access to EEClass COW table
518 // Implemented by : code:IBCLogger.LogEEClassCOWTableAccessHelper
519 LOGACCESS_PTR(EEClassCOWTable, MethodTable)
520
521 // Log access to the FieldDescs list in the EEClass
522 // Implemented by : code:IBCLogger.LogFieldDescsAccessHelper
523 LOGACCESS_PTR(FieldDescs, FieldDesc)
524
525 // Log access to the MTs dispatch map
526 // Implemented by : code:IBCLogger.LogDispatchMapAccessHelper
527 LOGACCESS_PTR(DispatchMap,MethodTable)
528
529 // Log read access to the MTs dispatch implementation table
530 // Implemented by : code:IBCLogger.LogDispatchTableAccessHelper
531 LOGACCESS_PTR(DispatchTable,MethodTable)
532
533 // Log read access to the MTs dispatch implementation table
534 // Implemented by : code:IBCLogger.LogDispatchTableAccessHelper
535 LOGACCESS_PTR(DispatchTableSlot,DispatchSlot)
536
537 // Log an update to the field marshalers
538 // Implemented by : code:IBCLogger.LogFieldMarshalersReadAccessHelper
539 LOGACCESS_PTR(FieldMarshalersRead,MethodTable)
540
541 // Log a lookup in the cctor info table
542 // Implemented by : code:IBCLogger.LogCCtorInfoReadAccessHelper
543 LOGACCESS_PTR(CCtorInfoRead,MethodTable)
544
545 // Log a lookup in the class hash table
546 // Implemented by : code:IBCLogger.LogClassHashTableAccessHelper
547 LOGACCESS_PTR(ClassHashTable,EEClassHashEntry)
548
549 // Log a lookup of the method list for a CER
550 // Implemented by : code:IBCLogger.LogCerMethodListReadAccessHelper
551 LOGACCESS_PTR(CerMethodListRead,MethodDesc)
552
553 // Log a metadata access
554 // Implemented by : code:IBCLogger.LogMetaDataAccessHelper
555 LOGACCESS_PTR(MetaData,const void)
556
557 // Log a metadata search
558 // Implemented by : code:IBCLogger.LogMetaDataSearchAccessHelper
559 LOGACCESS_PTR(MetaDataSearch,const void)
560
561 // Log a RVA fielddesc access */
562 // Implemented by : code:IBCLogger.LogRVADataAccessHelper
563 LOGACCESS_PTR(RVAData,FieldDesc)
564
565 // Log a lookup in the type hash table
566 // Implemented by : code:IBCLogger.LogTypeHashTableAccessHelper
567 LOGACCESS_PTR(TypeHashTable,TypeHandle const)
568
569 // Log a lookup in the Rid map
570 // Implemented by : code:IBCLogger.LogRidMapAccessHelper
571 LOGACCESS_VALUE( RidMap, RidMapLogData );
572
573public:
574
575#ifdef IBCLOGGER_ENABLED
576 IBCLogger();
577 ~IBCLogger();
578
579 // Methods for enabling/disabling instrumentation.
580 void EnableAllInstr();
581 void DisableAllInstr();
582#else // IBCLOGGER_ENABLED
583 void EnableAllInstr()
584 {
585 }
586
587 void DisableAllInstr()
588 {
589 }
590#endif // IBCLOGGER_ENABLED
591
592#ifndef DACCESS_COMPILE
593 void DisableRidAccessOrderInstr();
594 void DisableMethodDescAccessInstr();
595
596 inline BOOL InstrEnabled()
597 {
598 SUPPORTS_DAC;
599 return (dwInstrEnabled != 0);
600 }
601
602 static CrstStatic * GetSync();
603
604private:
605 void LogMethodAccessHelper(const MethodDesc* pMD, ULONG flagNum);
606 static void LogMethodAccessWrapper(IBCLogger* pLogger, const void * pValue1, const void * pValue2);
607
608 void LogTypeAccessHelper(TypeHandle th, ULONG flagNum);
609 static void LogTypeAccessWrapper(IBCLogger* pLogger, const void * pValue1, const void * pValue2);
610
611 BOOL MethodDescAccessInstrEnabled();
612 BOOL RidAccessInstrEnabled();
613
614private:
615 DWORD dwInstrEnabled;
616
617 static CrstStatic m_sync;
618#endif // DACCESS_COMPILE
619};
620
621#endif // IBCLOGGER_H
622