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.CPP
5//
6
7//
8// Infrastructure for recording touches of EE data structures
9//
10//
11
12
13#include "common.h"
14#ifdef IBCLOGGER_ENABLED
15#include "method.hpp"
16#include "corbbtprof.h"
17#include "metadatatracker.h"
18#include "field.h"
19#include "typekey.h"
20#include "ibclogger.h"
21
22//#ifdef _DEBUG
23//#define DEBUG_IBCLOGGER
24//#endif
25
26#ifdef DEBUG_IBCLOGGER
27
28#define DEBUG_PRINTF1(a) printf(a)
29#define DEBUG_PRINTF2(a,b) printf(a,b)
30#define DEBUG_PRINTF3(a,b,c) printf(a,b,c)
31#define DEBUG_PRINTF4(a,b,c,d) printf(a,b,c,d)
32#define DEBUG_PRINTF5(a,b,c,d,e) printf(a,b,c,d,e)
33#else
34#define DEBUG_PRINTF1(a)
35#define DEBUG_PRINTF2(a,b)
36#define DEBUG_PRINTF3(a,b,c)
37#define DEBUG_PRINTF4(a,b,c,d)
38#define DEBUG_PRINTF5(a,b,c,d,e)
39#endif
40
41DWORD dwIBCLogCount = 0;
42CrstStatic IBCLogger::m_sync;
43
44#ifdef _DEBUG
45/*static*/ unsigned IbcCallback::s_highestId = 0;
46#endif
47
48IBCLoggingDisabler::IBCLoggingDisabler()
49{
50 m_pInfo = NULL;
51 m_fDisabled = false;
52
53 if (g_IBCLogger.InstrEnabled())
54 {
55 m_pInfo = GetThread()->GetIBCInfo();
56 if (m_pInfo != NULL)
57 {
58 m_fDisabled = m_pInfo->DisableLogging();
59 }
60 }
61}
62
63IBCLoggingDisabler::IBCLoggingDisabler(bool ignore)
64{
65 m_pInfo = NULL;
66 m_fDisabled = false;
67
68 if (ignore == false)
69 {
70 if (g_IBCLogger.InstrEnabled())
71 {
72 m_pInfo = GetThread()->GetIBCInfo();
73 if (m_pInfo != NULL)
74 {
75 m_fDisabled = m_pInfo->DisableLogging();
76 }
77 }
78 }
79}
80
81IBCLoggingDisabler::IBCLoggingDisabler(ThreadLocalIBCInfo* pInfo)
82{
83 LIMITED_METHOD_CONTRACT;
84 m_pInfo = pInfo;
85
86 if (m_pInfo != NULL)
87 {
88 m_fDisabled = m_pInfo->DisableLogging();
89 }
90 else
91 {
92 m_fDisabled = false;
93 }
94}
95
96IBCLoggingDisabler::~IBCLoggingDisabler()
97{
98 LIMITED_METHOD_CONTRACT;
99 if (m_fDisabled)
100 m_pInfo->EnableLogging();
101}
102
103IBCLoggerAwareAllocMemTracker::~IBCLoggerAwareAllocMemTracker()
104{
105 CONTRACTL
106 {
107 NOTHROW;
108 GC_NOTRIGGER;
109 MODE_ANY;
110 }
111 CONTRACTL_END;
112
113 if (!m_fReleased)
114 {
115 GetThread()->FlushIBCInfo();
116 }
117}
118
119IBCLogger::IBCLogger()
120 : dwInstrEnabled(0)
121{
122 LIMITED_METHOD_CONTRACT;
123 m_sync.Init(CrstIbcProfile, CrstFlags(CRST_UNSAFE_ANYMODE | CRST_REENTRANCY | CRST_DEBUGGER_THREAD));
124}
125
126IBCLogger::~IBCLogger()
127{
128 WRAPPER_NO_CONTRACT;
129
130 m_sync.Destroy();
131}
132
133void IBCLogger::LogAccessThreadSafeHelperStatic(const void * p, pfnIBCAccessCallback callback)
134{
135 WRAPPER_NO_CONTRACT;
136 /* To make the logging callsite as small as possible keep the part that passes extra */
137 /* argument to LogAccessThreadSafeHelper in separate non-inlined function */
138 g_IBCLogger.LogAccessThreadSafeHelper(p, callback);
139}
140
141void IBCLogger::LogAccessThreadSafeHelper(const void * p, pfnIBCAccessCallback callback)
142{
143 WRAPPER_NO_CONTRACT;
144 SO_NOT_MAINLINE_FUNCTION;
145 CONTRACT_VIOLATION( HostViolation );
146
147 /* For the Global Class we may see p == NULL */
148 if (p == NULL)
149 return;
150
151 Thread * pThread = GetThread();
152
153 /* This could be called by the concurrent GC thread*/
154 /* where GetThread() returns NULL. In such cases,*/
155 /* we want to log data accessed by the GC, but we will just ignore it for now.*/
156 if (pThread == NULL)
157 return;
158
159 ThreadLocalIBCInfo* pInfo = pThread->GetIBCInfo();
160 if (pInfo == NULL)
161 {
162 CONTRACT_VIOLATION( ThrowsViolation | FaultViolation);
163 pInfo = new ThreadLocalIBCInfo();
164 pThread->SetIBCInfo(pInfo);
165 }
166
167 //
168 // During certain events we disable IBC logging.
169 // This may be to prevent deadlocks or we might
170 // not want to have IBC logging during these events.
171 //
172 if ( !pInfo->IsLoggingDisabled() )
173 {
174 CONTRACT_VIOLATION( ThrowsViolation | TakesLockViolation | FaultViolation);
175 pInfo->CallbackHelper(p, callback);
176 }
177}
178
179CrstStatic* IBCLogger::GetSync()
180{
181 CONTRACTL
182 {
183 THROWS;
184 GC_NOTRIGGER;
185 MODE_ANY;
186 SO_NOT_MAINLINE;
187 }
188 CONTRACTL_END;
189
190 return &m_sync;
191}
192
193void IBCLogger::DelayedCallbackPtr(pfnIBCAccessCallback callback, const void * pValue1, const void * pValue2 /*=NULL*/)
194{
195 WRAPPER_NO_CONTRACT;
196
197 ThreadLocalIBCInfo* pInfo = GetThread()->GetIBCInfo();
198
199 // record that we could not currently resolve this callback
200 pInfo->SetCallbackFailed();
201
202 // If we are processing the delayed list then we don't want or need to
203 // add this pair <callback, pValue> to the delay list.
204 if (pInfo->ProcessingDelayedList())
205 {
206 return;
207 }
208
209 // We could throw an out of memory exception
210 CONTRACT_VIOLATION( ThrowsViolation );
211
212 // Get our thread local hashtable
213 DelayCallbackTable * pTable = pInfo->GetPtrDelayList();
214
215 // Create IbcCallback in our stack frame to use as a key for the Lookup
216 IbcCallback key(callback, pValue1, pValue2);
217
218 // Perform lookup of this key in our hashtable
219 IbcCallback * pEntry = pTable->Lookup(&key);
220
221 // If we already have this pair <callback, pValue> in our table
222 // then just return, because we don't need to add a duplicate
223 if (pEntry != NULL)
224 {
225 // Print out a debug message if we are debugging this
226 DEBUG_PRINTF4("Did not add duplicate delayed ptr callback: pfn=0x%08x, pValue1=0x%8p, pValue2=0x%8p\n",
227 pEntry->GetPfn(), pEntry->GetValue1(), pEntry->GetValue2());
228 return;
229 }
230 // Now that we know that we will add a new entry into our hashtable
231 // We create a new IbcCallback in the heap to use as a persisted key
232 pEntry = new IbcCallback(callback, pValue1, pValue2);
233
234 // Mark this key as new valid IbcCallback
235 pEntry->SetValid();
236
237 // Add the entry into our hashtable.
238 pTable->Add(pEntry);
239
240 // Print out a debug message if we are debugging this
241 DEBUG_PRINTF4("Added a new delayed ptr callback: pfn=0x%08x, pValue1=0x%8p, pValue2=0x%8p\n",
242 key.GetPfn(), key.GetValue1(), key.GetValue2());
243}
244
245// some of IBC probes never complete successfully at all.
246// and there is no point for them to stay in the delay list forever,
247// because it significantly slows down the IBC instrumentation.
248// c_maxRetries: the maximun number of times the unsuccessful IBC probe is tried
249// c_minCount: is the minimum number of entries in the delay list that we
250// need before we will call ProcessDelayedCallbacks()
251// c_minCountIncr: is the minimum number of entries in the delay list that we
252// need to add before we will call ProcessDelayedCallbacks() again
253//
254static const int c_maxRetries = 10;
255static const int c_minCount = 8;
256static const int c_minCountIncr = 8;
257
258ThreadLocalIBCInfo::ThreadLocalIBCInfo()
259{
260 LIMITED_METHOD_CONTRACT;
261 SO_NOT_MAINLINE_FUNCTION;
262
263 m_fCallbackFailed = false;
264 m_fProcessingDelayedList = false;
265 m_fLoggingDisabled = false;
266 m_iMinCountToProcess = c_minCount;
267 m_pDelayList = NULL;
268}
269
270ThreadLocalIBCInfo:: ~ThreadLocalIBCInfo()
271{
272 WRAPPER_NO_CONTRACT;
273
274 if (m_pDelayList != NULL)
275 {
276 // We have one last call to the CallbackHelper to
277 // flush out any remaining items on our delay list
278 //
279 // CONTRACT_VIOLATION( ThrowsViolation | TakesLockViolation );
280 // CallbackHelper(NULL, NULL);
281
282 DeleteDelayedCallbacks();
283 }
284}
285
286void ThreadLocalIBCInfo::DeleteDelayedCallbacks()
287{
288 CONTRACTL
289 {
290 NOTHROW;
291 GC_NOTRIGGER;
292 MODE_ANY;
293 SO_NOT_MAINLINE;
294 }
295 CONTRACTL_END;
296
297 for (DelayCallbackTable::Iterator elem = m_pDelayList->Begin(),
298 end = m_pDelayList->End();
299 (elem != end); elem++)
300 {
301 IbcCallback * pCallback = const_cast<IbcCallback *>(*elem);
302
303 _ASSERTE(pCallback->IsValid());
304
305 // free up each of the IbcCallback pointers that we allocated
306 pCallback->Invalidate();
307 delete pCallback;
308 }
309
310 delete m_pDelayList;
311 m_pDelayList = NULL;
312}
313
314void ThreadLocalIBCInfo::FlushDelayedCallbacks()
315{
316 CONTRACTL
317 {
318 NOTHROW;
319 GC_NOTRIGGER;
320 MODE_ANY;
321 SO_NOT_MAINLINE;
322 }
323 CONTRACTL_END;
324
325 if (m_pDelayList != NULL)
326 {
327 CONTRACT_VIOLATION( ThrowsViolation );
328 CallbackHelper(NULL, NULL);
329
330 DeleteDelayedCallbacks();
331 }
332}
333
334DelayCallbackTable * ThreadLocalIBCInfo::GetPtrDelayList()
335{
336 CONTRACTL
337 {
338 THROWS;
339 GC_NOTRIGGER;
340 MODE_ANY;
341 SO_NOT_MAINLINE;
342 }
343 CONTRACTL_END;
344
345 if (m_pDelayList == NULL)
346 {
347 m_pDelayList = new DelayCallbackTable;
348 }
349
350 return m_pDelayList;
351}
352
353int ThreadLocalIBCInfo::ProcessDelayedCallbacks()
354{
355 CONTRACTL
356 {
357 THROWS;
358 GC_NOTRIGGER;
359 MODE_ANY;
360 SO_NOT_MAINLINE;
361 }
362 CONTRACTL_END;
363
364 int removedCount = 0; // Our return result
365
366 _ASSERTE(m_pDelayList != NULL);
367 _ASSERTE(m_fProcessingDelayedList == false);
368
369 m_fProcessingDelayedList = true;
370
371 // Processing Delayed Callback list
372 DEBUG_PRINTF2("Processing Delayed Callback list: GetCount()=%d\n", m_pDelayList->GetCount());
373
374 // try callbacks in the list
375 for (DelayCallbackTable::Iterator elem = m_pDelayList->Begin(),
376 end = m_pDelayList->End();
377 (elem != end); elem++)
378 {
379 IbcCallback * pCallback = const_cast<IbcCallback *>(*elem);
380
381 _ASSERTE(pCallback->IsValid());
382
383 // For each callback that we process we use the
384 // field m_fCallbackFailed to record wheather we
385 // failed or succeeded in resolving the callback
386 //
387 m_fCallbackFailed = false;
388
389 pCallback->Invoke();
390
391 if (m_fCallbackFailed == false)
392 {
393 // Successfully proccessed a delayed callback
394 DEBUG_PRINTF5("Successfully processed a delayed callback: pfn=0x%08x, value1=0x%8p, value2=0x%8p, retries=%d\n",
395 pCallback->GetPfn(), pCallback->GetValue1(), pCallback->GetValue2(), pCallback->GetTryCount());
396
397 m_pDelayList->Remove(pCallback);
398 pCallback->Invalidate();
399 delete pCallback;
400 removedCount++;
401 }
402 else if (pCallback->IncrementTryCount() > c_maxRetries)
403 {
404 // Failed a delayed callback by hitting c_maxRetries
405 DEBUG_PRINTF4("Failed a delayed callback by hitting c_maxRetries: pfn=0x%08x, value1=0x%8p, value2=0x%8p\n",
406 pCallback->GetPfn(), pCallback->GetValue1(), pCallback->GetValue2());
407
408 m_pDelayList->Remove(pCallback);
409 pCallback->Invalidate();
410 delete pCallback;
411 removedCount++;
412 }
413 }
414
415 // Done Processing Delayed Callback list
416 DEBUG_PRINTF3("Done Processing Delayed Callback list: removed %d items, %d remain\n",
417 removedCount, m_pDelayList->GetCount());
418
419 _ASSERTE(m_fProcessingDelayedList == true);
420 m_fProcessingDelayedList = false;
421
422 return removedCount;
423}
424
425void ThreadLocalIBCInfo::CallbackHelper(const void * p, pfnIBCAccessCallback callback)
426{
427 CONTRACTL
428 {
429 THROWS;
430 GC_NOTRIGGER;
431 MODE_ANY;
432 CAN_TAKE_LOCK;
433 SO_NOT_MAINLINE;
434 }
435 CONTRACTL_END;
436
437 // Acquire the Crst lock before creating the IBCLoggingDisabler object.
438 // Only one thread at a time can be processing an IBC logging event.
439 CrstHolder lock(IBCLogger::GetSync());
440 {
441 // @ToDo: methods called from here should assert that they have the lock that we just took
442
443 IBCLoggingDisabler disableLogging( this ); // runs IBCLoggingDisabler::DisableLogging
444
445 // Just in case the processing of delayed list was terminated with exception
446 m_fProcessingDelayedList = false;
447
448 if (callback != NULL)
449 {
450 _ASSERTE(p != NULL);
451
452 // For each callback that we process we use the
453 // field m_fCallbackFailed to record whether we
454 // failed or succeeded in resolving the callback
455 //
456 m_fCallbackFailed = false;
457
458 callback(&g_IBCLogger, p, NULL);
459
460 if (m_fCallbackFailed == false)
461 {
462 // If we were able to successfully process this ibc probe then
463 // the chances are good that the delayed probes will succeed too.
464 // Thus it may be worth proccessing the delayed call back list.
465 // We will process this list if it currently has at least
466 // MinCountToProcess items in the delay list.
467 //
468 int delayListAfter = (m_pDelayList == NULL) ? 0 : m_pDelayList->GetCount();
469 if (delayListAfter >= GetMinCountToProcess())
470 {
471 int numRemoved = ProcessDelayedCallbacks();
472 if (numRemoved > 0)
473 {
474 // Reset the min count back down to the number that we still have remaining
475 m_iMinCountToProcess = m_pDelayList->GetCount();
476 }
477
478 // we increase the minCount by the min count increment so
479 // that we have to add a few new items to the delay list
480 // before we retry ProcessDelayedCallbacks() again.
481 IncMinCountToProcess(c_minCountIncr);
482 }
483 }
484 }
485 else // (callback == NULL) -- This is a special case
486 {
487 _ASSERTE(p == NULL);
488
489 // We just need to call ProcessDelayedCallbacks() unconditionally
490 if (m_pDelayList->GetCount() > 0)
491 {
492 ProcessDelayedCallbacks();
493 }
494 }
495
496 // runs IBCLoggingDisabler::~IBCLoggingDisabler
497 // which runs IBCLoggingDisabler::EnableLogging
498 }
499}
500
501
502void IBCLogger::LogMethodAccessHelper(const MethodDesc* pMD, ULONG flagNum)
503{
504 CONTRACTL
505 {
506 NOTHROW;
507 GC_NOTRIGGER;
508 MODE_ANY;
509 SO_NOT_MAINLINE;
510 PRECONDITION(g_IBCLogger.InstrEnabled());
511 }
512 CONTRACTL_END;
513
514 {
515 // Don't set the ReadMethodCode flag for EE implemented methods such as Invoke
516 if ((flagNum == ReadMethodCode) && pMD->IsEEImpl())
517 return;
518
519 // we cannot log before the ObjectClass or StringClass are loaded
520 if (g_pObjectClass == NULL || g_pStringClass == NULL)
521 goto DelayCallback;
522
523 RelativeFixupPointer<PTR_MethodTable> * ppMT = pMD->GetMethodTablePtr();
524 if (ppMT->IsNull())
525 goto DelayCallback;
526
527 TADDR pMaybeTaggedMT = ppMT->GetValueMaybeTagged((TADDR)ppMT);
528 if (CORCOMPILE_IS_POINTER_TAGGED(pMaybeTaggedMT))
529 goto DelayCallback;
530
531 MethodTable *pMT = (MethodTable *)pMaybeTaggedMT;
532 if (!pMT->IsRestored_NoLogging())
533 goto DelayCallback;
534
535 LogMethodTableAccessHelper(pMT);
536
537 Module *pModule = pMT->GetModule();
538
539 if (MethodDescAccessInstrEnabled())
540 {
541 mdToken token;
542 if ( pMD->HasClassOrMethodInstantiation_NoLogging() )
543 {
544 // We will need to defer the Logging if we cannot compute the PreferredZapModule
545
546 //
547 // If we are creating a generic type or method we can have null TypeHandle args
548 // TFS: 749998
549 // We can also have unrestored MethodTables in our Instantiation args during FixupNativeEntry
550 //
551 Instantiation classInst = pMD->GetClassInstantiation();
552 Instantiation methodInst = pMD->GetMethodInstantiation();
553 for (DWORD i = 0; i < classInst.GetNumArgs(); i++)
554 {
555 TypeHandle thArg = classInst[i];
556 if (thArg.IsNull() || thArg.IsEncodedFixup() || !thArg.IsRestored_NoLogging())
557 goto DelayCallback;
558 }
559 for (DWORD i = 0; i < methodInst.GetNumArgs(); i++)
560 {
561 TypeHandle thArg = methodInst[i];
562 if (thArg.IsNull() || thArg.IsEncodedFixup() || !thArg.IsRestored_NoLogging())
563 goto DelayCallback;
564 }
565
566 Module *pPZModule = Module::GetPreferredZapModuleForMethodDesc(pMD);
567 token = pPZModule->LogInstantiatedMethod(pMD, flagNum);
568 if (!IsNilToken(token))
569 {
570 pPZModule->LogTokenAccess(token, MethodProfilingData, flagNum);
571 }
572 }
573 else
574 {
575 token = pMD->GetMemberDef_NoLogging();
576 pModule->LogTokenAccess(token, MethodProfilingData, flagNum);
577 }
578 }
579 return;
580 }
581
582DelayCallback:
583 DelayedCallbackPtr(LogMethodAccessWrapper, pMD, (void *)(SIZE_T)flagNum);
584}
585
586void IBCLogger::LogMethodAccessWrapper(IBCLogger* pLogger, const void * pValue1, const void * pValue2)
587{
588 WRAPPER_NO_CONTRACT;
589 pLogger->LogMethodAccessHelper((MethodDesc *)pValue1, (ULONG)(SIZE_T)pValue2);
590}
591
592void IBCLogger::LogMethodDescAccessHelper(const MethodDesc *pMD)
593{
594 WRAPPER_NO_CONTRACT;
595 SO_NOT_MAINLINE_FUNCTION;
596
597 LogMethodAccessHelper(pMD, ReadMethodDesc);
598}
599
600void IBCLogger::LogMethodDescWriteAccessHelper(MethodDesc *pMD)
601{
602 WRAPPER_NO_CONTRACT;
603 SO_NOT_MAINLINE_FUNCTION;
604
605 LogMethodAccessHelper(pMD, ReadMethodDesc);
606 LogMethodAccessHelper(pMD, WriteMethodDesc);
607}
608
609void IBCLogger::LogMethodPrecodeAccessHelper(MethodDesc *pMD)
610{
611 WRAPPER_NO_CONTRACT;
612 SO_NOT_MAINLINE_FUNCTION;
613
614 LogMethodAccessHelper(pMD, ReadMethodPrecode);
615}
616
617void IBCLogger::LogMethodPrecodeWriteAccessHelper(MethodDesc *pMD)
618{
619 WRAPPER_NO_CONTRACT;
620 SO_NOT_MAINLINE_FUNCTION;
621
622 LogMethodAccessHelper(pMD, ReadMethodPrecode);
623 LogMethodAccessHelper(pMD, WriteMethodPrecode);
624}
625
626// Log access to method code or method header
627void IBCLogger::LogMethodCodeAccessHelper(MethodDesc *pMD)
628{
629 CONTRACTL
630 {
631 NOTHROW;
632 GC_NOTRIGGER;
633 MODE_ANY;
634 SO_NOT_MAINLINE;
635 PRECONDITION(g_IBCLogger.InstrEnabled());
636 }
637 CONTRACTL_END;
638
639 LogMethodAccessHelper(pMD, ReadMethodCode);
640}
641
642// Log access to the method code and method header for NDirect calls
643void IBCLogger::LogNDirectCodeAccessHelper(MethodDesc *pMD)
644{
645 CONTRACTL
646 {
647 NOTHROW;
648 GC_NOTRIGGER;
649 MODE_ANY;
650 SO_NOT_MAINLINE;
651 PRECONDITION(g_IBCLogger.InstrEnabled());
652 }
653 CONTRACTL_END;
654
655 LogMethodAccessHelper(pMD, ReadMethodDesc);
656 LogMethodAccessHelper(pMD, ReadMethodCode);
657}
658
659
660// Log access to method gc info
661void IBCLogger::LogMethodGCInfoAccessHelper(MethodDesc *pMD)
662{
663 WRAPPER_NO_CONTRACT;
664 SO_NOT_MAINLINE_FUNCTION;
665
666 _ASSERTE(InstrEnabled());
667
668 LogMethodAccessHelper(pMD, ReadGCInfo);
669 LogMethodAccessHelper(pMD, CommonReadGCInfo);
670}
671
672// Log access to method table
673void IBCLogger::LogMethodTableAccessHelper(MethodTable const * pMT)
674{
675 WRAPPER_NO_CONTRACT;
676 SO_NOT_MAINLINE_FUNCTION;
677
678 LogTypeAccessHelper(pMT, ReadMethodTable);
679}
680
681// Log access to method table
682void IBCLogger::LogTypeMethodTableAccessHelper(const TypeHandle *th)
683{
684 WRAPPER_NO_CONTRACT;
685 SO_NOT_MAINLINE_FUNCTION;
686
687 LogTypeAccessHelper(*th, ReadMethodTable);
688}
689
690// Log write access to method table
691void IBCLogger::LogTypeMethodTableWriteableAccessHelper(const TypeHandle *th)
692{
693 WRAPPER_NO_CONTRACT;
694 SO_NOT_MAINLINE_FUNCTION;
695
696 LogTypeAccessHelper(*th, ReadTypeDesc);
697 LogTypeAccessHelper(*th, WriteTypeDesc);
698}
699
700// Log access via method table, to a token-based type or an instantiated type.
701void IBCLogger::LogTypeAccessHelper(TypeHandle th, ULONG flagNum)
702{
703 CONTRACTL
704 {
705 NOTHROW;
706 GC_NOTRIGGER;
707 MODE_ANY;
708 SO_NOT_MAINLINE;
709 PRECONDITION(g_IBCLogger.InstrEnabled());
710 }
711 CONTRACTL_END;
712
713 CONTRACT_VIOLATION( ThrowsViolation );
714
715 idTypeSpec token = idTypeSpecNil;
716 Module* pPreferredZapModule = NULL;
717
718 if (th.IsNull() || th.IsEncodedFixup())
719 return;
720
721 // we cannot do any logging before the ObjectClass and StringClass are loaded
722 if (g_pObjectClass == NULL || g_pStringClass == NULL)
723 goto DelayCallback;
724
725 if (!th.IsRestored_NoLogging())
726 goto DelayCallback;
727
728 //
729 // We assign the pPreferredZapModule and the token, then fall out to the LogTokenAccess
730 //
731 // Logging accesses to TypeDescs is done by blob and we create a special IBC token for the blob
732 if (th.IsTypeDesc())
733 {
734 pPreferredZapModule = Module::GetPreferredZapModuleForTypeHandle(th);
735
736 token = pPreferredZapModule->LogInstantiatedType(th, flagNum);
737 }
738 else
739 {
740 MethodTable *pMT = th.AsMethodTable();
741
742 if (pMT->IsArray())
743 {
744 pPreferredZapModule = Module::GetPreferredZapModuleForMethodTable(pMT);
745
746 token = pPreferredZapModule->LogInstantiatedType(th, flagNum);
747 }
748 else
749 {
750 Module* pModule = pMT->GetModule();
751
752 // Instantiations of generic types (like other parameterized types like arrays)
753 // need to be handled specially. Generic instantiations do not have a ready-made token
754 // in the loader module and need special handling
755 //
756 if (pMT->HasInstantiation() && // Is this any of List<T>, List<Blah<T>>, or List<String>?
757 !pMT->IsGenericTypeDefinition() && // Ignore the type definition (List<T>) as it corresponds to the typeDef token
758 !pMT->ContainsGenericVariables()) // We more or less don't save these anyway, apart from the GenericTypeDefinition
759 {
760 Instantiation inst = pMT->GetInstantiation();
761
762 // This function can get called from BuildMethodTableThrowing(). The instantiation info is not yet set then
763 if (!inst.IsEmpty() && !inst[0].IsNull())
764 {
765 pPreferredZapModule = Module::GetPreferredZapModuleForMethodTable(pMT);
766
767 token = pPreferredZapModule->LogInstantiatedType(th, flagNum);
768 }
769 }
770 else
771 {
772 pPreferredZapModule = pModule;
773 token = pMT->GetCl_NoLogging();
774 }
775 }
776 }
777
778 if (!IsNilToken(token))
779 pPreferredZapModule->LogTokenAccess(token, TypeProfilingData, flagNum);
780
781 return;
782
783DelayCallback:
784 DelayedCallbackPtr(LogTypeAccessWrapper, th.AsPtr(), (void *)(SIZE_T)flagNum);
785}
786
787void IBCLogger::LogTypeAccessWrapper(IBCLogger* pLogger, const void * pValue, const void * pValue2)
788{
789 CONTRACTL
790 {
791 NOTHROW;
792 GC_NOTRIGGER;
793 SO_TOLERANT;
794 MODE_ANY;
795 }
796 CONTRACTL_END;
797 pLogger->LogTypeAccessHelper(TypeHandle::FromPtr((void *)pValue), (ULONG)(SIZE_T)pValue2);
798}
799
800// Log access to method tables which are private (i.e. methodtables that are updated in the ngen image)
801void IBCLogger::LogMethodTableWriteableDataAccessHelper(MethodTable const * pMT)
802{
803 WRAPPER_NO_CONTRACT;
804 SO_NOT_MAINLINE_FUNCTION;
805
806 LogTypeAccessHelper(pMT, ReadMethodTable);
807 LogTypeAccessHelper(pMT, ReadMethodTableWriteableData);
808}
809
810// Log access to method tables which are private (i.e. methodtables that are updated in the ngen image)
811void IBCLogger::LogMethodTableWriteableDataWriteAccessHelper(MethodTable *pMT)
812{
813 WRAPPER_NO_CONTRACT;
814 SO_NOT_MAINLINE_FUNCTION;
815
816 LogTypeAccessHelper(pMT, ReadMethodTable);
817 LogTypeAccessHelper(pMT, WriteMethodTableWriteableData);
818}
819
820void IBCLogger::LogMethodTableNonVirtualSlotsAccessHelper(MethodTable const * pMT)
821{
822 WRAPPER_NO_CONTRACT;
823 SO_NOT_MAINLINE_FUNCTION;
824
825 LogTypeAccessHelper(pMT, ReadMethodTable);
826 LogTypeAccessHelper(pMT, ReadNonVirtualSlots);
827}
828
829// Log access to EEClass
830void IBCLogger::LogEEClassAndMethodTableAccessHelper(MethodTable * pMT)
831{
832 WRAPPER_NO_CONTRACT;
833 SO_NOT_MAINLINE_FUNCTION;
834
835 if (pMT == NULL)
836 return;
837
838 LogTypeAccessHelper(pMT, ReadMethodTable);
839
840 if (!pMT->IsCanonicalMethodTable()) {
841 pMT = pMT->GetCanonicalMethodTable();
842 LogTypeAccessHelper(pMT, ReadMethodTable);
843 }
844
845 LogTypeAccessHelper(pMT, ReadEEClass);
846}
847
848// Log write to EEClass
849void IBCLogger::LogEEClassCOWTableAccessHelper(MethodTable * pMT)
850{
851 WRAPPER_NO_CONTRACT;
852 SO_NOT_MAINLINE_FUNCTION;
853
854 if (pMT == NULL)
855 return;
856
857 LogTypeAccessHelper(pMT, ReadMethodTable);
858
859 if (!pMT->IsCanonicalMethodTable()) {
860 pMT = pMT->GetCanonicalMethodTable();
861 LogTypeAccessHelper(pMT, ReadMethodTable);
862 }
863
864 LogTypeAccessHelper(pMT, ReadEEClass);
865 LogTypeAccessHelper(pMT, WriteEEClass);
866}
867
868// Log access to FieldDescs list in EEClass
869void IBCLogger::LogFieldDescsAccessHelper(FieldDesc * pFD)
870{
871 WRAPPER_NO_CONTRACT;
872 SO_NOT_MAINLINE_FUNCTION;
873
874 MethodTable * pMT = pFD->GetApproxEnclosingMethodTable_NoLogging();
875
876 LogTypeAccessHelper(pMT, ReadMethodTable);
877
878 if (!pMT->IsCanonicalMethodTable()) {
879 pMT = pMT->GetCanonicalMethodTable();
880 LogTypeAccessHelper(pMT, ReadMethodTable);
881 }
882
883 LogTypeAccessHelper(pMT, ReadFieldDescs);
884}
885
886void IBCLogger::LogDispatchMapAccessHelper(MethodTable *pMT)
887{
888 WRAPPER_NO_CONTRACT;
889 SO_NOT_MAINLINE_FUNCTION;
890
891 LogTypeAccessHelper(pMT, ReadMethodTable);
892 LogTypeAccessHelper(pMT, ReadDispatchMap);
893}
894
895void IBCLogger::LogDispatchTableAccessHelper(MethodTable *pMT)
896{
897 WRAPPER_NO_CONTRACT;
898 SO_NOT_MAINLINE_FUNCTION;
899
900 LogTypeAccessHelper(pMT, ReadMethodTable);
901 LogTypeAccessHelper(pMT, ReadDispatchMap);
902 LogTypeAccessHelper(pMT, ReadDispatchTable);
903}
904
905void IBCLogger::LogDispatchTableSlotAccessHelper(DispatchSlot *pDS)
906{
907 WRAPPER_NO_CONTRACT;
908 SO_NOT_MAINLINE_FUNCTION;
909
910 if (pDS->IsNull())
911 return;
912
913 MethodDesc *pMD = MethodTable::GetMethodDescForSlotAddress(pDS->GetTarget());
914 MethodTable *pMT = pMD->GetMethodTable_NoLogging();
915 LogDispatchTableAccessHelper(pMT);
916}
917
918// Log write to EEClass
919void IBCLogger::LogFieldMarshalersReadAccessHelper(MethodTable * pMT)
920{
921 WRAPPER_NO_CONTRACT;
922 SO_NOT_MAINLINE_FUNCTION;
923
924 if (pMT == NULL)
925 return;
926
927 LogTypeAccessHelper(pMT, ReadMethodTable);
928
929 if (!pMT->IsCanonicalMethodTable()) {
930 pMT = pMT->GetCanonicalMethodTable();
931 LogTypeAccessHelper(pMT, ReadMethodTable);
932 }
933
934 LogTypeAccessHelper(pMT, ReadEEClass);
935 LogTypeAccessHelper(pMT, ReadFieldMarshalers);
936}
937
938// Log access to cctor info table
939void IBCLogger::LogCCtorInfoReadAccessHelper(MethodTable *pMT)
940{
941 WRAPPER_NO_CONTRACT;
942 SO_NOT_MAINLINE_FUNCTION;
943 LogTypeAccessHelper(pMT, ReadCCtorInfo);
944}
945
946
947void IBCLogger::LogTypeHashTableAccessHelper(const TypeHandle *th)
948{
949 WRAPPER_NO_CONTRACT;
950 SO_NOT_MAINLINE_FUNCTION;
951
952 LogTypeAccessHelper(*th, ReadTypeHashTable);
953}
954
955// Log access to class hash table
956void IBCLogger::LogClassHashTableAccessHelper(EEClassHashEntry *pEntry)
957{
958 CONTRACTL
959 {
960 NOTHROW;
961 GC_NOTRIGGER;
962 MODE_ANY;
963 SO_NOT_MAINLINE;
964 PRECONDITION(g_IBCLogger.InstrEnabled());
965 }
966 CONTRACTL_END;
967
968 // ExecutionManager::FindZapModule may enter the host (if we were hosted), but it's
969 // ok since we're just logging IBC data.
970 CONTRACT_VIOLATION( HostViolation );
971
972 Module *pModule = ExecutionManager::FindZapModule(dac_cast<TADDR>(pEntry));
973 if (pModule == NULL)
974 {
975 // if FindZapModule returns NULL, it always will return NULL
976 // so there is no point in adding a DelayedCallback here.
977 return;
978 }
979
980 // we cannot log before the ObjectClass or StringClass are loaded
981 if (g_pObjectClass == NULL || g_pStringClass == NULL)
982 goto DelayCallback;
983
984 HashDatum datum;
985 datum = pEntry->GetData();
986 mdToken token;
987 if ((((ULONG_PTR) datum) & EECLASSHASH_TYPEHANDLE_DISCR) == 0)
988 {
989 TypeHandle t = TypeHandle::FromPtr(datum);
990 _ASSERTE(!t.IsNull());
991 MethodTable *pMT = t.GetMethodTable();
992 if (pMT == NULL)
993 goto DelayCallback;
994
995 token = pMT->GetCl_NoLogging();
996 }
997 else if (((ULONG_PTR)datum & EECLASSHASH_MDEXPORT_DISCR) == 0)
998 {
999 DWORD dwDatum = (DWORD)(DWORD_PTR)(datum); // <TODO> WIN64 - Pointer Truncation</TODO>
1000 token = ((dwDatum >> 1) & 0x00ffffff) | mdtTypeDef;
1001 }
1002 else
1003 return;
1004
1005 pModule->LogTokenAccess(token, TypeProfilingData, ReadClassHashTable);
1006 return;
1007
1008DelayCallback:
1009 DelayedCallbackPtr(LogClassHashTableAccessWrapper, pEntry);
1010}
1011
1012// Log access to meta data
1013void IBCLogger::LogMetaDataAccessHelper(const void * addr)
1014{
1015 CONTRACTL
1016 {
1017 NOTHROW;
1018 GC_NOTRIGGER;
1019 MODE_ANY;
1020 SO_NOT_MAINLINE;
1021 PRECONDITION(g_IBCLogger.InstrEnabled());
1022 }
1023 CONTRACTL_END;
1024
1025 // ExecutionManager::FindZapModule may enter the host (if we were hosted), but it's
1026 // ok since we're just logging IBC data.
1027 CONTRACT_VIOLATION( HostViolation );
1028
1029#if METADATATRACKER_ENABLED
1030 if (Module *pModule = ExecutionManager::FindZapModule(dac_cast<TADDR>(addr)))
1031 {
1032 mdToken token = MetaDataTracker::MapAddrToToken(addr);
1033
1034 pModule->LogTokenAccess(token, ProfilingFlags_MetaData);
1035 pModule->LogTokenAccess(token, CommonMetaData);
1036 return;
1037 }
1038#endif //METADATATRACKER_ENABLED
1039
1040 // if FindZapModule returns NULL, it always will return NULL
1041 // so there is no point in adding a DelayedCallback here.
1042}
1043
1044// Log a search to meta data
1045// See the comment above CMiniMdRW::GetHotMetadataTokensSearchAware
1046void IBCLogger::LogMetaDataSearchAccessHelper(const void * result)
1047{
1048 CONTRACTL
1049 {
1050 NOTHROW;
1051 GC_NOTRIGGER;
1052 MODE_ANY;
1053 SO_NOT_MAINLINE;
1054 PRECONDITION(g_IBCLogger.InstrEnabled());
1055 }
1056 CONTRACTL_END;
1057
1058 // ExecutionManager::FindZapModule may enter the host (if we were hosted), but it's
1059 // ok since we're just logging IBC data.
1060 CONTRACT_VIOLATION( HostViolation );
1061
1062#if METADATATRACKER_ENABLED
1063 if (Module *pModule = ExecutionManager::FindZapModule(dac_cast<TADDR>(result)))
1064 {
1065 mdToken token = MetaDataTracker::MapAddrToToken(result);
1066
1067 pModule->LogTokenAccess(token, ProfilingFlags_MetaData);
1068 pModule->LogTokenAccess(token, CommonMetaData);
1069 pModule->LogTokenAccess(token, ProfilingFlags_MetaDataSearch);
1070 return;
1071 }
1072#endif //METADATATRACKER_ENABLED
1073
1074 // if FindZapModule returns NULL, it always will return NULL
1075 // so there is no point in adding a DelayedCallback here.
1076}
1077
1078// Log access to method list associated with a CER
1079void IBCLogger::LogCerMethodListReadAccessHelper(MethodDesc *pMD)
1080{
1081 CONTRACTL
1082 {
1083 NOTHROW;
1084 GC_NOTRIGGER;
1085 MODE_ANY;
1086 SO_NOT_MAINLINE;
1087 PRECONDITION(g_IBCLogger.InstrEnabled());
1088 }
1089 CONTRACTL_END;
1090
1091 LogMethodAccessHelper(pMD, ReadCerMethodList);
1092}
1093
1094void IBCLogger::LogRidMapAccessHelper( RidMapLogData data )
1095{
1096 WRAPPER_NO_CONTRACT;
1097 SO_NOT_MAINLINE_FUNCTION;
1098
1099 data.First()->LogTokenAccess( data.Second(), RidMap );
1100}
1101
1102// Log access to RVA data
1103void IBCLogger::LogRVADataAccessHelper(FieldDesc *pFD)
1104{
1105 CONTRACTL
1106 {
1107 NOTHROW;
1108 GC_NOTRIGGER;
1109 MODE_ANY;
1110 SO_NOT_MAINLINE;
1111 PRECONDITION(g_IBCLogger.InstrEnabled());
1112 }
1113 CONTRACTL_END;
1114
1115 // we cannot log before the ObjectClass or StringClass are loaded
1116 if (g_pObjectClass == NULL || g_pStringClass == NULL)
1117 goto DelayCallback;
1118
1119 if (CORCOMPILE_IS_POINTER_TAGGED(SIZE_T(pFD)))
1120 return;
1121
1122 MethodTable * pMT;
1123 pMT = pFD->GetApproxEnclosingMethodTable();
1124
1125 if (!pMT->IsRestored_NoLogging())
1126 goto DelayCallback;
1127
1128 if (pMT->HasInstantiation())
1129 return;
1130
1131 pMT->GetModule()->LogTokenAccess(pFD->GetMemberDef(), TypeProfilingData, RVAFieldData);
1132 return;
1133
1134DelayCallback:
1135 DelayedCallbackPtr(LogRVADataAccessWrapper, pFD);
1136}
1137
1138
1139#define LOADORDER_INSTR 0x00000001
1140#define RID_ACCESSORDER_INSTR 0x00000002
1141#define METHODDESC_ACCESS_INSTR 0x00000004
1142#define ALL_INSTR (LOADORDER_INSTR | RID_ACCESSORDER_INSTR | METHODDESC_ACCESS_INSTR)
1143
1144void IBCLogger::EnableAllInstr()
1145{
1146 LIMITED_METHOD_CONTRACT;
1147#if METADATATRACKER_ENABLED
1148 MetaDataTracker::Enable();
1149 MetaDataTracker::s_IBCLogMetaDataAccess = IBCLogger::LogMetaDataAccessStatic;
1150 MetaDataTracker::s_IBCLogMetaDataSearch = IBCLogger::LogMetaDataSearchAccessStatic;
1151#endif //METADATATRACKER_ENABLED
1152 dwInstrEnabled = ALL_INSTR;
1153}
1154
1155void IBCLogger::DisableAllInstr()
1156{
1157 LIMITED_METHOD_CONTRACT;
1158 dwInstrEnabled = 0;
1159}
1160
1161void IBCLogger::DisableRidAccessOrderInstr()
1162{
1163 LIMITED_METHOD_CONTRACT;
1164 dwInstrEnabled &= (~RID_ACCESSORDER_INSTR);
1165}
1166
1167void IBCLogger::DisableMethodDescAccessInstr()
1168{
1169 LIMITED_METHOD_CONTRACT;
1170 dwInstrEnabled &= (~METHODDESC_ACCESS_INSTR);
1171}
1172
1173BOOL IBCLogger::MethodDescAccessInstrEnabled()
1174{
1175 LIMITED_METHOD_CONTRACT;
1176 return (dwInstrEnabled & METHODDESC_ACCESS_INSTR);
1177}
1178
1179BOOL IBCLogger::RidAccessInstrEnabled()
1180{
1181 LIMITED_METHOD_CONTRACT;
1182 return (dwInstrEnabled & RID_ACCESSORDER_INSTR);
1183}
1184
1185#endif // IBCLOGGER_ENABLED
1186