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//
5// EEToProfInterfaceImpl.h
6//
7
8//
9// Declaration of class that wraps calling into the profiler's implementation
10// of ICorProfilerCallback*
11//
12
13// ======================================================================================
14
15
16#ifndef __EETOPROFINTERFACEIMPL_H__
17#define __EETOPROFINTERFACEIMPL_H__
18
19#include <stddef.h>
20#include "profilepriv.h"
21#include "eeprofinterfaces.h"
22#include "shash.h"
23#include "eventtracebase.h"
24#include "gcinterface.h"
25
26class SimpleRWLock;
27
28class ProfToEEInterfaceImpl;
29
30interface IAssemblyBindingClosure;
31struct AssemblyReferenceClosureWalkContextForProfAPI;
32
33const GUID k_guidZero = {0};
34
35class EEToProfInterfaceImpl
36{
37public:
38
39 //
40 // Internal initialization / cleanup
41 //
42
43 EEToProfInterfaceImpl();
44 ~EEToProfInterfaceImpl();
45
46 HRESULT Init(
47 ProfToEEInterfaceImpl * pProfToEE,
48 const CLSID * pClsid,
49 __inout_z LPCWSTR wszClsid,
50 __in_z LPCWSTR wszProfileDLL,
51 BOOL fLoadedViaAttach,
52 DWORD dwConcurrentGCWaitTimeoutInMs);
53
54 BOOL IsCallback3Supported();
55 BOOL IsCallback4Supported();
56 BOOL IsCallback5Supported();
57 BOOL IsCallback6Supported();
58 BOOL IsCallback7Supported();
59 BOOL IsCallback8Supported();
60
61 HRESULT SetEventMask(DWORD dwEventMask, DWORD dwEventMaskHigh);
62
63 // Used in ProfToEEInterfaceImpl.cpp to set this to the profiler's hook's
64 // function pointer (see SetFunctionIDMapper).
65 void SetFunctionIDMapper(FunctionIDMapper * pFunc);
66 void SetFunctionIDMapper2(FunctionIDMapper2 * pFunc, void * clientData);
67
68 FunctionIDMapper * GetFunctionIDMapper();
69 FunctionIDMapper2 * GetFunctionIDMapper2();
70 BOOL IsLoadedViaAttach();
71 HRESULT EnsureProfilerDetachable();
72 void SetUnrevertiblyModifiedILFlag();
73 void SetModifiedRejitState();
74
75 FunctionEnter * GetEnterHook();
76 FunctionLeave * GetLeaveHook();
77 FunctionTailcall * GetTailcallHook();
78
79 FunctionEnter2 * GetEnter2Hook();
80 FunctionLeave2 * GetLeave2Hook();
81 FunctionTailcall2 * GetTailcall2Hook();
82
83 FunctionEnter3 * GetEnter3Hook();
84 FunctionLeave3 * GetLeave3Hook();
85 FunctionTailcall3 * GetTailcall3Hook();
86 FunctionEnter3WithInfo * GetEnter3WithInfoHook();
87 FunctionLeave3WithInfo * GetLeave3WithInfoHook();
88 FunctionTailcall3WithInfo * GetTailcall3WithInfoHook();
89
90 BOOL IsClientIDToFunctionIDMappingEnabled();
91
92 UINT_PTR LookupClientIDFromCache(FunctionID functionID);
93
94 HRESULT SetEnterLeaveFunctionHooks(
95 FunctionEnter * pFuncEnter,
96 FunctionLeave * pFuncLeave,
97 FunctionTailcall * pFuncTailcall);
98
99 HRESULT SetEnterLeaveFunctionHooks2(
100 FunctionEnter2 * pFuncEnter,
101 FunctionLeave2 * pFuncLeave,
102 FunctionTailcall2 * pFuncTailcall);
103
104 HRESULT SetEnterLeaveFunctionHooks3(
105 FunctionEnter3 * pFuncEnter3,
106 FunctionLeave3 * pFuncLeave3,
107 FunctionTailcall3 * pFuncTailcall3);
108
109 HRESULT SetEnterLeaveFunctionHooks3WithInfo(
110 FunctionEnter3WithInfo * pFuncEnter3WithInfo,
111 FunctionLeave3WithInfo * pFuncLeave3WithInfo,
112 FunctionTailcall3WithInfo * pFuncTailcall3WithInfo);
113
114 BOOL RequiresGenericsContextForEnterLeave();
115
116 UINT_PTR EEFunctionIDMapper(FunctionID funcId, BOOL * pbHookFunction);
117
118 // This fills in the non call-specific portions of the cookie GUID.
119 // This should only be called once at startup if necessary.
120 HRESULT InitGUID();
121
122 // This will assign a mostly-unique GUID. If enough calls to GetGUID
123 // are made from the same thread, then the GUIDs will cycle.
124 // (Current, it will cycle every 256 calls)
125 void GetGUID(GUID * pGUID);
126
127 //
128 // Initialize callback
129 //
130
131 HRESULT Initialize();
132
133 HRESULT InitializeForAttach(void * pvClientData, UINT cbClientData);
134
135 HRESULT ProfilerAttachComplete();
136
137 //
138 // Thread Events
139 //
140
141 HRESULT ThreadCreated(
142 ThreadID threadID);
143
144 HRESULT ThreadDestroyed(
145 ThreadID threadID);
146
147 HRESULT ThreadAssignedToOSThread(ThreadID managedThreadId,
148 DWORD osThreadId);
149
150 HRESULT ThreadNameChanged(ThreadID managedThreadId,
151 ULONG cchName,
152 __in_ecount_opt(cchName) WCHAR name[]);
153
154 //
155 // Startup/Shutdown Events
156 //
157
158 HRESULT Shutdown();
159
160 //
161 // JIT/Function Events
162 //
163
164 HRESULT FunctionUnloadStarted(
165 FunctionID functionId);
166
167 HRESULT JITCompilationFinished(
168 FunctionID functionId,
169 HRESULT hrStatus,
170 BOOL fIsSafeToBlock);
171
172 HRESULT JITCompilationStarted(
173 FunctionID functionId,
174 BOOL fIsSafeToBlock);
175
176 HRESULT DynamicMethodJITCompilationStarted(
177 FunctionID functionId,
178 BOOL fIsSafeToBlock,
179 LPCBYTE pILHeader,
180 ULONG cbILHeader);
181
182 HRESULT DynamicMethodJITCompilationFinished(
183 FunctionID functionId,
184 HRESULT hrStatus,
185 BOOL fIsSafeToBlock);
186
187 HRESULT DynamicMethodUnloaded(
188 FunctionID functionId);
189
190 HRESULT JITCachedFunctionSearchStarted(
191 /* [in] */ FunctionID functionId,
192 /* [out] */ BOOL * pbUseCachedFunction);
193
194 HRESULT JITCachedFunctionSearchFinished(
195 /* [in] */ FunctionID functionId,
196 /* [in] */ COR_PRF_JIT_CACHE result);
197
198 HRESULT JITFunctionPitched(FunctionID functionId);
199
200 HRESULT JITInlining(
201 /* [in] */ FunctionID callerId,
202 /* [in] */ FunctionID calleeId,
203 /* [out] */ BOOL * pfShouldInline);
204
205 HRESULT ReJITCompilationStarted(
206 /* [in] */ FunctionID functionId,
207 /* [in] */ ReJITID reJitId,
208 /* [in] */ BOOL fIsSafeToBlock);
209
210 HRESULT GetReJITParameters(
211 /* [in] */ ModuleID moduleId,
212 /* [in] */ mdMethodDef methodId,
213 /* [in] */ ICorProfilerFunctionControl *
214 pFunctionControl);
215
216 HRESULT ReJITCompilationFinished(
217 /* [in] */ FunctionID functionId,
218 /* [in] */ ReJITID reJitId,
219 /* [in] */ HRESULT hrStatus,
220 /* [in] */ BOOL fIsSafeToBlock);
221
222 HRESULT ReJITError(
223 /* [in] */ ModuleID moduleId,
224 /* [in] */ mdMethodDef methodId,
225 /* [in] */ FunctionID functionId,
226 /* [in] */ HRESULT hrStatus);
227
228 //
229 // Module Events
230 //
231
232 HRESULT ModuleLoadStarted(
233 ModuleID moduleId);
234
235 HRESULT ModuleLoadFinished(
236 ModuleID moduleId,
237 HRESULT hrStatus);
238
239 HRESULT ModuleUnloadStarted(
240 ModuleID moduleId);
241
242 HRESULT ModuleUnloadFinished(
243 ModuleID moduleId,
244 HRESULT hrStatus);
245
246 HRESULT ModuleAttachedToAssembly(
247 ModuleID moduleId,
248 AssemblyID AssemblyId);
249
250 HRESULT ModuleInMemorySymbolsUpdated(
251 ModuleID moduleId);
252
253 //
254 // Class Events
255 //
256
257 HRESULT ClassLoadStarted(
258 ClassID classId);
259
260 HRESULT ClassLoadFinished(
261 ClassID classId,
262 HRESULT hrStatus);
263
264 HRESULT ClassUnloadStarted(
265 ClassID classId);
266
267 HRESULT ClassUnloadFinished(
268 ClassID classId,
269 HRESULT hrStatus);
270
271 //
272 // AppDomain Events
273 //
274
275 HRESULT AppDomainCreationStarted(
276 AppDomainID appDomainId);
277
278 HRESULT AppDomainCreationFinished(
279 AppDomainID appDomainId,
280 HRESULT hrStatus);
281
282 HRESULT AppDomainShutdownStarted(
283 AppDomainID appDomainId);
284
285 HRESULT AppDomainShutdownFinished(
286 AppDomainID appDomainId,
287 HRESULT hrStatus);
288
289 //
290 // Assembly Events
291 //
292
293 HRESULT AssemblyLoadStarted(
294 AssemblyID assemblyId);
295
296 HRESULT AssemblyLoadFinished(
297 AssemblyID assemblyId,
298 HRESULT hrStatus);
299
300 HRESULT AssemblyUnloadStarted(
301 AssemblyID assemblyId);
302
303 HRESULT AssemblyUnloadFinished(
304 AssemblyID assemblyId,
305 HRESULT hrStatus);
306
307 //
308 // Transition Events
309 //
310
311 HRESULT UnmanagedToManagedTransition(
312 FunctionID functionId,
313 COR_PRF_TRANSITION_REASON reason);
314
315 HRESULT ManagedToUnmanagedTransition(
316 FunctionID functionId,
317 COR_PRF_TRANSITION_REASON reason);
318
319 //
320 // Exception Events
321 //
322
323 HRESULT ExceptionThrown(
324 ObjectID thrownObjectId);
325
326 HRESULT ExceptionSearchFunctionEnter(
327 FunctionID functionId);
328
329 HRESULT ExceptionSearchFunctionLeave();
330
331 HRESULT ExceptionSearchFilterEnter(
332 FunctionID funcId);
333
334 HRESULT ExceptionSearchFilterLeave();
335
336 HRESULT ExceptionSearchCatcherFound(
337 FunctionID functionId);
338
339 HRESULT ExceptionOSHandlerEnter(
340 FunctionID funcId);
341
342 HRESULT ExceptionOSHandlerLeave(
343 FunctionID funcId);
344
345 HRESULT ExceptionUnwindFunctionEnter(
346 FunctionID functionId);
347
348 HRESULT ExceptionUnwindFunctionLeave();
349
350 HRESULT ExceptionUnwindFinallyEnter(
351 FunctionID functionId);
352
353 HRESULT ExceptionUnwindFinallyLeave();
354
355 HRESULT ExceptionCatcherEnter(
356 FunctionID functionId,
357 ObjectID objectId);
358
359 HRESULT ExceptionCatcherLeave();
360
361 //
362 // CCW Events
363 //
364
365 HRESULT COMClassicVTableCreated(
366 /* [in] */ ClassID wrappedClassId,
367 /* [in] */ REFGUID implementedIID,
368 /* [in] */ void * pVTable,
369 /* [in] */ ULONG cSlots);
370
371 HRESULT COMClassicVTableDestroyed(
372 /* [in] */ ClassID wrappedClassId,
373 /* [in] */ REFGUID implementedIID,
374 /* [in] */ void * pVTable);
375
376 //
377 // Remoting Events
378 //
379
380 HRESULT RemotingClientInvocationStarted();
381
382 HRESULT RemotingClientSendingMessage(GUID * pCookie,
383 BOOL fIsAsync);
384
385 HRESULT RemotingClientReceivingReply(GUID * pCookie,
386 BOOL fIsAsync);
387
388 HRESULT RemotingClientInvocationFinished();
389
390 HRESULT RemotingServerReceivingMessage(GUID * pCookie,
391 BOOL fIsAsync);
392
393 HRESULT RemotingServerInvocationStarted();
394
395 HRESULT RemotingServerInvocationReturned();
396
397 HRESULT RemotingServerSendingReply(GUID * pCookie,
398 BOOL fIsAsync);
399
400
401 //
402 // GC Events
403 //
404
405 HRESULT RuntimeSuspendStarted(COR_PRF_SUSPEND_REASON suspendReason);
406
407 HRESULT RuntimeSuspendFinished();
408
409 HRESULT RuntimeSuspendAborted();
410
411 HRESULT RuntimeResumeStarted();
412
413 HRESULT RuntimeResumeFinished();
414
415 HRESULT RuntimeThreadSuspended(ThreadID suspendedThreadId);
416
417 HRESULT RuntimeThreadResumed(ThreadID resumedThreadId);
418
419 HRESULT ObjectAllocated(
420 /* [in] */ ObjectID objectId,
421 /* [in] */ ClassID classId);
422
423 HRESULT FinalizeableObjectQueued(BOOL isCritical, ObjectID objectID);
424
425 //
426 // GC Moved References and RootReferences2 Notification Stuff
427 //
428
429 HRESULT MovedReference(BYTE * pbMemBlockStart,
430 BYTE * pbMemBlockEnd,
431 ptrdiff_t cbRelocDistance,
432 void * pHeapId,
433 BOOL fCompacting);
434
435 HRESULT EndMovedReferences(void * pHeapId);
436
437 HRESULT RootReference2(BYTE * objectId,
438 EtwGCRootKind dwEtwRootKind,
439 EtwGCRootFlags dwEtwRootFlags,
440 void * rootID,
441 void * pHeapId);
442
443 HRESULT EndRootReferences2(void * pHeapId);
444
445 HRESULT ConditionalWeakTableElementReference(BYTE * primaryObjectId,
446 BYTE * secondaryObjectId,
447 void * rootID,
448 void * pHeapId);
449
450 HRESULT EndConditionalWeakTableElementReferences(void * pHeapId);
451
452 //
453 // GC Root notification stuff
454 //
455
456 HRESULT AllocByClass(ObjectID objId, ClassID classId, void* pHeapId);
457
458 HRESULT EndAllocByClass(void * pHeapId);
459
460 //
461 // Heap walk notification stuff
462 //
463 HRESULT ObjectReference(ObjectID objId,
464 ClassID classId,
465 ULONG cNumRefs,
466 ObjectID * arrObjRef);
467
468 //
469 // GC Handle creation / destruction notifications
470 //
471 HRESULT HandleCreated(UINT_PTR handleId, ObjectID initialObjectId);
472
473 HRESULT HandleDestroyed(UINT_PTR handleId);
474
475 HRESULT GarbageCollectionStarted(int cGenerations, BOOL generationCollected[], COR_PRF_GC_REASON reason);
476
477 HRESULT GarbageCollectionFinished();
478
479 //
480 // Detach
481 //
482 HRESULT ProfilerDetachSucceeded();
483
484 BOOL HasTimedOutWaitingForConcurrentGC();
485
486 HRESULT GetAssemblyReferences(LPCWSTR wszAssemblyPath, IAssemblyBindingClosure * pClosure, AssemblyReferenceClosureWalkContextForProfAPI * pContext);
487
488private:
489
490 //
491 // Generation 0 Allocation by Class notification stuff
492 //
493
494 // This is for a hashing of ClassID values
495 struct CLASSHASHENTRY : HASHENTRY
496 {
497 ClassID m_clsId; // The class ID (also the key)
498 size_t m_count; // How many of this class have been counted
499 };
500
501 // This is a simple implementation of CHashTable to provide a very simple
502 // implementation of the Cmp pure virtual function
503 class CHashTableImpl : public CHashTable
504 {
505 public:
506 CHashTableImpl(ULONG iBuckets);
507 virtual ~CHashTableImpl();
508
509 protected:
510 virtual BOOL Cmp(SIZE_T k1, const HASHENTRY * pc2);
511 };
512
513 // This contains the data for storing allocation information
514 // in terms of numbers of objects sorted by class.
515 struct AllocByClassData
516 {
517 CHashTableImpl * pHashTable; // The hash table
518 CLASSHASHENTRY * arrHash; // Array that the hashtable uses for linking
519 ULONG cHash; // The total number of elements in arrHash
520 ULONG iHash; // Next empty entry in the hash array
521 ClassID * arrClsId; // Array of ClassIDs for the call to ObjectsAllocatedByClass
522 ULONG * arrcObjects; // Array of counts for the call to ObjectsAllocatedByClass
523 size_t cLength; // Length of the above two parallel arrays
524 };
525
526 static const UINT kcReferencesMax = 512;
527
528 struct GCReferencesData
529 {
530 size_t curIdx;
531 size_t compactingCount;
532 BYTE * arrpbMemBlockStartOld[kcReferencesMax];
533 BYTE * arrpbMemBlockStartNew[kcReferencesMax];
534 union
535 {
536 size_t arrMemBlockSize[kcReferencesMax];
537 ULONG arrULONG[kcReferencesMax];
538 BYTE * arrpbRootId[kcReferencesMax];
539 };
540 GCReferencesData * pNext;
541 };
542
543 // Since this stuff can only be performed by one thread (right now), we don't need
544 // to make this thread safe and can just have one block we reuse every time around
545 static AllocByClassData * m_pSavedAllocDataBlock;
546
547 // Pointer to the profiler's implementation of the callback interface(s).
548 // Profilers MUST support ICorProfilerCallback2.
549 // Profilers MAY optionally support ICorProfilerCallback3,4,5,6,7,8,9
550 ICorProfilerCallback2 * m_pCallback2;
551 ICorProfilerCallback3 * m_pCallback3;
552 ICorProfilerCallback4 * m_pCallback4;
553 ICorProfilerCallback5 * m_pCallback5;
554 ICorProfilerCallback6 * m_pCallback6;
555 ICorProfilerCallback7 * m_pCallback7;
556 ICorProfilerCallback8 * m_pCallback8;
557 ICorProfilerCallback9 * m_pCallback9;
558
559 HMODULE m_hmodProfilerDLL;
560
561 BOOL m_fLoadedViaAttach;
562 ProfToEEInterfaceImpl * m_pProfToEE;
563
564 // Used in EEToProfInterfaceImpl.cpp to call into the profiler (see EEFunctionIDMapper)
565 FunctionIDMapper * m_pProfilersFuncIDMapper;
566 FunctionIDMapper2 * m_pProfilersFuncIDMapper2;
567 void * m_pProfilersFuncIDMapper2ClientData;
568
569 // This is used as a cookie template for remoting calls
570 GUID m_GUID;
571
572 // This is an incrementing counter for constructing unique GUIDS from
573 // m_GUID
574 LONG m_lGUIDCount;
575
576 // This will contain a list of free ref data structs, so they
577 // don't have to be re-allocated on every GC
578 GCReferencesData * m_pGCRefDataFreeList;
579
580 // This is for managing access to the free list above.
581 CRITSEC_COOKIE m_csGCRefDataFreeList;
582
583 FunctionEnter * m_pEnter;
584 FunctionLeave * m_pLeave;
585 FunctionTailcall * m_pTailcall;
586
587 FunctionEnter2 * m_pEnter2;
588 FunctionLeave2 * m_pLeave2;
589 FunctionTailcall2 * m_pTailcall2;
590
591 BOOL m_fIsClientIDToFunctionIDMappingEnabled;
592
593 FunctionEnter3 * m_pEnter3;
594 FunctionLeave3 * m_pLeave3;
595 FunctionTailcall3 * m_pTailcall3;
596
597 FunctionEnter3WithInfo * m_pEnter3WithInfo;
598 FunctionLeave3WithInfo * m_pLeave3WithInfo;
599 FunctionTailcall3WithInfo * m_pTailcall3WithInfo;
600
601
602 // Remembers whether the profiler used SetILFunctionBody() which modifies IL in a
603 // way that cannot be reverted. This prevents a detach from succeeding.
604 BOOL m_fUnrevertiblyModifiedIL;
605
606 // Remember whether the profiler has enabled Rejit, and prevent detach if it has.
607 BOOL m_fModifiedRejitState;
608
609 GCReferencesData * AllocateMovedReferencesData();
610
611 void FreeMovedReferencesData(GCReferencesData * pData);
612
613 HRESULT MovedReferences(GCReferencesData * pData);
614
615 HRESULT RootReferences2(GCReferencesData * pData);
616
617 HRESULT ConditionalWeakTableElementReferences(GCReferencesData * pData);
618
619 HRESULT NotifyAllocByClass(AllocByClassData * pData);
620
621 HRESULT CreateProfiler(
622 const CLSID * pClsid,
623 __in_z LPCWSTR wszClsid,
624 __in_z LPCWSTR wszProfileDLL);
625
626 HRESULT DetermineAndSetEnterLeaveFunctionHooksForJit();
627
628 HRESULT STDMETHODCALLTYPE SetEnterLeaveFunctionHooksForJit(
629 FunctionEnter3 * pFuncEnter,
630 FunctionLeave3 * pFuncLeave,
631 FunctionTailcall3 * pFuncTailcall);
632
633 struct FunctionIDAndClientID
634 {
635 FunctionID functionID;
636 UINT_PTR clientID;
637 };
638
639 class FunctionIDHashTableTraits : public NoRemoveSHashTraits<DefaultSHashTraits<FunctionIDAndClientID> >
640 {
641 public:
642
643 static const COUNT_T s_minimum_allocation = 31;
644 typedef DefaultSHashTraits<FunctionIDAndClientID *>::count_t count_t;
645 typedef UINT_PTR key_t;
646
647 static key_t GetKey(FunctionIDAndClientID e)
648 {
649 LIMITED_METHOD_CONTRACT;
650 return e.functionID;
651 }
652
653 static BOOL Equals(key_t k1, key_t k2)
654 {
655 LIMITED_METHOD_CONTRACT;
656 return k1 == k2;
657 }
658
659 static count_t Hash(key_t k)
660 {
661 LIMITED_METHOD_CONTRACT;
662 return (count_t)k;
663 }
664
665 static const FunctionIDAndClientID Null()
666 {
667 LIMITED_METHOD_CONTRACT;
668 FunctionIDAndClientID functionIDAndClientID;
669 functionIDAndClientID.functionID = NULL;
670 functionIDAndClientID.clientID = NULL;
671 return functionIDAndClientID;
672 }
673
674 static bool IsNull(const FunctionIDAndClientID &functionIDAndClientID)
675 {
676 LIMITED_METHOD_CONTRACT;
677 _ASSERTE((functionIDAndClientID.functionID != NULL) || (functionIDAndClientID.clientID == NULL));
678 return functionIDAndClientID.functionID == NULL;
679 }
680 };
681
682 typedef SHash<FunctionIDHashTableTraits> FunctionIDHashTable;
683
684 // ELT3 no long keeps track of FunctionID of current managed method. Therefore, a hash table of bookkeeping
685 // the mapping from FunctionID to clientID is needed to build up ELT2 on top of ELT3. When ELT2 (slow-path
686 // or fast-path) is registered by the profiler and the profiler's IDFunctionMapper requests to hook up the
687 // function being loading, the clientID returned by FunctionIDMapper will be saved as the value to be looked
688 // up by the corresponding FunctionID in the hash table. FunctionIDs can be recycled after an app domain
689 // that contains the function bodies is unloaded so this hash table needs to replace the existing FunctionID
690 // with new FunctionID if a duplication is found in the hash table.
691 FunctionIDHashTable * m_pFunctionIDHashTable;
692
693 // Since the hash table can be read and writen concurrently, a reader-writer lock is used to synchronize
694 // all accesses to the hash table.
695 SimpleRWLock * m_pFunctionIDHashTableRWLock;
696
697 // Timeout for wait operation on concurrent GC. Only used for attach scenario
698 DWORD m_dwConcurrentGCWaitTimeoutInMs;
699
700 // Remember the fact we've timed out when waiting for concurrent GC. Will report the error later
701 BOOL m_bHasTimedOutWaitingForConcurrentGC;
702};
703
704#endif // __EETOPROFINTERFACEIMPL_H__
705