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.cpp
6//
7
8//
9// This module implements wrappers around calling the profiler's
10// ICorProfilerCallaback* interfaces. When code in the EE needs to call the
11// profiler, it goes through EEToProfInterfaceImpl to do so.
12//
13// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
14// NOTE! NOTE! NOTE! NOTE! NOTE! NOTE! NOTE! NOTE! NOTE! NOTE! NOTE! NOTE!
15//
16// PLEASE READ!
17//
18// There are strict rules for how to implement ICorProfilerCallback* wrappers. Please read
19// https://github.com/dotnet/coreclr/blob/master/Documentation/botr/profilability.md
20// to understand the rules and why they exist.
21//
22// As a reminder, here is a short summary of your responsibilities. Every PUBLIC
23// ENTRYPOINT (from EE to profiler) must have:
24//
25// - An entrypoint macro at the top. Your choices are:
26// CLR_TO_PROFILER_ENTRYPOINT (typical choice)
27// This is used for calling ICorProfilerCallback* methods that either have no
28// ThreadID parameters, or if they do have a ThreadID parameter, the parameter's
29// value is always the *current* ThreadID (i.e., param == GetThread()). This will
30// also force a mode switch to preemptive before calling the profiler.
31// CLR_TO_PROFILER_ENTRYPOINT_FOR_THREAD
32// Similar to above, except these are used for ICorProfilerCallback* methods that
33// specify a ThreadID parameter whose value may not always be the *current*
34// ThreadID. You must specify the ThreadID as the first parameter to these
35// macros. The macro will then use your ThreadID rather than that of the current
36// GetThread(), to assert that the callback is currently allowed for that
37// ThreadID (i.e., that we have not yet issued a ThreadDestroyed() for that
38// ThreadID).
39//
40// - A complete contract block with comments over every contract choice. Wherever
41// possible, use the preferred contracts (if not possible, you must comment why):
42// NOTHROW
43// All callbacks are really NOTHROW, but that's enforced partially by
44// the profiler, whose try/catch blocks aren't visible to the
45// contract system. So you'll need to put a scoped
46// PERMANENT_CONTRACT_VIOLATION(ThrowsViolation, ReasonProfilerCallout)
47// around the call to the profiler
48// GC_TRIGGERS
49// MODE_PREEMPTIVE (MODE_COOPERATIVE if passing an ObjectID)
50// If you use MODE_ANY, you must comment why you don't want an exact mode.
51// CAN_TAKE_LOCK
52// ASSERT_NO_EE_LOCKS_HELD()
53// SO_NOT_MAINLINE
54// Note that the preferred contracts in this file are DIFFERENT than the preferred
55// contracts for proftoeeinterfaceimpl.cpp.
56//
57// Private helper functions in this file do not have the same preferred contracts as
58// public entrypoints, and they should be contracted following the same guidelines
59// as per the rest of the EE.
60//
61// NOTE! NOTE! NOTE! NOTE! NOTE! NOTE! NOTE! NOTE! NOTE! NOTE! NOTE! NOTE!
62// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
63//
64
65// ======================================================================================
66
67#include "common.h"
68
69#ifdef PROFILING_SUPPORTED
70
71
72#include "eetoprofinterfaceimpl.h"
73#include "eetoprofinterfaceimpl.inl"
74#include "contract.h"
75#include "proftoeeinterfaceimpl.h"
76#include "proftoeeinterfaceimpl.inl"
77#include "profilinghelper.inl"
78#include "profdetach.h"
79#include "simplerwlock.hpp"
80#include "eeconfig.h"
81
82//---------------------------------------------------------------------------------------
83// Helpers
84
85// Bitmask of flags that may be passed to the CLR_TO_PROFILER_ENTRYPOINT* macros
86// to constrain when the callback may be issued
87enum ClrToProfEntrypointFlags
88{
89 // Default
90 kEE2PNone = 0x00000000,
91
92 // Callback is allowable even for detaching profilers
93 kEE2PAllowableWhileDetaching = 0x00000001,
94
95 // Callback is allowable even for initializing profilers
96 kEE2PAllowableWhileInitializing = 0x00000002,
97
98 // Callback is made while in a GC_NOTRIGGER contract. Whereas contracts are
99 // debug-only, this flag is used in retail builds as well.
100 kEE2PNoTrigger = 0x00000004,
101};
102
103#ifdef FEATURE_PROFAPI_ATTACH_DETACH
104#define ASSERT_EVAC_COUNTER_NONZERO() \
105 _ASSERTE((GetThreadNULLOk() == NULL) || \
106 (GetThreadNULLOk()->GetProfilerEvacuationCounter() != 0U))
107#else // FEATURE_PROFAPI_ATTACH_DETACH
108#define ASSERT_EVAC_COUNTER_NONZERO()
109#endif // FEATURE_PROFAPI_ATTACH_DETACH
110
111#define CHECK_PROFILER_STATUS(ee2pFlags) \
112 /* If one of these asserts fires, perhaps you forgot to use */ \
113 /* BEGIN/END_PIN_PROFILER */ \
114 ASSERT_EVAC_COUNTER_NONZERO(); \
115 _ASSERTE(g_profControlBlock.pProfInterface.Load() != NULL); \
116 _ASSERTE(g_profControlBlock.pProfInterface == this); \
117 /* Early abort if... */ \
118 if ( \
119 /* Profiler isn't active, */ \
120 !CORProfilerPresent() && \
121 \
122 /* and it's not the case that both a) this callback is allowed */ \
123 /* on a detaching profiler, and b) the profiler is detaching */ \
124 !( \
125 (((ee2pFlags) & kEE2PAllowableWhileDetaching) != 0) && \
126 (g_profControlBlock.curProfStatus.Get() == kProfStatusDetaching) \
127 ) && \
128 \
129 /* and it's not the case that both a) this callback is allowed */ \
130 /* on an initializing profiler, and b) the profiler is initializing */ \
131 !( \
132 (((ee2pFlags) & kEE2PAllowableWhileInitializing) != 0) && \
133 ( \
134 (g_profControlBlock.curProfStatus.Get() \
135 == kProfStatusInitializingForStartupLoad) || \
136 (g_profControlBlock.curProfStatus.Get() \
137 == kProfStatusInitializingForAttachLoad) \
138 ) \
139 ) \
140 ) \
141 { \
142 return S_OK; \
143 }
144
145// Least common denominator for the callback wrappers. Logs, removes stack
146// guard (REMOVE_STACK_GUARD_FOR_PROFILER_CALL), records in EE Thread object that
147// we're in a callback, and asserts that we're allowed to issue callbacks for the
148// specified ThreadID (i.e., no ThreadDestroyed callback has been issued for the
149// ThreadID).
150//
151#define CLR_TO_PROFILER_ENTRYPOINT_FOR_THREAD_EX(ee2pFlags, threadId, logParams) \
152 INCONTRACT(AssertTriggersContract(!((ee2pFlags) & kEE2PNoTrigger))); \
153 CHECK_PROFILER_STATUS(ee2pFlags); \
154 LOG(logParams); \
155 _ASSERTE(m_pCallback2 != NULL); \
156 REMOVE_STACK_GUARD_FOR_PROFILER_CALL; \
157 /* Normally, set COR_PRF_CALLBACKSTATE_INCALLBACK | */ \
158 /* COR_PRF_CALLBACKSTATE_IN_TRIGGERS_SCOPE in the callback state, but omit */ \
159 /* COR_PRF_CALLBACKSTATE_IN_TRIGGERS_SCOPE if we're in a GC_NOTRIGGERS callback */ \
160 SetCallbackStateFlagsHolder __csf( \
161 (((ee2pFlags) & kEE2PNoTrigger) != 0) ? \
162 COR_PRF_CALLBACKSTATE_INCALLBACK : \
163 COR_PRF_CALLBACKSTATE_INCALLBACK | COR_PRF_CALLBACKSTATE_IN_TRIGGERS_SCOPE \
164 ); \
165 _ASSERTE(ProfilerCallbacksAllowedForThread((Thread *) (threadId)))
166
167#define CLR_TO_PROFILER_ENTRYPOINT_EX(ee2pFlags, logParams) \
168 CLR_TO_PROFILER_ENTRYPOINT_FOR_THREAD_EX(ee2pFlags, GetThreadNULLOk(), logParams)
169
170// Typical entrypoint macro you'll use. Checks that we're allowed to issue
171// callbacks for the current thread (i.e., no ThreadDestroyed callback has been
172// issued for the current thread).
173#define CLR_TO_PROFILER_ENTRYPOINT(logParams) \
174 CLR_TO_PROFILER_ENTRYPOINT_EX(kEE2PNone, logParams)
175#define CLR_TO_PROFILER_ENTRYPOINT_FOR_THREAD(threadId, logParams) \
176 CLR_TO_PROFILER_ENTRYPOINT_FOR_THREAD_EX(kEE2PNone, threadId, logParams)
177
178
179//---------------------------------------------------------------------------------------
180//
181// Wrapper around Thread::ProfilerCallbacksAllowed
182//
183// Arguments:
184// pThread - Thread on which we need to determine whether callbacks are allowed
185//
186// Return Value:
187// TRUE if the profiler portion has marked this thread as allowable, else FALSE.
188//
189
190inline BOOL ProfilerCallbacksAllowedForThread(Thread * pThread)
191{
192 WRAPPER_NO_CONTRACT;
193 return ((pThread == NULL) || (pThread->ProfilerCallbacksAllowed()));
194}
195
196
197//---------------------------------------------------------------------------------------
198//
199// Wrapper around Thread::SetProfilerCallbacksAllowed
200//
201// Arguments:
202// pThread - Thread on which we're setting whether callbacks shall be allowed
203// fValue - The value to store.
204//
205
206inline void SetProfilerCallbacksAllowedForThread(Thread * pThread, BOOL fValue)
207{
208 WRAPPER_NO_CONTRACT;
209 _ASSERTE(pThread != NULL);
210 pThread->SetProfilerCallbacksAllowed(fValue);
211}
212
213
214//---------------------------------------------------------------------------------------
215//
216// Low-level function to find and CoCreateInstance the profiler's DLL. Called when
217// initializing via EEToProfInterfaceImpl::Init()
218//
219// Arguments:
220// * pClsid - [in] Profiler's CLSID
221// * wszClsid - [in] String form of CLSID or progid of profiler to load.
222// * wszProfileDLL - [in] Path to profiler DLL
223// * ppCallback - [out] Pointer to profiler's ICorProfilerCallback2 interface
224// * phmodProfilerDLL - [out] HMODULE of profiler's DLL.
225//
226// Return Value:
227// HRESULT indicating success or failure.
228//
229// Notes:
230// * This function (or one of its callees) will log an error to the event log if
231// there is a failure
232
233static HRESULT CoCreateProfiler(
234 const CLSID * pClsid,
235 __in_z LPCWSTR wszClsid,
236 __in_z LPCWSTR wszProfileDLL,
237 ICorProfilerCallback2 ** ppCallback,
238 HMODULE * phmodProfilerDLL)
239{
240 CONTRACTL
241 {
242 THROWS;
243 GC_TRIGGERS;
244 MODE_ANY;
245
246 // This causes events to be logged, which loads resource strings,
247 // which takes locks.
248 CAN_TAKE_LOCK;
249
250 SO_NOT_MAINLINE;
251 } CONTRACTL_END;
252
253 _ASSERTE(pClsid != NULL);
254 _ASSERTE(wszClsid != NULL);
255 _ASSERTE(ppCallback != NULL);
256 _ASSERTE(phmodProfilerDLL != NULL);
257
258 LOG((LF_CORPROF, LL_INFO10, "**PROF: Entered CoCreateProfiler.\n"));
259
260 HRESULT hr;
261 *phmodProfilerDLL = NULL;
262
263 // This is the ICorProfilerCallback2 ptr we get back from the profiler's class
264 // factory's CreateInstance()
265 ReleaseHolder<ICorProfilerCallback2> pCallback2FromCreateInstance;
266
267 // This is the ICorProfilerCallback2 ptr we get back from the profiler's QI (see its
268 // first use below for an explanation on why this is necessary).
269 ReleaseHolder<ICorProfilerCallback2> pCallback2FromQI;
270
271 // Create an instance of the profiler
272 hr = FakeCoCreateInstanceEx(*pClsid,
273 wszProfileDLL,
274 IID_ICorProfilerCallback2,
275 (LPVOID *) &pCallback2FromCreateInstance,
276 phmodProfilerDLL);
277
278 // (pCallback2FromCreateInstance == NULL) should be considered an error!
279 if ((pCallback2FromCreateInstance == NULL) && SUCCEEDED(hr))
280 {
281 hr = E_NOINTERFACE;
282 }
283
284 if (hr == E_NOINTERFACE)
285 {
286 // Helpful message for a potentially common problem
287 ProfilingAPIUtility::LogNoInterfaceError(IID_ICorProfilerCallback2, wszClsid);
288 }
289 else if (hr == CORPROF_E_PROFILER_CANCEL_ACTIVATION)
290 {
291 // Profiler didn't encounter a bad error, but is voluntarily choosing not to
292 // profile this runtime. Profilers that need to set system environment
293 // variables to be able to profile services may use this HRESULT to avoid
294 // profiling all the other managed apps on the box.
295 ProfilingAPIUtility::LogProfInfo(IDS_PROF_CANCEL_ACTIVATION, wszClsid);
296 }
297 else if (FAILED(hr))
298 {
299 // Catch-all error for other CoCreateInstance failures
300 ProfilingAPIUtility::LogProfError(IDS_E_PROF_CCI_FAILED, wszClsid, hr);
301 }
302
303 // Now that hr is normalized (set to error if pCallback2FromCreateInstance == NULL),
304 // LOG and abort if there was a problem.
305 if (FAILED(hr))
306 {
307 LOG((
308 LF_CORPROF,
309 LL_INFO10,
310 "**PROF: Unable to CoCreateInstance profiler class %S. hr=0x%x.\n",
311 wszClsid,
312 hr));
313 return hr;
314 }
315
316 // Redundantly QI for ICorProfilerCallback2. This keeps CLR behavior consistent
317 // with Whidbey, and works around the following bug in some profilers' class factory
318 // CreateInstance:
319 // * CreateInstance() ignores the IID it's given
320 // * CreateInstance() returns a pointer to the object it created, even though
321 // that object might not support the IID passed to CreateInstance().
322 // Whidbey CLR worked around this problem by redundantly QI'ing for the same IID
323 // again after CreateInstance() returned. In this redudant QI, the profiler code would
324 // finally realize it didn't support that IID, and return an error there. Without
325 // the redundant QI, the CLR would accept what it got from CreateInstance(), and
326 // start calling into it using the unsupported interface's vtable, which would
327 // cause an AV.
328 //
329 // There were many MSDN samples (for example
330 // http://msdn.microsoft.com/msdnmag/issues/03/01/NETProfilerAPI/) which
331 // unfortunately had this CreateInstance() bug, so many profilers might have been
332 // generated based on this code. Since it's easy & cheap to work around the
333 // problem, we do so here with the redundant QI.
334 hr = pCallback2FromCreateInstance->QueryInterface(
335 IID_ICorProfilerCallback2,
336 (LPVOID *) &pCallback2FromQI);
337
338 // (pCallback2FromQI == NULL) should be considered an error!
339 if ((pCallback2FromQI == NULL) && SUCCEEDED(hr))
340 {
341 hr = E_NOINTERFACE;
342 }
343
344 // Any error at this stage implies IID_ICorProfilerCallback2 is not supported
345 if (FAILED(hr))
346 {
347 // Helpful message for a potentially common problem
348 ProfilingAPIUtility::LogNoInterfaceError(IID_ICorProfilerCallback2, wszClsid);
349 return hr;
350 }
351
352 // Ok, safe to transfer ownership to caller's [out] param
353 *ppCallback = pCallback2FromQI.Extract();
354 pCallback2FromQI = NULL;
355
356 return S_OK;
357}
358
359
360//---------------------------------------------------------------------------------------
361//
362// Implementation of CHashTableImpl functions. This class a simple implementation of
363// CHashTable to provide a very simple implementation of the Cmp pure virtual function
364//
365
366EEToProfInterfaceImpl::CHashTableImpl::CHashTableImpl(ULONG iBuckets)
367 : CHashTable(iBuckets)
368{
369 WRAPPER_NO_CONTRACT;
370}
371
372EEToProfInterfaceImpl::CHashTableImpl::~CHashTableImpl()
373{
374 WRAPPER_NO_CONTRACT;
375}
376
377//---------------------------------------------------------------------------------------
378//
379// Comparison function for hash table of ClassIDs
380//
381// Arguments:
382// pc1 - hash key to compare
383// pc2 - hash value to compare
384//
385// Return Value:
386// TRUE if the key & value refer to the same ClassID; otherwise FALSE
387//
388
389BOOL EEToProfInterfaceImpl::CHashTableImpl::Cmp(SIZE_T k1, const HASHENTRY * pc2)
390{
391 LIMITED_METHOD_CONTRACT;
392
393 ClassID key = (ClassID) k1;
394 ClassID val = ((CLASSHASHENTRY *)pc2)->m_clsId;
395
396 return (key != val);
397}
398
399
400//---------------------------------------------------------------------------------------
401// Private maintenance functions for initialization, cleanup, etc.
402
403EEToProfInterfaceImpl::AllocByClassData *EEToProfInterfaceImpl::m_pSavedAllocDataBlock = NULL;
404
405//---------------------------------------------------------------------------------------
406//
407// EEToProfInterfaceImpl ctor just sets initial values
408//
409
410EEToProfInterfaceImpl::EEToProfInterfaceImpl() :
411 m_pCallback2(NULL),
412 m_pCallback3(NULL),
413 m_pCallback4(NULL),
414 m_pCallback5(NULL),
415 m_pCallback6(NULL),
416 m_pCallback7(NULL),
417 m_pCallback8(NULL),
418 m_pCallback9(NULL),
419 m_hmodProfilerDLL(NULL),
420 m_fLoadedViaAttach(FALSE),
421 m_pProfToEE(NULL),
422 m_pProfilersFuncIDMapper(NULL),
423 m_pProfilersFuncIDMapper2(NULL),
424 m_pProfilersFuncIDMapper2ClientData(NULL),
425 m_GUID(k_guidZero),
426 m_lGUIDCount(0),
427 m_pGCRefDataFreeList(NULL),
428 m_csGCRefDataFreeList(NULL),
429 m_pEnter(NULL),
430 m_pLeave(NULL),
431 m_pTailcall(NULL),
432 m_pEnter2(NULL),
433 m_pLeave2(NULL),
434 m_pTailcall2(NULL),
435 m_fIsClientIDToFunctionIDMappingEnabled(TRUE),
436 m_pEnter3(NULL),
437 m_pLeave3(NULL),
438 m_pTailcall3(NULL),
439 m_pEnter3WithInfo(NULL),
440 m_pLeave3WithInfo(NULL),
441 m_pTailcall3WithInfo(NULL),
442 m_fUnrevertiblyModifiedIL(FALSE),
443 m_fModifiedRejitState(FALSE),
444 m_pFunctionIDHashTable(NULL),
445 m_pFunctionIDHashTableRWLock(NULL),
446 m_dwConcurrentGCWaitTimeoutInMs(INFINITE),
447 m_bHasTimedOutWaitingForConcurrentGC(FALSE)
448{
449 // Also NULL out this static. (Note: consider making this a member variable.)
450 m_pSavedAllocDataBlock = NULL;
451 LIMITED_METHOD_CONTRACT;
452}
453
454//
455//---------------------------------------------------------------------------------------
456//
457// Post-constructor initialization of EEToProfInterfaceImpl. Sets everything up,
458// including creating the profiler.
459//
460// Parameters:
461// * pProfToEE - A newly-created ProfToEEInterfaceImpl instance that will be passed
462// to the profiler as the ICorProfilerInfo3 interface implementation.
463// * pClsid - Profiler's CLSID
464// * wszClsid - String form of CLSID or progid of profiler to load
465// * wszProfileDLL - Path to profiler DLL
466// * fLoadedViaAttach - TRUE iff the profiler is being attach-loaded (else
467// profiler is being startup-loaded)
468//
469// Return Value:
470// HRESULT indicating success or failure.
471//
472// Notes:
473// This function (or one of its callees) will log an error to the event log if there
474// is a failure
475//
476
477
478HRESULT EEToProfInterfaceImpl::Init(
479 ProfToEEInterfaceImpl * pProfToEE,
480 const CLSID * pClsid,
481 __in_z LPCWSTR wszClsid,
482 __in_z LPCWSTR wszProfileDLL,
483 BOOL fLoadedViaAttach,
484 DWORD dwConcurrentGCWaitTimeoutInMs)
485{
486 CONTRACTL
487 {
488 THROWS;
489 GC_TRIGGERS;
490 MODE_ANY;
491
492 // This causes events to be logged, which loads resource strings,
493 // which takes locks.
494 CAN_TAKE_LOCK;
495
496 MODE_PREEMPTIVE;
497 }
498 CONTRACTL_END;
499
500 HRESULT hr = E_UNEXPECTED;
501
502 _ASSERTE(pProfToEE != NULL);
503
504 m_fLoadedViaAttach = fLoadedViaAttach;
505 m_dwConcurrentGCWaitTimeoutInMs = dwConcurrentGCWaitTimeoutInMs;
506
507 // The rule sez your Crst should switch to preemptive when it's taken. We intentionally
508 // break this rule with CRST_UNSAFE_ANYMODE, because this Crst is taken DURING A GC
509 // (see AllocateMovedReferencesData(), called by MovedReference(), called by the GC),
510 // and we don't want to be switching modes in the middle of a GC! Indeed, on server there
511 // may not even be a mode in the first place.
512 CRITSEC_AllocationHolder csGCRefDataFreeList(ClrCreateCriticalSection(CrstProfilerGCRefDataFreeList, CRST_UNSAFE_ANYMODE));
513 if (csGCRefDataFreeList == NULL)
514 {
515 LOG((LF_CORPROF,
516 LL_ERROR,
517 "**PROF: Failed to create Crst during initialization.\n"));
518
519 // A specialized event log entry for this failure would be confusing and
520 // unhelpful. So just log a generic internal failure event
521 ProfilingAPIUtility::LogProfError(IDS_E_PROF_INTERNAL_INIT, wszClsid, E_FAIL);
522 return E_FAIL;
523 }
524
525 // CEEInfo::GetProfilingHandle will be PREEMPTIVE mode when trying to update
526 // m_pFunctionIDHashTable while ProfileEnter, ProfileLeave and ProfileTailcall
527 // and LookupClientIDFromCache all will be in COOPERATIVE mode when trying
528 // to read m_pFunctionIDHashTable, so pFunctionIDHashTableRWLock must be created
529 // with COOPERATIVE_OR_PREEMPTIVE. It is safe to so do because FunctionIDHashTable,
530 // synchronized by m_pFunctionIDHashTableRWLock runs only native code and uses
531 // only native heap.
532 NewHolder<SimpleRWLock> pFunctionIDHashTableRWLock(new (nothrow) SimpleRWLock(COOPERATIVE_OR_PREEMPTIVE, LOCK_TYPE_DEFAULT));
533
534 NewHolder<FunctionIDHashTable> pFunctionIDHashTable(new (nothrow) FunctionIDHashTable());
535
536 if ((pFunctionIDHashTable == NULL) || (pFunctionIDHashTableRWLock == NULL))
537 {
538 LOG((LF_CORPROF,
539 LL_ERROR,
540 "**PROF: Failed to create FunctionIDHashTable or FunctionIDHashTableRWLock during initialization.\n"));
541
542 // A specialized event log entry for this failure would be confusing and
543 // unhelpful. So just log a generic internal failure event
544 ProfilingAPIUtility::LogProfError(IDS_E_PROF_INTERNAL_INIT, wszClsid, E_OUTOFMEMORY);
545
546 return E_OUTOFMEMORY;
547 }
548
549 // This wraps the following profiler calls in a try / catch:
550 // * ClassFactory::CreateInstance
551 // * AddRef/Release/QueryInterface
552 // Although most profiler calls are not protected, these creation calls are
553 // protected here since it's cheap to do so (this is only done once per load of a
554 // profiler), and it would be nice to avoid tearing down the entire process when
555 // attaching a profiler that may pass back bogus vtables.
556 EX_TRY
557 {
558 // CoCreate the profiler (but don't call its Initialize() method yet)
559 hr = CreateProfiler(pClsid, wszClsid, wszProfileDLL);
560 }
561 EX_CATCH
562 {
563 hr = E_UNEXPECTED;
564 ProfilingAPIUtility::LogProfError(IDS_E_PROF_UNHANDLED_EXCEPTION_ON_LOAD, wszClsid);
565 }
566 // Intentionally swallowing all exceptions, as we don't want a poorly-written
567 // profiler that throws or AVs on attach to cause the entire process to go away.
568 EX_END_CATCH(SwallowAllExceptions);
569
570
571 if (FAILED(hr))
572 {
573 // CreateProfiler (or catch clause above) has already logged an event to the
574 // event log on failure
575 return hr;
576 }
577
578 m_pProfToEE = pProfToEE;
579
580 m_csGCRefDataFreeList = csGCRefDataFreeList.Extract();
581 csGCRefDataFreeList = NULL;
582
583 m_pFunctionIDHashTable = pFunctionIDHashTable.Extract();
584 pFunctionIDHashTable = NULL;
585
586 m_pFunctionIDHashTableRWLock = pFunctionIDHashTableRWLock.Extract();
587 pFunctionIDHashTableRWLock = NULL;
588
589 return S_OK;
590}
591
592
593//---------------------------------------------------------------------------------------
594//
595// This is used by Init() to load the user-specified profiler (but not to call
596// its Initialize() method).
597//
598// Arguments:
599// pClsid - Profiler's CLSID
600// wszClsid - String form of CLSID or progid of profiler to load
601// wszProfileDLL - Path to profiler DLL
602//
603// Return Value:
604// HRESULT indicating success / failure. If this is successful, m_pCallback2 will be
605// set to the profiler's ICorProfilerCallback2 interface on return. m_pCallback3,4
606// will be set to the profiler's ICorProfilerCallback3 interface on return if
607// ICorProfilerCallback3,4 is supported.
608//
609// Assumptions:
610// Although the profiler has not yet been instantiated, it is assumed that the internal
611// profiling API structures have already been created
612//
613// Notes:
614// This function (or one of its callees) will log an error to the event log
615// if there is a failure
616
617HRESULT EEToProfInterfaceImpl::CreateProfiler(
618 const CLSID * pClsid,
619 __in_z LPCWSTR wszClsid,
620 __in_z LPCWSTR wszProfileDLL)
621{
622 CONTRACTL
623 {
624 THROWS;
625 GC_TRIGGERS;
626 MODE_ANY;
627
628 // This causes events to be logged, which loads resource strings,
629 // which takes locks.
630 CAN_TAKE_LOCK;
631
632 MODE_PREEMPTIVE;
633 SO_NOT_MAINLINE;
634 }
635 CONTRACTL_END;
636
637 // Always called before Thread created.
638 _ASSERTE(GetThreadNULLOk() == NULL);
639
640 // We'll be calling into the profiler to create its ICorProfilerCallback*
641 // implementation
642 REMOVE_STACK_GUARD_FOR_PROFILER_CALL;
643
644 // Try and CoCreate the registered profiler
645 ReleaseHolder<ICorProfilerCallback2> pCallback2;
646 HModuleHolder hmodProfilerDLL;
647 HRESULT hr = CoCreateProfiler(
648 pClsid,
649 wszClsid,
650 wszProfileDLL,
651 &pCallback2,
652 &hmodProfilerDLL);
653 if (FAILED(hr))
654 {
655 // CoCreateProfiler logs events to the event log on failures
656 return hr;
657 }
658
659 // CoCreateProfiler ensures that if it succeeds, we get some valid pointers
660 _ASSERTE(pCallback2 != NULL);
661 _ASSERTE(hmodProfilerDLL != NULL);
662
663 // Save profiler pointers into this. The reference ownership now
664 // belongs to this class, so NULL out locals without allowing them to release
665 m_pCallback2 = pCallback2.Extract();
666 pCallback2 = NULL;
667 m_hmodProfilerDLL = hmodProfilerDLL.Extract();
668 hmodProfilerDLL = NULL;
669
670 // The profiler may optionally support ICorProfilerCallback3,4,5,6,7,8,9. Let's check.
671
672 ReleaseHolder<ICorProfilerCallback9> pCallback9;
673 hr = m_pCallback2->QueryInterface(
674 IID_ICorProfilerCallback9,
675 (LPVOID *)&pCallback9);
676 if (SUCCEEDED(hr) && (pCallback9 != NULL))
677 {
678 // Nifty. Transfer ownership to this class
679 _ASSERTE(m_pCallback9 == NULL);
680 m_pCallback9 = pCallback9.Extract();
681 pCallback9 = NULL;
682
683 // And while we're at it, we must now also have an ICorProfilerCallback3,4,5,6,7,8
684 // due to inheritance relationship of the interfaces
685 _ASSERTE(m_pCallback8 == NULL);
686 m_pCallback8 = static_cast<ICorProfilerCallback8 *>(m_pCallback9);
687 m_pCallback8->AddRef();
688
689 _ASSERTE(m_pCallback7 == NULL);
690 m_pCallback7 = static_cast<ICorProfilerCallback7 *>(m_pCallback8);
691 m_pCallback7->AddRef();
692
693 _ASSERTE(m_pCallback6 == NULL);
694 m_pCallback6 = static_cast<ICorProfilerCallback6 *>(m_pCallback7);
695 m_pCallback6->AddRef();
696
697 _ASSERTE(m_pCallback5 == NULL);
698 m_pCallback5 = static_cast<ICorProfilerCallback5 *>(m_pCallback6);
699 m_pCallback5->AddRef();
700
701 _ASSERTE(m_pCallback4 == NULL);
702 m_pCallback4 = static_cast<ICorProfilerCallback4 *>(m_pCallback5);
703 m_pCallback4->AddRef();
704
705 _ASSERTE(m_pCallback3 == NULL);
706 m_pCallback3 = static_cast<ICorProfilerCallback3 *>(m_pCallback4);
707 m_pCallback3->AddRef();
708 }
709
710 if (m_pCallback8 == NULL)
711 {
712 ReleaseHolder<ICorProfilerCallback8> pCallback8;
713 hr = m_pCallback2->QueryInterface(
714 IID_ICorProfilerCallback8,
715 (LPVOID *)&pCallback8);
716 if (SUCCEEDED(hr) && (pCallback8 != NULL))
717 {
718 // Nifty. Transfer ownership to this class
719 _ASSERTE(m_pCallback8 == NULL);
720 m_pCallback8 = pCallback8.Extract();
721 pCallback8 = NULL;
722
723 // And while we're at it, we must now also have an ICorProfilerCallback3,4,5,6,7
724 // due to inheritance relationship of the interfaces
725
726 _ASSERTE(m_pCallback7 == NULL);
727 m_pCallback7 = static_cast<ICorProfilerCallback7 *>(m_pCallback8);
728 m_pCallback7->AddRef();
729
730 _ASSERTE(m_pCallback6 == NULL);
731 m_pCallback6 = static_cast<ICorProfilerCallback6 *>(m_pCallback7);
732 m_pCallback6->AddRef();
733
734 _ASSERTE(m_pCallback5 == NULL);
735 m_pCallback5 = static_cast<ICorProfilerCallback5 *>(m_pCallback6);
736 m_pCallback5->AddRef();
737
738 _ASSERTE(m_pCallback4 == NULL);
739 m_pCallback4 = static_cast<ICorProfilerCallback4 *>(m_pCallback5);
740 m_pCallback4->AddRef();
741
742 _ASSERTE(m_pCallback3 == NULL);
743 m_pCallback3 = static_cast<ICorProfilerCallback3 *>(m_pCallback4);
744 m_pCallback3->AddRef();
745 }
746 }
747
748 if (m_pCallback7 == NULL)
749 {
750 ReleaseHolder<ICorProfilerCallback7> pCallback7;
751 hr = m_pCallback2->QueryInterface(
752 IID_ICorProfilerCallback7,
753 (LPVOID *)&pCallback7);
754 if (SUCCEEDED(hr) && (pCallback7 != NULL))
755 {
756 // Nifty. Transfer ownership to this class
757 _ASSERTE(m_pCallback7 == NULL);
758 m_pCallback7 = pCallback7.Extract();
759 pCallback7 = NULL;
760
761 // And while we're at it, we must now also have an ICorProfilerCallback3,4,5,6
762 // due to inheritance relationship of the interfaces
763
764 _ASSERTE(m_pCallback6 == NULL);
765 m_pCallback6 = static_cast<ICorProfilerCallback6 *>(m_pCallback7);
766 m_pCallback6->AddRef();
767
768 _ASSERTE(m_pCallback5 == NULL);
769 m_pCallback5 = static_cast<ICorProfilerCallback5 *>(m_pCallback6);
770 m_pCallback5->AddRef();
771
772 _ASSERTE(m_pCallback4 == NULL);
773 m_pCallback4 = static_cast<ICorProfilerCallback4 *>(m_pCallback5);
774 m_pCallback4->AddRef();
775
776 _ASSERTE(m_pCallback3 == NULL);
777 m_pCallback3 = static_cast<ICorProfilerCallback3 *>(m_pCallback4);
778 m_pCallback3->AddRef();
779 }
780 }
781
782 if (m_pCallback6 == NULL)
783 {
784 ReleaseHolder<ICorProfilerCallback6> pCallback6;
785 hr = m_pCallback2->QueryInterface(
786 IID_ICorProfilerCallback6,
787 (LPVOID *)&pCallback6);
788 if (SUCCEEDED(hr) && (pCallback6 != NULL))
789 {
790 // Nifty. Transfer ownership to this class
791 _ASSERTE(m_pCallback6 == NULL);
792 m_pCallback6 = pCallback6.Extract();
793 pCallback6 = NULL;
794
795 // And while we're at it, we must now also have an ICorProfilerCallback3,4,5
796 // due to inheritance relationship of the interfaces
797
798 _ASSERTE(m_pCallback5 == NULL);
799 m_pCallback5 = static_cast<ICorProfilerCallback5 *>(m_pCallback6);
800 m_pCallback5->AddRef();
801
802 _ASSERTE(m_pCallback4 == NULL);
803 m_pCallback4 = static_cast<ICorProfilerCallback4 *>(m_pCallback5);
804 m_pCallback4->AddRef();
805
806 _ASSERTE(m_pCallback3 == NULL);
807 m_pCallback3 = static_cast<ICorProfilerCallback3 *>(m_pCallback4);
808 m_pCallback3->AddRef();
809 }
810 }
811
812 if (m_pCallback5 == NULL)
813 {
814 ReleaseHolder<ICorProfilerCallback5> pCallback5;
815 hr = m_pCallback2->QueryInterface(
816 IID_ICorProfilerCallback5,
817 (LPVOID *) &pCallback5);
818 if (SUCCEEDED(hr) && (pCallback5 != NULL))
819 {
820 // Nifty. Transfer ownership to this class
821 _ASSERTE(m_pCallback5 == NULL);
822 m_pCallback5 = pCallback5.Extract();
823 pCallback5 = NULL;
824
825 // And while we're at it, we must now also have an ICorProfilerCallback3, and
826 // ICorProfilerCallback4 due to inheritance relationship of the interfaces
827 _ASSERTE(m_pCallback4 == NULL);
828 m_pCallback4 = static_cast<ICorProfilerCallback4 *>(m_pCallback5);
829 m_pCallback4->AddRef();
830
831 _ASSERTE(m_pCallback3 == NULL);
832 m_pCallback3 = static_cast<ICorProfilerCallback3 *>(m_pCallback4);
833 m_pCallback3->AddRef();
834 }
835 }
836
837 if (m_pCallback4 == NULL)
838 {
839 ReleaseHolder<ICorProfilerCallback4> pCallback4;
840 hr = m_pCallback2->QueryInterface(
841 IID_ICorProfilerCallback4,
842 (LPVOID *) &pCallback4);
843 if (SUCCEEDED(hr) && (pCallback4 != NULL))
844 {
845 // Nifty. Transfer ownership to this class
846 _ASSERTE(m_pCallback4 == NULL);
847 m_pCallback4 = pCallback4.Extract();
848 pCallback4 = NULL;
849
850 // And while we're at it, we must now also have an ICorProfilerCallback3, and
851 // due to inheritance relationship of the interfaces
852 _ASSERTE(m_pCallback3 == NULL);
853 m_pCallback3 = static_cast<ICorProfilerCallback3 *>(m_pCallback4);
854 m_pCallback3->AddRef();
855 }
856 }
857
858 if (m_pCallback3 == NULL)
859 {
860 ReleaseHolder<ICorProfilerCallback3> pCallback3;
861 hr = m_pCallback2->QueryInterface(
862 IID_ICorProfilerCallback3,
863 (LPVOID *) &pCallback3);
864 if (SUCCEEDED(hr) && (pCallback3 != NULL))
865 {
866 // Nifty. Transfer ownership to this class
867 _ASSERTE(m_pCallback3 == NULL);
868 m_pCallback3 = pCallback3.Extract();
869 pCallback3 = NULL;
870 }
871 }
872
873 return S_OK;
874}
875
876
877
878
879//---------------------------------------------------------------------------------------
880//
881// Performs cleanup for EEToProfInterfaceImpl, including releasing the profiler's
882// callback interface. Called on termination of a profiler connection.
883//
884
885EEToProfInterfaceImpl::~EEToProfInterfaceImpl()
886{
887 CONTRACTL
888 {
889 NOTHROW;
890 GC_NOTRIGGER;
891 MODE_ANY;
892
893 // When we release the profiler's callback interface
894 // below, it may well perform cleanup that takes locks.
895 // Example: profiler may release a metadata interface, which
896 // causes it to take a reader lock
897 CAN_TAKE_LOCK;
898 }
899 CONTRACTL_END;
900
901 // Make sure there's no pointer about to dangle once we disappear.
902 // FUTURE: For reattach-with-neutered-profilers feature crew, change this assert to
903 // scan through list of detaching profilers to make sure none of them give a
904 // GetEEToProfPtr() equal to this
905#ifdef FEATURE_PROFAPI_ATTACH_DETACH
906 _ASSERTE(ProfilingAPIDetach::GetEEToProfPtr() == NULL);
907#endif // FEATURE_PROFAPI_ATTACH_DETACH
908
909 // Release user-specified profiler DLL
910 // NOTE: If we're tearing down the process, then do nothing related
911 // to cleaning up the profiler DLL, as the DLL may no longer
912 // be present.
913 if (!IsAtProcessExit())
914 {
915 if (m_pCallback2 != NULL)
916 {
917 REMOVE_STACK_GUARD_FOR_PROFILER_CALL;
918 m_pCallback2->Release();
919 m_pCallback2 = NULL;
920 }
921
922 BOOL fIsV4Profiler = (m_pCallback3 != NULL);
923
924 if (fIsV4Profiler)
925 {
926 REMOVE_STACK_GUARD_FOR_PROFILER_CALL;
927 m_pCallback3->Release();
928 m_pCallback3 = NULL;
929 }
930
931 if (m_pCallback4 != NULL)
932 {
933 REMOVE_STACK_GUARD_FOR_PROFILER_CALL;
934 m_pCallback4->Release();
935 m_pCallback4 = NULL;
936 }
937
938 if (m_pCallback5 != NULL)
939 {
940 REMOVE_STACK_GUARD_FOR_PROFILER_CALL;
941 m_pCallback5->Release();
942 m_pCallback5 = NULL;
943 }
944
945 if (m_pCallback6 != NULL)
946 {
947 REMOVE_STACK_GUARD_FOR_PROFILER_CALL;
948 m_pCallback6->Release();
949 m_pCallback6 = NULL;
950 }
951
952 if (m_pCallback7 != NULL)
953 {
954 REMOVE_STACK_GUARD_FOR_PROFILER_CALL;
955 m_pCallback7->Release();
956 m_pCallback7 = NULL;
957 }
958
959 if (m_pCallback8 != NULL)
960 {
961 REMOVE_STACK_GUARD_FOR_PROFILER_CALL;
962 m_pCallback8->Release();
963 m_pCallback8 = NULL;
964 }
965
966 if (m_pCallback9 != NULL)
967 {
968 REMOVE_STACK_GUARD_FOR_PROFILER_CALL;
969 m_pCallback9->Release();
970 m_pCallback9 = NULL;
971 }
972
973 // Only unload the V4 profiler if this is not part of shutdown. This protects
974 // Whidbey profilers that aren't used to being FreeLibrary'd.
975 if (fIsV4Profiler && !g_fEEShutDown)
976 {
977 if (m_hmodProfilerDLL != NULL)
978 {
979 FreeLibrary(m_hmodProfilerDLL);
980 m_hmodProfilerDLL = NULL;
981 }
982
983 // Now that the profiler is destroyed, it is no longer referencing our
984 // ProfToEEInterfaceImpl, so it's safe to destroy that, too.
985 if (m_pProfToEE != NULL)
986 {
987 delete m_pProfToEE;
988 m_pProfToEE = NULL;
989 }
990 }
991 }
992
993 // Delete the structs associated with GC moved references
994 while (m_pGCRefDataFreeList)
995 {
996 GCReferencesData * pDel = m_pGCRefDataFreeList;
997 m_pGCRefDataFreeList = m_pGCRefDataFreeList->pNext;
998 delete pDel;
999 }
1000
1001 if (m_pSavedAllocDataBlock)
1002 {
1003#ifdef _WIN64
1004 _ASSERTE((UINT_PTR)m_pSavedAllocDataBlock != 0xFFFFFFFFFFFFFFFF);
1005#else
1006 _ASSERTE((UINT_PTR)m_pSavedAllocDataBlock != 0xFFFFFFFF);
1007#endif
1008
1009 _ASSERTE(m_pSavedAllocDataBlock->pHashTable != NULL);
1010 // Get rid of the hash table
1011 if (m_pSavedAllocDataBlock->pHashTable)
1012 delete m_pSavedAllocDataBlock->pHashTable;
1013
1014 // Get rid of the two arrays used to hold class<->numinstance info
1015 if (m_pSavedAllocDataBlock->cLength != 0)
1016 {
1017 _ASSERTE(m_pSavedAllocDataBlock->arrClsId != NULL);
1018 _ASSERTE(m_pSavedAllocDataBlock->arrcObjects != NULL);
1019
1020 delete [] m_pSavedAllocDataBlock->arrClsId;
1021 delete [] m_pSavedAllocDataBlock->arrcObjects;
1022 }
1023
1024 // Get rid of the hash array used by the hash table
1025 if (m_pSavedAllocDataBlock->arrHash)
1026 {
1027 delete [] m_pSavedAllocDataBlock->arrHash;
1028 }
1029
1030 m_pSavedAllocDataBlock = NULL;
1031 }
1032
1033 m_GUID = k_guidZero;
1034
1035 if (m_csGCRefDataFreeList != NULL)
1036 {
1037 ClrDeleteCriticalSection(m_csGCRefDataFreeList);
1038 m_csGCRefDataFreeList = NULL;
1039 }
1040
1041 if (m_pFunctionIDHashTable != NULL)
1042 {
1043 delete m_pFunctionIDHashTable;
1044 m_pFunctionIDHashTable = NULL;
1045 }
1046
1047 if (m_pFunctionIDHashTableRWLock != NULL)
1048 {
1049 delete m_pFunctionIDHashTableRWLock;
1050 m_pFunctionIDHashTableRWLock = NULL;
1051 }
1052}
1053
1054
1055
1056//---------------------------------------------------------------------------------------
1057//
1058// Initialize the GUID used for the cookie in remoting callbacks. If already
1059// initialized, this just does nothing and returns S_OK.
1060//
1061// Return Value:
1062// HRESULT indicating success or failure. If the GUID was already initialized,
1063// just returns S_OK
1064//
1065//
1066
1067HRESULT EEToProfInterfaceImpl::InitGUID()
1068{
1069 CONTRACTL
1070 {
1071 NOTHROW;
1072 GC_NOTRIGGER;
1073 CANNOT_TAKE_LOCK;
1074 ASSERT_NO_EE_LOCKS_HELD();
1075 }
1076 CONTRACTL_END;
1077
1078 if (IsEqualGUID(m_GUID, k_guidZero))
1079 {
1080 return CoCreateGuid(&m_GUID);
1081 }
1082
1083 return S_OK;
1084}
1085
1086//---------------------------------------------------------------------------------------
1087//
1088// Returns a GUID suitable for use as a remoting callback cookie for this thread.
1089// The GUID is based on the template GUID (m_GUID), the current thread, and
1090// a counter.
1091//
1092// Arguments:
1093// pGUID - [out] The GUID requested
1094//
1095
1096void EEToProfInterfaceImpl::GetGUID(GUID * pGUID)
1097{
1098 CONTRACTL
1099 {
1100 NOTHROW;
1101 GC_NOTRIGGER;
1102 ASSERT_NO_EE_LOCKS_HELD();
1103 }
1104 CONTRACTL_END;
1105
1106 // the member GUID and the argument should both be valid
1107 _ASSERTE(!(IsEqualGUID(m_GUID, k_guidZero)));
1108 _ASSERTE(pGUID);
1109
1110 // Copy the contents of the template GUID
1111 memcpy(pGUID, &m_GUID, sizeof(GUID));
1112
1113 // Adjust the last two bytes
1114 pGUID->Data4[6] = (BYTE) GetCurrentThreadId();
1115 pGUID->Data4[7] = (BYTE) InterlockedIncrement((LPLONG)&m_lGUIDCount);
1116}
1117
1118//---------------------------------------------------------------------------------------
1119//
1120// Wrapper around calling profiler's FunctionIDMapper hook. Called by JIT.
1121//
1122// Arguments:
1123// funcId - FunctionID for profiler to map
1124// pbHookFunction - [out] Specifies whether the profiler wants to hook (enter/leave)
1125// this function
1126//
1127// Return Value:
1128// The profiler-specified value that we should use to identify this function
1129// in future hooks (enter/leave).
1130// If the remapped ID returned by the profiler is NULL, we will replace it with
1131// funcId. Thus, this function will never return NULL.
1132//
1133
1134UINT_PTR EEToProfInterfaceImpl::EEFunctionIDMapper(FunctionID funcId, BOOL * pbHookFunction)
1135{
1136 // This isn't a public callback via ICorProfilerCallback*, but it's close (a
1137 // public callback via a function pointer). So we'll aim to have the preferred
1138 // contracts here.
1139 CONTRACTL
1140 {
1141 // Yay!
1142 NOTHROW;
1143
1144 // Yay!
1145 GC_TRIGGERS;
1146
1147 // Yay!
1148 MODE_PREEMPTIVE;
1149
1150 // Yay!
1151 CAN_TAKE_LOCK;
1152
1153 // ListLockEntry typically held during this callback (thanks to
1154 // MethodTable::DoRunClassInitThrowing).
1155
1156 SO_NOT_MAINLINE;
1157 }
1158 CONTRACTL_END;
1159
1160 // only called when CORProfilerFunctionIDMapperEnabled() is true,
1161 // which means either m_pProfilersFuncIDMapper or m_pProfilersFuncIDMapper2 should not be NULL;
1162 _ASSERTE((m_pProfilersFuncIDMapper != NULL) || (m_pProfilersFuncIDMapper2 != NULL));
1163
1164 UINT_PTR clientId = NULL;
1165
1166 if (m_pProfilersFuncIDMapper2 != NULL)
1167 {
1168 CLR_TO_PROFILER_ENTRYPOINT((LF_CORPROF,
1169 LL_INFO100,
1170 "**PROF: Calling profiler's FunctionIDMapper2. funcId: 0x%p. clientData: 0x%p.\n",
1171 funcId,
1172 m_pProfilersFuncIDMapper2ClientData));
1173
1174 // The attached profiler may not want to hook this function, so ask it
1175 clientId = m_pProfilersFuncIDMapper2(funcId, m_pProfilersFuncIDMapper2ClientData, pbHookFunction);
1176
1177 }
1178 else
1179 {
1180 CLR_TO_PROFILER_ENTRYPOINT((LF_CORPROF,
1181 LL_INFO100,
1182 "**PROF: Calling profiler's FunctionIDMapper. funcId: 0x%p.\n",
1183 funcId));
1184
1185 // The attached profiler may not want to hook this function, so ask it
1186 clientId = m_pProfilersFuncIDMapper(funcId, pbHookFunction);
1187 }
1188
1189 static LONG s_lIsELT2Enabled = -1;
1190 if (s_lIsELT2Enabled == -1)
1191 {
1192 LONG lEnabled = ((m_pEnter2 != NULL) ||
1193 (m_pLeave2 != NULL) ||
1194 (m_pTailcall2 != NULL));
1195
1196 InterlockedCompareExchange(&s_lIsELT2Enabled, lEnabled, -1);
1197 }
1198
1199 // We need to keep track the mapping between ClientID and FunctionID for ELT2
1200 if (s_lIsELT2Enabled != 0)
1201 {
1202 FunctionIDAndClientID functionIDAndClientID;
1203 functionIDAndClientID.functionID = funcId;
1204 functionIDAndClientID.clientID = clientId;
1205
1206 // ClientID Hash table may throw OUTOFMEMORY exception, which is not expected by the caller.
1207 EX_TRY
1208 {
1209 SimpleWriteLockHolder writeLockHolder(m_pFunctionIDHashTableRWLock);
1210 m_pFunctionIDHashTable->AddOrReplace(functionIDAndClientID);
1211 }
1212 EX_CATCH
1213 {
1214 // Running out of heap memory means we no longer can maintain the integrity of the mapping table.
1215 // All ELT2 fast-path hooks are disabled since we cannot report correct FunctionID to the
1216 // profiler at this moment.
1217 m_fIsClientIDToFunctionIDMappingEnabled = FALSE;
1218 }
1219 EX_END_CATCH(RethrowTerminalExceptions);
1220
1221 // If ELT2 is in use, FunctionID will be returned to the JIT to be embedded into the ELT3 probes
1222 // instead of using clientID because the profiler may map several functionIDs to a clientID to
1223 // do things like code coverage analysis. FunctionID to clientID has the one-on-one relationship,
1224 // while the reverse may not have this one-on-one mapping. Therefore, FunctionID is used as the
1225 // key to retrieve the corresponding clientID from the internal FunctionID hash table.
1226 return funcId;
1227 }
1228
1229 // For profilers that support ELT3, clientID will be embedded into the ELT3 probes
1230 return clientId;
1231}
1232
1233
1234//---------------------------------------------------------------------------------------
1235//
1236// Private functions called by GC so we can cache data for later notification to
1237// the profiler
1238//
1239
1240//---------------------------------------------------------------------------------------
1241//
1242// Called lazily to allocate or use a recycled GCReferencesData.
1243//
1244// Return Value:
1245// GCReferencesData * requested by caller.
1246//
1247// Notes:
1248// Uses m_csGCRefDataFreeList to find a recycleable GCReferencesData
1249// Called by GC callbacks that need to record GC references reported
1250// to the callbacks by the GC as the GC walks the heap.
1251//
1252
1253EEToProfInterfaceImpl::GCReferencesData * EEToProfInterfaceImpl::AllocateMovedReferencesData()
1254{
1255 CONTRACTL
1256 {
1257 NOTHROW;
1258 GC_NOTRIGGER;
1259 if (GetThreadNULLOk()) { MODE_COOPERATIVE; }
1260
1261 // We directly take m_csGCRefDataFreeList around accessing the free list below
1262 CAN_TAKE_LOCK;
1263
1264 // Thread store lock normally held during this call
1265 }
1266 CONTRACTL_END;
1267
1268 GCReferencesData *pData = NULL;
1269
1270 // SCOPE: Lock m_csGCRefDataFreeList for access to the free list
1271 {
1272 CRITSEC_Holder csh(m_csGCRefDataFreeList);
1273
1274 // Anything on the free list for us to grab?
1275 if (m_pGCRefDataFreeList != NULL)
1276 {
1277 // Yup, get the first element from the free list
1278 pData = m_pGCRefDataFreeList;
1279 m_pGCRefDataFreeList = m_pGCRefDataFreeList->pNext;
1280 }
1281 }
1282
1283 if (pData == NULL)
1284 {
1285 // Still not set, so the free list must not have had anything
1286 // available. Go ahead and allocate a struct directly.
1287 pData = new (nothrow) GCReferencesData;
1288 if (!pData)
1289 {
1290 return NULL;
1291 }
1292 }
1293
1294 // Now init the new block
1295 _ASSERTE(pData != NULL);
1296
1297 // Set our index to the beginning
1298 pData->curIdx = 0;
1299 pData->compactingCount = 0;
1300
1301 return pData;
1302}
1303
1304//---------------------------------------------------------------------------------------
1305//
1306// After reporting references to the profiler, this recycles the GCReferencesData
1307// that was used. See EEToProfInterfaceImpl::EndRootReferences2.
1308//
1309// Arguments:
1310// pData - Pointer to GCReferencesData to recycle
1311//
1312
1313void EEToProfInterfaceImpl::FreeMovedReferencesData(GCReferencesData * pData)
1314{
1315 CONTRACTL
1316 {
1317 NOTHROW;
1318 GC_NOTRIGGER;
1319 MODE_ANY;
1320
1321 // We directly take m_csGCRefDataFreeList around accessing the free list below
1322 CAN_TAKE_LOCK;
1323
1324 // Thread store lock normally held during this callback
1325
1326 }
1327 CONTRACTL_END;
1328
1329 // SCOPE: Lock m_csGCRefDataFreeList for access to the free list
1330 {
1331 CRITSEC_Holder csh(m_csGCRefDataFreeList);
1332 pData->pNext = m_pGCRefDataFreeList;
1333 m_pGCRefDataFreeList = pData;
1334 }
1335}
1336
1337//---------------------------------------------------------------------------------------
1338//
1339// Called by the GC to notify profapi of a moved reference. We cache the
1340// info here so we can later notify the profiler of all moved references
1341// in bulk.
1342//
1343// Arguments:
1344// pbMemBlockStart - Start of moved block
1345// pbMemBlockEnd - End of moved block
1346// cbRelocDistance - Offset from pbMemBlockStart of where the block
1347// was moved to
1348// pHeapId - GCReferencesData * used to record the block
1349// fCompacting - Is this a compacting collection?
1350//
1351// Return Value:
1352// HRESULT indicating success or failure
1353//
1354
1355HRESULT EEToProfInterfaceImpl::MovedReference(BYTE * pbMemBlockStart,
1356 BYTE * pbMemBlockEnd,
1357 ptrdiff_t cbRelocDistance,
1358 void * pHeapId,
1359 BOOL fCompacting)
1360{
1361 CONTRACTL
1362 {
1363 NOTHROW;
1364
1365 // Called during a GC
1366 GC_NOTRIGGER;
1367 if (GetThreadNULLOk()) { MODE_COOPERATIVE; }
1368
1369 // Thread store lock normally held during this callback
1370 }
1371 CONTRACTL_END;
1372
1373 _ASSERTE(pHeapId);
1374 _ASSERTE(*((size_t *)pHeapId) != (size_t)(-1));
1375
1376 // Get a pointer to the data for this heap
1377 GCReferencesData *pData = (GCReferencesData *)(*((size_t *)pHeapId));
1378
1379 // If this is the first notification of a moved reference for this heap
1380 // in this particular gc activation, then we need to get a ref data block
1381 // from the free list of blocks, or if that's empty then we need to
1382 // allocate a new one.
1383 if (pData == NULL)
1384 {
1385 pData = AllocateMovedReferencesData();
1386 if (pData == NULL)
1387 {
1388 return E_OUTOFMEMORY;
1389 }
1390
1391 // Set the cookie so that we will be provided it on subsequent
1392 // callbacks
1393 ((*((size_t *)pHeapId))) = (size_t)pData;
1394 }
1395
1396 _ASSERTE(pData->curIdx >= 0 && pData->curIdx <= kcReferencesMax);
1397
1398 // If the struct has been filled, then we need to notify the profiler of
1399 // these moved references and clear the struct for the next load of
1400 // moved references
1401 if (pData->curIdx == kcReferencesMax)
1402 {
1403 MovedReferences(pData);
1404 pData->curIdx = 0;
1405 pData->compactingCount = 0;
1406 }
1407
1408 // Now save the information in the struct
1409 pData->arrpbMemBlockStartOld[pData->curIdx] = pbMemBlockStart;
1410 pData->arrpbMemBlockStartNew[pData->curIdx] = pbMemBlockStart + cbRelocDistance;
1411 pData->arrMemBlockSize[pData->curIdx] = pbMemBlockEnd - pbMemBlockStart;
1412
1413 // Increment the index into the parallel arrays
1414 pData->curIdx += 1;
1415
1416 // Keep track of whether this is a compacting collection
1417 if (fCompacting)
1418 {
1419 pData->compactingCount += 1;
1420 // The gc is supposed to make up its mind whether this is a compacting collection or not
1421 // Thus if this one is compacting, everything so far had to say compacting
1422 _ASSERTE(pData->compactingCount == pData->curIdx);
1423 }
1424 else
1425 {
1426 // The gc is supposed to make up its mind whether this is a compacting collection or not
1427 // Thus if this one is non-compacting, everything so far had to say non-compacting
1428 _ASSERTE(pData->compactingCount == 0 && cbRelocDistance == 0);
1429 }
1430 return (S_OK);
1431}
1432
1433//---------------------------------------------------------------------------------------
1434//
1435// Called by the GC to indicate that the GC is finished calling
1436// EEToProfInterfaceImpl::MovedReference for this collection. This function will
1437// call into the profiler to notify it of all the moved references we've cached.
1438//
1439// Arguments:
1440// pHeapId - Casted to a GCReferencesData * that contains the moved reference
1441// data we've cached.
1442//
1443// Return Value:
1444// HRESULT indicating success or failure.
1445//
1446
1447HRESULT EEToProfInterfaceImpl::EndMovedReferences(void * pHeapId)
1448{
1449 CONTRACTL
1450 {
1451 NOTHROW;
1452
1453 // Called during a GC
1454 GC_NOTRIGGER;
1455 if (GetThreadNULLOk()) { MODE_COOPERATIVE; }
1456
1457 // We directly take m_csGCRefDataFreeList around accessing the free list below
1458 CAN_TAKE_LOCK;
1459
1460 // Thread store lock normally held during this callback
1461 }
1462 CONTRACTL_END;
1463
1464 _ASSERTE(pHeapId);
1465 _ASSERTE((*((size_t *)pHeapId)) != (size_t)(-1));
1466
1467 HRESULT hr = S_OK;
1468
1469 // Get a pointer to the data for this heap
1470 GCReferencesData *pData = (GCReferencesData *)(*((size_t *)pHeapId));
1471
1472 // If there were no moved references, profiler doesn't need to know
1473 if (!pData)
1474 return (S_OK);
1475
1476 // Communicate the moved references to the profiler
1477 _ASSERTE(pData->curIdx> 0);
1478 hr = MovedReferences(pData);
1479
1480 // Now we're done with the data block, we can shove it onto the free list
1481 // SCOPE: Lock m_csGCRefDataFreeList for access to the free list
1482 {
1483 CRITSEC_Holder csh(m_csGCRefDataFreeList);
1484 pData->pNext = m_pGCRefDataFreeList;
1485 m_pGCRefDataFreeList = pData;
1486 }
1487
1488#ifdef _DEBUG
1489 // Set the cookie to an invalid number
1490 (*((size_t *)pHeapId)) = (size_t)(-1);
1491#endif // _DEBUG
1492
1493 return (hr);
1494}
1495
1496
1497#define HASH_ARRAY_SIZE_INITIAL 1024
1498#define HASH_ARRAY_SIZE_INC 256
1499#define HASH_NUM_BUCKETS 32
1500#define HASH(x) ( (ULONG) ((SIZE_T)x) ) // A simple hash function
1501
1502//---------------------------------------------------------------------------------------
1503//
1504// Callback used by the GC when walking the heap (via AllocByClassHelper in
1505// ProfToEEInterfaceImpl.cpp).
1506//
1507// Arguments:
1508// objId - Object reference encountered during heap walk
1509// classId - ClassID for objID
1510// pHeapId - heap walk context used by this function; it's interpreted
1511// as an AllocByClassData * to keep track of objects on the
1512// heap by class.
1513//
1514// Return Value:
1515// HRESULT indicating whether to continue with the heap walk (i.e.,
1516// success HRESULT) or abort it (i.e., failure HRESULT).
1517//
1518
1519HRESULT EEToProfInterfaceImpl::AllocByClass(ObjectID objId, ClassID clsId, void * pHeapId)
1520{
1521 CONTRACTL
1522 {
1523 NOTHROW;
1524 GC_NOTRIGGER;
1525 SO_INTOLERANT;
1526 MODE_ANY;
1527 }
1528 CONTRACTL_END;
1529
1530#ifdef _DEBUG
1531 // This is a slight attempt to make sure that this is never called in a multi-threaded
1532 // manner. This heap walk should be done by one thread at a time only.
1533 static DWORD dwProcId = 0xFFFFFFFF;
1534#endif
1535
1536 _ASSERTE(pHeapId != NULL);
1537 _ASSERTE((*((size_t *)pHeapId)) != (size_t)(-1));
1538
1539 // The heapId they pass in is really a AllocByClassData struct ptr.
1540 AllocByClassData *pData = (AllocByClassData *)(*((size_t *)pHeapId));
1541
1542 // If it's null, need to allocate one
1543 if (pData == NULL)
1544 {
1545#ifdef _DEBUG
1546 // This is a slight attempt to make sure that this is never called in a multi-threaded
1547 // manner. This heap walk should be done by one thread at a time only.
1548 dwProcId = GetCurrentProcessId();
1549#endif
1550
1551 // See if we've saved a data block from a previous GC
1552 if (m_pSavedAllocDataBlock != NULL)
1553 pData = m_pSavedAllocDataBlock;
1554
1555 // This means we need to allocate all the memory to keep track of the info
1556 else
1557 {
1558 // Get a new alloc data block
1559 pData = new (nothrow) AllocByClassData;
1560 if (pData == NULL)
1561 return (E_OUTOFMEMORY);
1562
1563 // Create a new hash table
1564 pData->pHashTable = new (nothrow) CHashTableImpl(HASH_NUM_BUCKETS);
1565 if (!pData->pHashTable)
1566 {
1567 delete pData;
1568 return (E_OUTOFMEMORY);
1569 }
1570
1571 // Get the memory for the array that the hash table is going to use
1572 pData->arrHash = new (nothrow) CLASSHASHENTRY[HASH_ARRAY_SIZE_INITIAL];
1573 if (pData->arrHash == NULL)
1574 {
1575 delete pData->pHashTable;
1576 delete pData;
1577 return (E_OUTOFMEMORY);
1578 }
1579
1580 // Save the number of elements in the array
1581 pData->cHash = HASH_ARRAY_SIZE_INITIAL;
1582
1583 // Now initialize the hash table
1584 HRESULT hr = pData->pHashTable->NewInit((BYTE *)pData->arrHash, sizeof(CLASSHASHENTRY));
1585 if (hr == E_OUTOFMEMORY)
1586 {
1587 delete [] pData->arrHash;
1588 delete pData->pHashTable;
1589 delete pData;
1590 return (E_OUTOFMEMORY);
1591 }
1592 _ASSERTE(pData->pHashTable->IsInited());
1593
1594 // Null some entries
1595 pData->arrClsId = NULL;
1596 pData->arrcObjects = NULL;
1597 pData->cLength = 0;
1598
1599 // Hold on to the structure
1600 m_pSavedAllocDataBlock = pData;
1601 }
1602
1603 // Got some memory and hash table to store entries, yay!
1604 *((size_t *)pHeapId) = (size_t)pData;
1605
1606 // Initialize the data
1607 pData->iHash = 0;
1608 pData->pHashTable->Clear();
1609 }
1610
1611 _ASSERTE(pData->iHash <= pData->cHash);
1612 _ASSERTE(dwProcId == GetCurrentProcessId());
1613
1614 // Lookup to see if this class already has an entry
1615 CLASSHASHENTRY * pEntry =
1616 reinterpret_cast<CLASSHASHENTRY *>(pData->pHashTable->Find(HASH(clsId), (SIZE_T)clsId));
1617
1618 // If this class has already been encountered, just increment the counter.
1619 if (pEntry)
1620 pEntry->m_count++;
1621
1622 // Otherwise, need to add this one as a new entry in the hash table
1623 else
1624 {
1625 // If we're full, we need to realloc
1626 if (pData->iHash == pData->cHash)
1627 {
1628 // Try to realloc the memory
1629 CLASSHASHENTRY *tmp = new (nothrow) CLASSHASHENTRY[pData->cHash + HASH_ARRAY_SIZE_INC];
1630 if (!tmp)
1631 {
1632 return (E_OUTOFMEMORY);
1633 }
1634
1635 _ASSERTE(pData->arrHash);
1636 memcpy (tmp, pData->arrHash, pData->cHash*sizeof(CLASSHASHENTRY));
1637 delete [] pData->arrHash;
1638 pData->arrHash = tmp;
1639 // Tell the hash table that the memory location of the array has changed
1640 pData->pHashTable->SetTable((BYTE *)pData->arrHash);
1641
1642 // Save the new size of the array
1643 pData->cHash += HASH_ARRAY_SIZE_INC;
1644 }
1645
1646 // Now add the new entry
1647 CLASSHASHENTRY *pNewEntry = (CLASSHASHENTRY *) pData->pHashTable->Add(HASH(clsId), pData->iHash++);
1648
1649 pNewEntry->m_clsId = clsId;
1650 pNewEntry->m_count = 1;
1651 }
1652
1653 // Indicate success
1654 return (S_OK);
1655}
1656
1657HRESULT EEToProfInterfaceImpl::EndAllocByClass(void *pHeapId)
1658{
1659 _ASSERTE(pHeapId != NULL);
1660 _ASSERTE((*((size_t *)pHeapId)) != (size_t)(-1));
1661
1662 HRESULT hr = S_OK;
1663
1664 AllocByClassData *pData = (AllocByClassData *)(*((size_t *)pHeapId));
1665
1666 // Notify the profiler if there are elements to notify it of
1667 if (pData != NULL)
1668 hr = NotifyAllocByClass(pData);
1669
1670#ifdef _DEBUG
1671 (*((size_t *)pHeapId)) = (size_t)(-1);
1672#endif // _DEBUG
1673
1674 return (hr);
1675}
1676
1677//---------------------------------------------------------------------------------------
1678//
1679// Convert ETW-style root flag bitmask to ProfAPI-stye root flag bitmask
1680//
1681// Arguments:
1682// dwEtwRootFlags - ETW-style root flag bitmask
1683//
1684// Return Value:
1685// The corresponding ProfAPI-stye root flag bitmask
1686//
1687
1688DWORD EtwRootFlagsToProfApiRootFlags(DWORD dwEtwRootFlags)
1689{
1690 LIMITED_METHOD_CONTRACT;
1691
1692 // If a new ETW flag is added, adjust this assert, and add a case below.
1693 _ASSERTE((dwEtwRootFlags &
1694 ~(kEtwGCRootFlagsPinning | kEtwGCRootFlagsWeakRef | kEtwGCRootFlagsInterior | kEtwGCRootFlagsRefCounted))
1695 == 0);
1696
1697 DWORD dwProfApiRootFlags = 0;
1698
1699 if ((dwEtwRootFlags & kEtwGCRootFlagsPinning) != 0)
1700 {
1701 dwProfApiRootFlags |= COR_PRF_GC_ROOT_PINNING;
1702 }
1703 if ((dwEtwRootFlags & kEtwGCRootFlagsWeakRef) != 0)
1704 {
1705 dwProfApiRootFlags |= COR_PRF_GC_ROOT_WEAKREF;
1706 }
1707 if ((dwEtwRootFlags & kEtwGCRootFlagsInterior) != 0)
1708 {
1709 dwProfApiRootFlags |= COR_PRF_GC_ROOT_INTERIOR;
1710 }
1711 if ((dwEtwRootFlags & kEtwGCRootFlagsRefCounted) != 0)
1712 {
1713 dwProfApiRootFlags |= COR_PRF_GC_ROOT_REFCOUNTED;
1714 }
1715 return dwProfApiRootFlags;
1716}
1717
1718//---------------------------------------------------------------------------------------
1719//
1720// Convert ETW-style root kind enum to ProfAPI-stye root kind enum
1721//
1722// Arguments:
1723// dwEtwRootKind - ETW-style root kind enum
1724//
1725// Return Value:
1726// Corresponding ProfAPI-stye root kind enum
1727//
1728
1729DWORD EtwRootKindToProfApiRootKind(EtwGCRootKind dwEtwRootKind)
1730{
1731 LIMITED_METHOD_CONTRACT;
1732
1733 switch(dwEtwRootKind)
1734 {
1735 default:
1736 // If a new ETW root kind is added, create a profapi root kind as well, and add
1737 // the appropriate case below
1738 _ASSERTE(!"Unrecognized ETW root kind");
1739 // Deliberately fall through to kEtwGCRootKindOther
1740
1741 case kEtwGCRootKindOther:
1742 return COR_PRF_GC_ROOT_OTHER;
1743
1744 case kEtwGCRootKindStack:
1745 return COR_PRF_GC_ROOT_STACK;
1746
1747 case kEtwGCRootKindFinalizer:
1748 return COR_PRF_GC_ROOT_FINALIZER;
1749
1750 case kEtwGCRootKindHandle:
1751 return COR_PRF_GC_ROOT_HANDLE;
1752 }
1753}
1754
1755//---------------------------------------------------------------------------------------
1756//
1757// Callback used by the GC when scanning the roots (via ScanRootsHelper in
1758// ProfToEEInterfaceImpl.cpp).
1759//
1760// Arguments:
1761// objectId - Root object reference encountered
1762// dwEtwRootKind - ETW enum describing what kind of root objectId is
1763// dwEtwRootFlags - ETW flags describing the root qualities of objectId
1764// rootID - Root's methoddesc if dwEtwRootKind==kEtwGCRootKindStack, else NULL
1765// pHeapId - Used as a GCReferencesData * to keep track of the GC references
1766//
1767// Return Value:
1768// HRESULT indicating success or failure.
1769//
1770
1771HRESULT EEToProfInterfaceImpl::RootReference2(BYTE * objectId,
1772 EtwGCRootKind dwEtwRootKind,
1773 EtwGCRootFlags dwEtwRootFlags,
1774 void * rootID,
1775 void * pHeapId)
1776{
1777 _ASSERTE(pHeapId);
1778 _ASSERTE(*((size_t *)pHeapId) != (size_t)(-1));
1779
1780 LOG((LF_CORPROF, LL_INFO100000, "**PROF: Root Reference. "
1781 "ObjectID:0x%p dwEtwRootKind:0x%x dwEtwRootFlags:0x%x rootId:0x%p HeadId:0x%p\n",
1782 objectId, dwEtwRootKind, dwEtwRootFlags, rootID, pHeapId));
1783
1784 DWORD dwProfApiRootFlags = EtwRootFlagsToProfApiRootFlags(dwEtwRootFlags);
1785 DWORD dwProfApiRootKind = EtwRootKindToProfApiRootKind((EtwGCRootKind) dwEtwRootKind);
1786
1787 // Get a pointer to the data for this heap
1788 GCReferencesData *pData = (GCReferencesData *)(*((size_t *)pHeapId));
1789
1790 // If this is the first notification of an extended root reference for this heap
1791 // in this particular gc activation, then we need to get a ref data block
1792 // from the free list of blocks, or if that's empty then we need to
1793 // allocate a new one.
1794 if (pData == NULL)
1795 {
1796 pData = AllocateMovedReferencesData();
1797 if (pData == NULL)
1798 return (E_OUTOFMEMORY);
1799
1800 // Set the cookie so that we will be provided it on subsequent
1801 // callbacks
1802 ((*((size_t *)pHeapId))) = (size_t)pData;
1803 }
1804
1805 _ASSERTE(pData->curIdx >= 0 && pData->curIdx <= kcReferencesMax);
1806
1807 // If the struct has been filled, then we need to notify the profiler of
1808 // these root references and clear the struct for the next load of
1809 // root references
1810 if (pData->curIdx == kcReferencesMax)
1811 {
1812 RootReferences2(pData);
1813 pData->curIdx = 0;
1814 }
1815
1816 // Now save the information in the struct
1817 pData->arrpbMemBlockStartOld[pData->curIdx] = objectId;
1818 pData->arrpbMemBlockStartNew[pData->curIdx] = (BYTE *)rootID;
1819
1820 // assert that dwProfApiRootKind and dwProfApiRootFlags both fit in 16 bits, so we can
1821 // pack both into a 32-bit word
1822 _ASSERTE((dwProfApiRootKind & 0xffff) == dwProfApiRootKind && (dwProfApiRootFlags & 0xffff) == dwProfApiRootFlags);
1823
1824 pData->arrULONG[pData->curIdx] = (dwProfApiRootKind << 16) | dwProfApiRootFlags;
1825
1826 // Increment the index into the parallel arrays
1827 pData->curIdx += 1;
1828
1829 return S_OK;
1830}
1831
1832//---------------------------------------------------------------------------------------
1833//
1834// Called by the GC to indicate that the GC is finished calling
1835// EEToProfInterfaceImpl::RootReference2 for this collection. This function will
1836// call into the profiler to notify it of all the root references we've cached.
1837//
1838// Arguments:
1839// pHeapId - Casted to a GCReferencesData * that contains the root references
1840// we've cached.
1841//
1842// Return Value:
1843// HRESULT indicating success or failure.
1844//
1845
1846HRESULT EEToProfInterfaceImpl::EndRootReferences2(void * pHeapId)
1847{
1848 _ASSERTE(pHeapId);
1849 _ASSERTE((*((size_t *)pHeapId)) != (size_t)(-1));
1850
1851 HRESULT hr = S_OK;
1852
1853 // Get a pointer to the data for this heap
1854 GCReferencesData *pData = (GCReferencesData *)(*((size_t *)pHeapId));
1855
1856 // If there were no moved references, profiler doesn't need to know
1857 if (!pData)
1858 return (S_OK);
1859
1860 // Communicate the moved references to the profiler
1861 _ASSERTE(pData->curIdx> 0);
1862 hr = RootReferences2(pData);
1863
1864 // Now we're done with the data block, we can shove it onto the free list
1865 FreeMovedReferencesData(pData);
1866
1867#ifdef _DEBUG
1868 // Set the cookie to an invalid number
1869 (*((size_t *)pHeapId)) = (size_t)(-1);
1870#endif // _DEBUG
1871
1872 return (hr);
1873}
1874
1875//---------------------------------------------------------------------------------------
1876//
1877// Callback used by the GC when scanning the roots (via
1878// Ref_ScanDependentHandlesForProfilerAndETW in ObjectHandle.cpp).
1879//
1880// Arguments:
1881// primaryObjectId - Primary object reference in the DependentHandle
1882// secondaryObjectId - Secondary object reference in the DependentHandle
1883// rootID - The DependentHandle maintaining the dependency relationship
1884// pHeapId - Used as a GCReferencesData * to keep track of the GC references
1885//
1886// Return Value:
1887// HRESULT indicating success or failure.
1888//
1889
1890HRESULT EEToProfInterfaceImpl::ConditionalWeakTableElementReference(BYTE * primaryObjectId,
1891 BYTE * secondaryObjectId,
1892 void * rootID,
1893 void * pHeapId)
1894{
1895 _ASSERTE(pHeapId);
1896 _ASSERTE(*((size_t *)pHeapId) != (size_t)(-1));
1897
1898 // Callers must ensure the profiler asked to be notified about dependent handles,
1899 // since this is only available for profilers implementing ICorProfilerCallback5 and
1900 // greater.
1901 _ASSERTE(CORProfilerTrackConditionalWeakTableElements());
1902
1903 LOG((LF_CORPROF, LL_INFO100000, "**PROF: Root Dependent Handle. "
1904 "PrimaryObjectID:0x%p SecondaryObjectID:0x%p rootId:0x%p HeadId:0x%p\n",
1905 primaryObjectId, secondaryObjectId, rootID, pHeapId));
1906
1907 // Get a pointer to the data for this heap
1908 GCReferencesData *pData = (GCReferencesData *)(*((size_t *)pHeapId));
1909
1910 // If this is the first notification of a dependent handle reference in
1911 // this particular gc activation, then we need to get a ref data block
1912 // from the free list of blocks, or if that's empty then we need to
1913 // allocate a new one.
1914 if (pData == NULL)
1915 {
1916 pData = AllocateMovedReferencesData();
1917 if (pData == NULL)
1918 return (E_OUTOFMEMORY);
1919
1920 // Set the cookie so that we will be provided it on subsequent
1921 // callbacks
1922 ((*((size_t *)pHeapId))) = (size_t)pData;
1923 }
1924
1925 _ASSERTE(pData->curIdx >= 0 && pData->curIdx <= kcReferencesMax);
1926
1927 // If the struct has been filled, then we need to notify the profiler of
1928 // these dependent handle references and clear the struct for the next
1929 // load of dependent handle references
1930 if (pData->curIdx == kcReferencesMax)
1931 {
1932 ConditionalWeakTableElementReferences(pData);
1933 pData->curIdx = 0;
1934 }
1935
1936 // Now save the information in the struct
1937 pData->arrpbMemBlockStartOld[pData->curIdx] = primaryObjectId;
1938 pData->arrpbMemBlockStartNew[pData->curIdx] = secondaryObjectId;
1939 pData->arrpbRootId[pData->curIdx] = (BYTE*) rootID;
1940
1941 // Increment the index into the parallel arrays
1942 pData->curIdx += 1;
1943
1944 return S_OK;
1945}
1946
1947//---------------------------------------------------------------------------------------
1948//
1949// Called by the GC to indicate that the GC is finished calling
1950// EEToProfInterfaceImpl::ConditionalWeakTableElementReference for this collection. This
1951// function will call into the profiler to notify it of all the DependentHandle references
1952// we've cached.
1953//
1954// Arguments:
1955// pHeapId - Casted to a GCReferencesData * that contains the dependent handle
1956// references we've cached.
1957//
1958// Return Value:
1959// HRESULT indicating success or failure.
1960//
1961
1962HRESULT EEToProfInterfaceImpl::EndConditionalWeakTableElementReferences(void * pHeapId)
1963{
1964 _ASSERTE(pHeapId);
1965 _ASSERTE((*((size_t *)pHeapId)) != (size_t)(-1));
1966
1967 // Callers must ensure the profiler asked to be notified about dependent handles,
1968 // since this is only available for profilers implementing ICorProfilerCallback5 and
1969 // greater.
1970 _ASSERTE(CORProfilerTrackConditionalWeakTableElements());
1971
1972 HRESULT hr = S_OK;
1973
1974 // Get a pointer to the data for this heap
1975 GCReferencesData *pData = (GCReferencesData *)(*((size_t *)pHeapId));
1976
1977 // If there were no dependent handles, profiler doesn't need to know
1978 if (!pData)
1979 return (S_OK);
1980
1981 // Communicate the dependent handle references to the profiler
1982 _ASSERTE(pData->curIdx > 0);
1983 hr = ConditionalWeakTableElementReferences(pData);
1984
1985 // Now we're done with the data block, we can shove it onto the free list
1986 FreeMovedReferencesData(pData);
1987
1988#ifdef _DEBUG
1989 // Set the cookie to an invalid number
1990 (*((size_t *)pHeapId)) = (size_t)(-1);
1991#endif // _DEBUG
1992
1993 return (hr);
1994}
1995
1996
1997
1998//---------------------------------------------------------------------------------------
1999//
2000// Returns whether the profiler performed unrevertible acts, such as instrumenting
2001// code or requesting ELT hooks. RequestProfilerDetach uses this function before
2002// performing any sealing or evacuation checks to determine whether it's even possible
2003// for the profiler ever to detach.
2004//
2005// Return Value:
2006// * S_OK if it's safe to attempt a detach. Evacuation checks must still be performed
2007// before actually unloading the profiler.
2008// * else, an HRESULT error value indicating what the profiler did that made it
2009// undetachable. This is a public HRESULT suitable for returning from the
2010// RequestProfilerDetach API.
2011//
2012
2013HRESULT EEToProfInterfaceImpl::EnsureProfilerDetachable()
2014{
2015 LIMITED_METHOD_CONTRACT;
2016
2017 if (((g_profControlBlock.dwEventMask & COR_PRF_MONITOR_IMMUTABLE) != 0) ||
2018 ((g_profControlBlock.dwEventMaskHigh & COR_PRF_HIGH_MONITOR_IMMUTABLE) != 0))
2019 {
2020 LOG((
2021 LF_CORPROF,
2022 LL_ERROR,
2023 "**PROF: Profiler may not detach because it set an immutable flag. Flags = 0x%x.\n",
2024 g_profControlBlock.dwEventMask));
2025
2026 return CORPROF_E_IMMUTABLE_FLAGS_SET;
2027 }
2028
2029 if ((m_pEnter != NULL) ||
2030 (m_pLeave != NULL) ||
2031 (m_pTailcall != NULL) ||
2032 (m_pEnter2 != NULL) ||
2033 (m_pLeave2 != NULL) ||
2034 (m_pTailcall2 != NULL) ||
2035 (m_pEnter3 != NULL) ||
2036 (m_pEnter3WithInfo != NULL) ||
2037 (m_pLeave3 != NULL) ||
2038 (m_pLeave3WithInfo != NULL) ||
2039 (m_pTailcall3 != NULL) ||
2040 (m_pTailcall3WithInfo != NULL))
2041 {
2042 LOG((
2043 LF_CORPROF,
2044 LL_ERROR,
2045 "**PROF: Profiler may not detach because it set an ELT(2) hook.\n"));
2046
2047 return CORPROF_E_IRREVERSIBLE_INSTRUMENTATION_PRESENT;
2048 }
2049
2050 if (m_fUnrevertiblyModifiedIL)
2051 {
2052 LOG((
2053 LF_CORPROF,
2054 LL_ERROR,
2055 "**PROF: Profiler may not detach because it called SetILFunctionBody.\n"));
2056
2057 return CORPROF_E_IRREVERSIBLE_INSTRUMENTATION_PRESENT;
2058 }
2059
2060 if (m_fModifiedRejitState)
2061 {
2062 LOG((
2063 LF_CORPROF,
2064 LL_ERROR,
2065 "**PROF: Profiler may not detach because it enabled Rejit.\n"));
2066
2067 return CORPROF_E_IRREVERSIBLE_INSTRUMENTATION_PRESENT;
2068 }
2069
2070 return S_OK;
2071}
2072
2073// Declarations for asm wrappers of profiler callbacks
2074EXTERN_C void STDMETHODCALLTYPE ProfileEnterNaked(FunctionIDOrClientID functionIDOrClientID);
2075EXTERN_C void STDMETHODCALLTYPE ProfileLeaveNaked(FunctionIDOrClientID functionIDOrClientID);
2076EXTERN_C void STDMETHODCALLTYPE ProfileTailcallNaked(FunctionIDOrClientID functionIDOrClientID);
2077#define PROFILECALLBACK(name) name##Naked
2078
2079//---------------------------------------------------------------------------------------
2080//
2081// Determines the hooks (slow path vs. fast path) to which the JIT shall
2082// insert calls, and then tells the JIT which ones we want
2083//
2084// Return Value:
2085// HRESULT indicating success or failure
2086//
2087
2088HRESULT EEToProfInterfaceImpl::DetermineAndSetEnterLeaveFunctionHooksForJit()
2089{
2090 CONTRACTL
2091 {
2092 NOTHROW;
2093 GC_NOTRIGGER;
2094 MODE_ANY;
2095 CANNOT_TAKE_LOCK;
2096 }
2097 CONTRACTL_END;
2098
2099 // We're doing all ELT3 hooks, all-Whidbey hooks or all-Everett hooks. No mixing and matching.
2100 BOOL fCLRv4Hooks = (m_pEnter3 != NULL) ||
2101 (m_pLeave3 != NULL) ||
2102 (m_pTailcall3 != NULL) ||
2103 (m_pEnter3WithInfo != NULL) ||
2104 (m_pLeave3WithInfo != NULL) ||
2105 (m_pTailcall3WithInfo != NULL);
2106
2107 BOOL fWhidbeyHooks = (m_pEnter2 != NULL) ||
2108 (m_pLeave2 != NULL) ||
2109 (m_pTailcall2 != NULL);
2110
2111 // If no hooks were set (e.g., SetEventMask called with COR_PRF_MONITOR_ENTERLEAVE,
2112 // but SetEnterLeaveFunctionHooks(*) never called), then nothing to do
2113 if (!fCLRv4Hooks &&
2114 !fWhidbeyHooks &&
2115 (m_pEnter == NULL) &&
2116 (m_pLeave == NULL) &&
2117 (m_pTailcall == NULL))
2118 {
2119 return S_OK;
2120 }
2121
2122
2123 HRESULT hr = S_OK;
2124
2125 EX_TRY
2126 {
2127 if (fCLRv4Hooks)
2128 {
2129 // For each type of hook (enter/leave/tailcall) we must determine if we can use the
2130 // happy lucky fast path (i.e., direct call from JITd code right into the profiler's
2131 // hook or the JIT default stub (see below)), or the slow path (i.e., call into an
2132 // intermediary FCALL which then calls the profiler's hook) with extra information
2133 // about the current function.
2134
2135 hr = SetEnterLeaveFunctionHooksForJit(
2136 (m_pEnter3WithInfo != NULL) ?
2137 PROFILECALLBACK(ProfileEnter) :
2138 m_pEnter3,
2139 (m_pLeave3WithInfo != NULL) ?
2140 PROFILECALLBACK(ProfileLeave) :
2141 m_pLeave3,
2142 (m_pTailcall3WithInfo != NULL) ?
2143 PROFILECALLBACK(ProfileTailcall) :
2144 m_pTailcall3);
2145 }
2146 else
2147 {
2148 //
2149 // Everett or Whidbey hooks.
2150 //
2151
2152 // When using Everett or Whidbey hooks, the check looks like this:
2153 //
2154 // IF Hook exists
2155 // THEN Use slow path
2156 //
2157 // Why?
2158 //
2159 // - If the profiler wants the old-style Whidbey or Everett hooks, we need a wrapper
2160 // to convert from the ELT3 prototype the JIT expects to the Whidbey or Everett
2161 // prototype the profiler expects. It applies to Whidbey fast-path hooks. And due
2162 // to the overhead of looking up FunctionID from cache and using lock to synchronize
2163 // cache accesses, the so-called Whidbey fast-path hooks are much slower than they
2164 // used to be. Whidbey and Everett hooks are supported to keep existing profiler
2165 // running, but the profiler writers are encouraged to use ELT3 interface for the
2166 // best performance.
2167 //
2168 // Implicit in the above logic is if one of the hook types has no hook pointer
2169 // specified, then we pass NULL as the hook pointer to the JIT, in which case the JIT
2170 // just generates a call to the default stub (a single ret) w/out invoking the slow-path
2171 // wrapper. I call this the "fast path to nowhere"
2172
2173 BOOL fEnter = (m_pEnter != NULL) || (m_pEnter2 != NULL);
2174 BOOL fLeave = (m_pLeave != NULL) || (m_pLeave2 != NULL);
2175 BOOL fTailcall = (m_pTailcall != NULL) || (m_pTailcall2 != NULL);
2176
2177 hr = SetEnterLeaveFunctionHooksForJit(
2178 fEnter ?
2179 PROFILECALLBACK(ProfileEnter) :
2180 NULL,
2181 fLeave ?
2182 PROFILECALLBACK(ProfileLeave) :
2183 NULL,
2184 fTailcall ?
2185 PROFILECALLBACK(ProfileTailcall) :
2186 NULL);
2187 }
2188 }
2189 EX_CATCH
2190 {
2191 hr = E_FAIL;
2192 }
2193 // We need to swallow all exceptions, because we will lock otherwise (in addition to
2194 // the IA64-only lock while allocating stub space!). For example, specifying
2195 // RethrowTerminalExceptions forces us to test to see if the caught exception is
2196 // terminal and Exception::IsTerminal() can lock if we get a handle table cache miss
2197 // while getting a handle for the exception. It is good to minimize locks from
2198 // profiler Info functions (and their callees), and this is a dumb lock to have,
2199 // given that we can avoid it altogether by just having terminal exceptions be
2200 // swallowed here, and returning the failure to the profiler. For those who don't
2201 // like swallowing terminal exceptions, this is mitigated by the fact that,
2202 // currently, an exception only gets thrown from SetEnterLeaveFunctionHooksForJit on
2203 // IA64. But to keep consistent (and in case the world changes), we'll do this on
2204 // all platforms.
2205 EX_END_CATCH(SwallowAllExceptions);
2206
2207 return hr;
2208}
2209
2210
2211//---------------------------------------------------------------------------------------
2212//
2213// The Info method SetEventMask() simply defers to this function to do the real work.
2214//
2215// Arguments:
2216// dwEventMask - Event mask specified by the profiler
2217//
2218// Return Value:
2219// HRESULT indicating success / failure to return straight through to the profiler
2220//
2221
2222HRESULT EEToProfInterfaceImpl::SetEventMask(DWORD dwEventMask, DWORD dwEventMaskHigh)
2223{
2224 CONTRACTL
2225 {
2226 NOTHROW;
2227 GC_NOTRIGGER;
2228 MODE_ANY;
2229 EE_THREAD_NOT_REQUIRED;
2230 CANNOT_TAKE_LOCK;
2231 SO_NOT_MAINLINE;
2232 }
2233 CONTRACTL_END;
2234
2235 static const DWORD kEventFlagsRequiringSlowPathEnterLeaveHooks =
2236 COR_PRF_ENABLE_FUNCTION_ARGS |
2237 COR_PRF_ENABLE_FUNCTION_RETVAL |
2238 COR_PRF_ENABLE_FRAME_INFO
2239 ;
2240
2241 static const DWORD kEventFlagsAffectingEnterLeaveHooks =
2242 COR_PRF_MONITOR_ENTERLEAVE |
2243 kEventFlagsRequiringSlowPathEnterLeaveHooks
2244 ;
2245
2246 HRESULT hr;
2247
2248#ifdef _DEBUG
2249 // Some tests need to enable immutable flags after startup, when a profiler is
2250 // attached. These flags enable features that are used solely to verify the
2251 // correctness of other, MUTABLE features. Examples: enable immutable ELT to create
2252 // shadow stacks to verify stack walks (which can be done mutably via manual
2253 // EBP-frame walking), or enable immutable DSS to gather IP addresses to verify the
2254 // mutable GetFunctionFromIP.
2255 //
2256 // Similarly, test profilers may need to extend the set of flags allowable on attach
2257 // to enable features that help verify other parts of the profapi that ARE allowed
2258 // on attach.
2259 //
2260 // See code:#P2CLRRestrictionsOverview for more information
2261 DWORD dwImmutableEventFlags = COR_PRF_MONITOR_IMMUTABLE;
2262 DWORD dwAllowableAfterAttachEventFlags = COR_PRF_ALLOWABLE_AFTER_ATTACH;
2263 DWORD dwTestOnlyAllowedEventMask = 0;
2264 dwTestOnlyAllowedEventMask = CLRConfig::GetConfigValue(CLRConfig::INTERNAL_TestOnlyAllowedEventMask);
2265 if (dwTestOnlyAllowedEventMask != 0)
2266 {
2267 // Remove from the immutable flag list those flags that a test-only profiler may
2268 // need to set post-startup (specified via COMPlus_TestOnlyAllowedEventMask)
2269 dwImmutableEventFlags &= ~dwTestOnlyAllowedEventMask;
2270
2271 // And add to the "allowable after attach" list the same test-only flags.
2272 dwAllowableAfterAttachEventFlags |= dwTestOnlyAllowedEventMask;
2273
2274 LOG((LF_CORPROF, LL_INFO10, "**PROF: TestOnlyAllowedEventMask=0x%x. New immutable flags=0x%x. New AllowableAfterAttach flags=0x%x\n",
2275 dwTestOnlyAllowedEventMask,
2276 dwImmutableEventFlags,
2277 dwAllowableAfterAttachEventFlags));
2278 }
2279#endif //_DEBUG
2280
2281 // If we're not in initialization or shutdown, make sure profiler is
2282 // not trying to set an immutable attribute
2283 // FUTURE: If we add immutable flags to the high event mask, this would be a good
2284 // place to check for them as well.
2285 if (g_profControlBlock.curProfStatus.Get() != kProfStatusInitializingForStartupLoad)
2286 {
2287#ifdef _DEBUG
2288 if (((dwEventMask & dwImmutableEventFlags) !=
2289 (g_profControlBlock.dwEventMask & dwImmutableEventFlags)) ||
2290#else //!_DEBUG
2291 if (((dwEventMask & COR_PRF_MONITOR_IMMUTABLE) !=
2292 (g_profControlBlock.dwEventMask & COR_PRF_MONITOR_IMMUTABLE)) ||
2293#endif //_DEBUG
2294 ((dwEventMaskHigh & COR_PRF_HIGH_MONITOR_IMMUTABLE) !=
2295 (g_profControlBlock.dwEventMaskHigh & COR_PRF_HIGH_MONITOR_IMMUTABLE)))
2296 {
2297 // FUTURE: Should we have a dedicated HRESULT for setting immutable flag?
2298 return E_FAIL;
2299 }
2300 }
2301
2302 // If this is an attaching profiler, make sure the profiler only sets flags
2303 // allowable after an attach
2304 if (m_fLoadedViaAttach &&
2305#ifdef _DEBUG
2306 (((dwEventMask & (~dwAllowableAfterAttachEventFlags)) != 0) ||
2307#else //!_DEBUG
2308 (((dwEventMask & (~COR_PRF_ALLOWABLE_AFTER_ATTACH)) != 0) ||
2309#endif //_DEBUG
2310 (dwEventMaskHigh & (~COR_PRF_HIGH_ALLOWABLE_AFTER_ATTACH))))
2311 {
2312 return CORPROF_E_UNSUPPORTED_FOR_ATTACHING_PROFILER;
2313 }
2314
2315 // After fast path ELT hooks are set in Initial callback, the startup profiler is not allowed to change flags
2316 // that require slow path ELT hooks or disable ELT hooks.
2317 if ((g_profControlBlock.curProfStatus.Get() == kProfStatusInitializingForStartupLoad) &&
2318 (
2319 (m_pEnter3 != NULL) ||
2320 (m_pLeave3 != NULL) ||
2321 (m_pTailcall3 != NULL)
2322 ) &&
2323 (
2324 ((dwEventMask & kEventFlagsRequiringSlowPathEnterLeaveHooks) != 0) ||
2325 ((dwEventMask & COR_PRF_MONITOR_ENTERLEAVE) == 0)
2326 )
2327 )
2328 {
2329 _ASSERTE((g_profControlBlock.dwEventMask & kEventFlagsRequiringSlowPathEnterLeaveHooks) == 0);
2330 return CORPROF_E_INCONSISTENT_WITH_FLAGS;
2331 }
2332
2333 // After slow path ELT hooks are set in Initial callback, the startup profiler is not allowed to remove
2334 // all flags that require slow path ELT hooks or to change the flag to disable the ELT hooks.
2335 if ((g_profControlBlock.curProfStatus.Get() == kProfStatusInitializingForStartupLoad) &&
2336 (
2337 (m_pEnter3WithInfo != NULL) ||
2338 (m_pLeave3WithInfo != NULL) ||
2339 (m_pTailcall3WithInfo != NULL)
2340 ) &&
2341 (
2342 ((dwEventMask & kEventFlagsRequiringSlowPathEnterLeaveHooks) == 0) ||
2343 ((dwEventMask & COR_PRF_MONITOR_ENTERLEAVE) == 0)
2344 )
2345 )
2346 {
2347 _ASSERTE((g_profControlBlock.dwEventMask & kEventFlagsRequiringSlowPathEnterLeaveHooks) != 0);
2348 return CORPROF_E_INCONSISTENT_WITH_FLAGS;
2349 }
2350
2351
2352 // Note whether the caller is changing flags that affect enter leave hooks
2353 BOOL fEnterLeaveHooksAffected =
2354 // Did any of the relevant flags change?
2355 (
2356 (
2357 // Old flags
2358 ((g_profControlBlock.dwEventMask & kEventFlagsAffectingEnterLeaveHooks) ^
2359 // XORed w/ the new flags
2360 (dwEventMask & kEventFlagsAffectingEnterLeaveHooks))
2361 ) != 0
2362 ) &&
2363 // And are any enter/leave hooks set?
2364 (
2365 (m_pEnter3 != NULL) ||
2366 (m_pEnter3WithInfo != NULL) ||
2367 (m_pEnter2 != NULL) ||
2368 (m_pEnter != NULL) ||
2369 (m_pLeave3 != NULL) ||
2370 (m_pLeave3WithInfo != NULL) ||
2371 (m_pLeave2 != NULL) ||
2372 (m_pLeave != NULL) ||
2373 (m_pTailcall3 != NULL) ||
2374 (m_pTailcall3WithInfo != NULL) ||
2375 (m_pTailcall2 != NULL) ||
2376 (m_pTailcall != NULL)
2377 );
2378
2379 BOOL fNeedToTurnOffConcurrentGC = FALSE;
2380
2381 if (((dwEventMask & COR_PRF_MONITOR_GC) != 0) &&
2382 ((g_profControlBlock.dwEventMask & COR_PRF_MONITOR_GC) == 0))
2383 {
2384 // We don't need to worry about startup load as we'll turn off concurrent GC later
2385 if (g_profControlBlock.curProfStatus.Get() != kProfStatusInitializingForStartupLoad)
2386 {
2387 // Since we're not an initializing startup profiler, the EE must be fully started up
2388 // so we can check whether concurrent GC is on
2389 if (!g_fEEStarted)
2390 {
2391 return CORPROF_E_RUNTIME_UNINITIALIZED;
2392 }
2393
2394 // We don't want to change the flag before GC is fully initialized,
2395 // otherwise the concurrent GC setting would be overwritten
2396 // Make sure GC is fully initialized before proceed
2397 if (!IsGarbageCollectorFullyInitialized())
2398 {
2399 return CORPROF_E_NOT_YET_AVAILABLE;
2400 }
2401
2402 // If we are attaching and we are turning on COR_PRF_MONITOR_GC, turn off concurrent GC later
2403 // in this function
2404 if (g_profControlBlock.curProfStatus.Get() == kProfStatusInitializingForAttachLoad)
2405 {
2406 if (GCHeapUtilities::GetGCHeap()->IsConcurrentGCEnabled())
2407 {
2408 // We only allow turning off concurrent GC in the profiler attach thread inside
2409 // InitializeForAttach, otherwise we would be vulnerable to weird races such as
2410 // SetEventMask running on a separate thread and trying to turn off concurrent GC.
2411 // The best option here is to fail with CORPROF_E_CONCURRENT_GC_NOT_PROFILABLE.
2412 // Existing Dev10 profilers should be prepared to handle such case.
2413 if (IsProfilerAttachThread())
2414 {
2415 fNeedToTurnOffConcurrentGC = TRUE;
2416 }
2417 else
2418 {
2419 return CORPROF_E_CONCURRENT_GC_NOT_PROFILABLE;
2420 }
2421 }
2422 }
2423 else
2424 {
2425 // Fail if concurrent GC is enabled
2426 // This should only happen for attach profilers if user didn't turn on COR_PRF_MONITOR_GC
2427 // at attach time
2428 if (GCHeapUtilities::GetGCHeap()->IsConcurrentGCEnabled())
2429 {
2430 return CORPROF_E_CONCURRENT_GC_NOT_PROFILABLE;
2431 }
2432 }
2433 }
2434 }
2435
2436 if ((dwEventMask & COR_PRF_ENABLE_REJIT) != 0)
2437 {
2438 if ((g_profControlBlock.curProfStatus.Get() != kProfStatusInitializingForStartupLoad) && !ReJitManager::IsReJITEnabled())
2439 {
2440 return CORPROF_E_REJIT_NOT_ENABLED;
2441 }
2442
2443 g_profControlBlock.pProfInterface->SetModifiedRejitState();
2444 }
2445
2446 // High event bits
2447
2448 if (((dwEventMaskHigh & COR_PRF_HIGH_ADD_ASSEMBLY_REFERENCES) != 0) &&
2449 !IsCallback6Supported())
2450 {
2451 return CORPROF_E_CALLBACK6_REQUIRED;
2452 }
2453
2454 if (((dwEventMaskHigh & COR_PRF_HIGH_IN_MEMORY_SYMBOLS_UPDATED) != 0) &&
2455 !IsCallback7Supported())
2456 {
2457 return CORPROF_E_CALLBACK7_REQUIRED;
2458 }
2459
2460 // Now save the modified masks
2461 g_profControlBlock.dwEventMask = dwEventMask;
2462 g_profControlBlock.dwEventMaskHigh = dwEventMaskHigh;
2463
2464 if (fEnterLeaveHooksAffected)
2465 {
2466 hr = DetermineAndSetEnterLeaveFunctionHooksForJit();
2467 if (FAILED(hr))
2468 {
2469 return hr;
2470 }
2471 }
2472
2473 if (g_profControlBlock.curProfStatus.Get() == kProfStatusInitializingForStartupLoad)
2474 {
2475 // If the profiler has requested remoting cookies so that it can
2476 // track logical call stacks, then we must initialize the cookie
2477 // template.
2478 if ((g_profControlBlock.dwEventMask & COR_PRF_MONITOR_REMOTING_COOKIE)
2479 == COR_PRF_MONITOR_REMOTING_COOKIE)
2480 {
2481 hr = InitGUID();
2482 if (FAILED(hr))
2483 {
2484 return hr;
2485 }
2486 }
2487 }
2488
2489 // Turn off concurrent GC as the last step so that we don't need to turn it back on if something
2490 // else failed after that
2491 if (fNeedToTurnOffConcurrentGC)
2492 {
2493 // Turn off concurrent GC if it is on so that user can walk the heap safely in GC callbacks
2494 IGCHeap * pGCHeap = GCHeapUtilities::GetGCHeap();
2495
2496 LOG((LF_CORPROF, LL_INFO10, "**PROF: Turning off concurrent GC at attach.\n"));
2497
2498 // First turn off concurrent GC
2499 pGCHeap->TemporaryDisableConcurrentGC();
2500
2501 //
2502 // Then wait until concurrent GC to finish if concurrent GC is in progress
2503 // User can use a timeout that can be set by environment variable if the GC turns out
2504 // to be too long. The default value is INFINITE.
2505 //
2506 // NOTE:
2507 // If we don't do it in this order there might be a new concurrent GC started
2508 // before we actually turn off concurrent GC
2509 //
2510 hr = pGCHeap->WaitUntilConcurrentGCCompleteAsync(m_dwConcurrentGCWaitTimeoutInMs);
2511 if (FAILED(hr))
2512 {
2513 if (hr == HRESULT_FROM_WIN32(ERROR_TIMEOUT))
2514 {
2515 // Convert it to a more specific HRESULT
2516 hr = CORPROF_E_TIMEOUT_WAITING_FOR_CONCURRENT_GC;
2517
2518 // Since we cannot call LogProfEvent here due to contact violations, we'll need to
2519 // remember the fact that we've failed, and report the failure later after InitializeForAttach
2520 m_bHasTimedOutWaitingForConcurrentGC = TRUE;
2521 }
2522
2523 pGCHeap->TemporaryEnableConcurrentGC();
2524 return hr;
2525 }
2526
2527 // Remember that we've turned off concurrent GC and we'll turn it back on in TerminateProfiling
2528 g_profControlBlock.fConcurrentGCDisabledForAttach = TRUE;
2529
2530 LOG((LF_CORPROF, LL_INFO10, "**PROF: Concurrent GC has been turned off at attach.\n"));
2531 }
2532
2533 // Return success
2534 return S_OK;
2535}
2536
2537//---------------------------------------------------------------------------------------
2538//
2539// The Info method SetEnterLeaveFunctionHooks() simply defers to this function to do the
2540// real work.
2541//
2542// Arguments:
2543// (same as specified in the public API docs)
2544//
2545// Return Value:
2546// HRESULT indicating success / failure to return straight through to the profiler
2547//
2548
2549HRESULT EEToProfInterfaceImpl::SetEnterLeaveFunctionHooks(FunctionEnter * pFuncEnter,
2550 FunctionLeave * pFuncLeave,
2551 FunctionTailcall * pFuncTailcall)
2552{
2553 CONTRACTL
2554 {
2555 NOTHROW;
2556 GC_NOTRIGGER;
2557 MODE_ANY;
2558 EE_THREAD_NOT_REQUIRED;
2559 CANNOT_TAKE_LOCK;
2560 SO_NOT_MAINLINE;
2561 }
2562 CONTRACTL_END;
2563
2564 // You have to be setting at least one hook
2565 if ((pFuncEnter == NULL) && (pFuncLeave == NULL) && (pFuncTailcall == NULL))
2566 {
2567 return E_INVALIDARG;
2568 }
2569
2570 // ELT3 hooks beat Whidbey and Whidbey hooks beat Everett hooks. So if any ELT3 or
2571 // Whidbey hooks were set (SetEnterLeaveFunctionHooks3(WithInfo) or SetEnterLeaveFunctionHooks2),
2572 // this should be a noop
2573 if ((m_pEnter3 != NULL) ||
2574 (m_pEnter3WithInfo != NULL) ||
2575 (m_pLeave3 != NULL) ||
2576 (m_pLeave3WithInfo != NULL) ||
2577 (m_pTailcall3 != NULL) ||
2578 (m_pTailcall3WithInfo != NULL) ||
2579 (m_pEnter2 != NULL) ||
2580 (m_pLeave2 != NULL) ||
2581 (m_pTailcall2 != NULL))
2582 {
2583 return S_OK;
2584 }
2585
2586 // Always save onto the function pointers, since we won't know if the profiler
2587 // is going to tracking enter/leave until after it returns from Initialize
2588 m_pEnter = pFuncEnter;
2589 m_pLeave = pFuncLeave;
2590 m_pTailcall = pFuncTailcall;
2591
2592 return DetermineAndSetEnterLeaveFunctionHooksForJit();
2593}
2594
2595//---------------------------------------------------------------------------------------
2596//
2597// The Info method SetEnterLeaveFunctionHooks2() simply defers to this function to do the
2598// real work.
2599//
2600// Arguments:
2601// (same as specified in the public API docs)
2602//
2603// Return Value:
2604// HRESULT indicating success / failure to return straight through to the profiler
2605//
2606
2607HRESULT EEToProfInterfaceImpl::SetEnterLeaveFunctionHooks2(FunctionEnter2 * pFuncEnter,
2608 FunctionLeave2 * pFuncLeave,
2609 FunctionTailcall2 * pFuncTailcall)
2610{
2611 CONTRACTL
2612 {
2613 NOTHROW;
2614 GC_NOTRIGGER;
2615 MODE_ANY;
2616 EE_THREAD_NOT_REQUIRED;
2617 CANNOT_TAKE_LOCK;
2618 SO_NOT_MAINLINE;
2619 }
2620 CONTRACTL_END;
2621
2622 // You have to be setting at least one hook
2623 if ((pFuncEnter == NULL) && (pFuncLeave == NULL) && (pFuncTailcall == NULL))
2624 {
2625 return E_INVALIDARG;
2626 }
2627
2628 // ELT3 hooks beat Whidbey. So if any ELT3 hooks were set (SetEnterLeaveFunctionHooks3(WithInfo)),
2629 // this should be a noop
2630 if ((m_pEnter3 != NULL) ||
2631 (m_pEnter3WithInfo != NULL) ||
2632 (m_pLeave3 != NULL) ||
2633 (m_pLeave3WithInfo != NULL) ||
2634 (m_pTailcall3 != NULL) ||
2635 (m_pTailcall3WithInfo != NULL))
2636 {
2637 return S_OK;
2638 }
2639
2640 // Always save onto the function pointers, since we won't know if the profiler
2641 // is going to track enter/leave until after it returns from Initialize
2642 m_pEnter2 = pFuncEnter;
2643 m_pLeave2 = pFuncLeave;
2644 m_pTailcall2 = pFuncTailcall;
2645
2646 // Whidbey hooks override Everett hooks
2647 m_pEnter = NULL;
2648 m_pLeave = NULL;
2649 m_pTailcall = NULL;
2650
2651 return DetermineAndSetEnterLeaveFunctionHooksForJit();
2652}
2653
2654//---------------------------------------------------------------------------------------
2655//
2656// The Info method SetEnterLeaveFunctionHooks3() simply defers to this function to do the
2657// real work.
2658//
2659// Arguments:
2660// (same as specified in the public API docs)
2661//
2662// Return Value:
2663// HRESULT indicating success / failure to return straight through to the profiler
2664//
2665
2666HRESULT EEToProfInterfaceImpl::SetEnterLeaveFunctionHooks3(FunctionEnter3 * pFuncEnter3,
2667 FunctionLeave3 * pFuncLeave3,
2668 FunctionTailcall3 * pFuncTailcall3)
2669{
2670 CONTRACTL
2671 {
2672 NOTHROW;
2673 GC_NOTRIGGER;
2674 MODE_ANY;
2675 EE_THREAD_NOT_REQUIRED;
2676 CANNOT_TAKE_LOCK;
2677 SO_NOT_MAINLINE;
2678 }
2679 CONTRACTL_END;
2680
2681 // You have to be setting at least one hook
2682 if ((pFuncEnter3 == NULL) &&
2683 (pFuncLeave3 == NULL) &&
2684 (pFuncTailcall3 == NULL))
2685 {
2686 return E_INVALIDARG;
2687 }
2688
2689 if (CORProfilerELT3SlowPathEnabled())
2690 {
2691 return CORPROF_E_INCONSISTENT_WITH_FLAGS;
2692 }
2693
2694 // Always save onto the function pointers, since we won't know if the profiler
2695 // is going to track enter/leave until after it returns from Initialize
2696 m_pEnter3 = pFuncEnter3;
2697 m_pLeave3 = pFuncLeave3;
2698 m_pTailcall3 = pFuncTailcall3;
2699 m_pEnter3WithInfo = NULL;
2700 m_pLeave3WithInfo = NULL;
2701 m_pTailcall3WithInfo = NULL;
2702
2703 // ELT3 hooks override Whidbey hooks and Everett hooks.
2704 m_pEnter2 = NULL;
2705 m_pLeave2 = NULL;
2706 m_pTailcall2 = NULL;
2707 m_pEnter = NULL;
2708 m_pLeave = NULL;
2709 m_pTailcall = NULL;
2710
2711 return DetermineAndSetEnterLeaveFunctionHooksForJit();
2712}
2713
2714
2715//---------------------------------------------------------------------------------------
2716//
2717// The Info method SetEnterLeaveFunctionHooks3() simply defers to this function to do the
2718// real work.
2719//
2720// Arguments:
2721// (same as specified in the public API docs)
2722//
2723// Return Value:
2724// HRESULT indicating success / failure to return straight through to the profiler
2725//
2726
2727HRESULT EEToProfInterfaceImpl::SetEnterLeaveFunctionHooks3WithInfo(FunctionEnter3WithInfo * pFuncEnter3WithInfo,
2728 FunctionLeave3WithInfo * pFuncLeave3WithInfo,
2729 FunctionTailcall3WithInfo * pFuncTailcall3WithInfo)
2730{
2731 CONTRACTL
2732 {
2733 NOTHROW;
2734 GC_NOTRIGGER;
2735 MODE_ANY;
2736 EE_THREAD_NOT_REQUIRED;
2737 CANNOT_TAKE_LOCK;
2738 SO_NOT_MAINLINE;
2739 }
2740 CONTRACTL_END;
2741
2742 // You have to be setting at least one hook
2743 if ((pFuncEnter3WithInfo == NULL) &&
2744 (pFuncLeave3WithInfo == NULL) &&
2745 (pFuncTailcall3WithInfo == NULL))
2746 {
2747 return E_INVALIDARG;
2748 }
2749
2750 if (!CORProfilerELT3SlowPathEnabled())
2751 {
2752 return CORPROF_E_INCONSISTENT_WITH_FLAGS;
2753 }
2754
2755 // Always save onto the function pointers, since we won't know if the profiler
2756 // is going to track enter/leave until after it returns from Initialize
2757 m_pEnter3WithInfo = pFuncEnter3WithInfo;
2758 m_pLeave3WithInfo = pFuncLeave3WithInfo;
2759 m_pTailcall3WithInfo = pFuncTailcall3WithInfo;
2760 m_pEnter3 = NULL;
2761 m_pLeave3 = NULL;
2762 m_pTailcall3 = NULL;
2763
2764 // ELT3 hooks override Whidbey hooks and Everett hooks.
2765 m_pEnter2 = NULL;
2766 m_pLeave2 = NULL;
2767 m_pTailcall2 = NULL;
2768 m_pEnter = NULL;
2769 m_pLeave = NULL;
2770 m_pTailcall = NULL;
2771
2772 return DetermineAndSetEnterLeaveFunctionHooksForJit();
2773}
2774
2775
2776
2777//---------------------------------------------------------------------------------------
2778//
2779// ************************
2780// Public callback wrappers
2781// ************************
2782//
2783// NOTE: All public callback wrappers must follow the rules stated at the top
2784// of this file!
2785
2786// See corprof.idl / MSDN for detailed comments about each of these public
2787// functions, their parameters, return values, etc.
2788
2789
2790
2791//---------------------------------------------------------------------------------------
2792// INITIALIZE CALLBACKS
2793//
2794
2795HRESULT EEToProfInterfaceImpl::Initialize()
2796{
2797 CONTRACTL
2798 {
2799 // Yay!
2800 NOTHROW;
2801
2802 // Yay!
2803 GC_TRIGGERS;
2804
2805 // Yay!
2806 MODE_PREEMPTIVE;
2807
2808 // Yay!
2809 CAN_TAKE_LOCK;
2810
2811 // Yay!
2812 ASSERT_NO_EE_LOCKS_HELD();
2813
2814 SO_NOT_MAINLINE;
2815 }
2816 CONTRACTL_END;
2817
2818 CLR_TO_PROFILER_ENTRYPOINT_EX(kEE2PAllowableWhileInitializing,
2819 (LF_CORPROF,
2820 LL_INFO10,
2821 "**PROF: Calling profiler's Initialize() method.\n"));
2822
2823 _ASSERTE(m_pProfToEE != NULL);
2824
2825 // Startup initialization occurs before an EEThread object is created for this
2826 // thread.
2827 _ASSERTE(GetThreadNULLOk() == NULL);
2828
2829 {
2830 // All callbacks are really NOTHROW, but that's enforced partially by the profiler,
2831 // whose try/catch blocks aren't visible to the contract system
2832 PERMANENT_CONTRACT_VIOLATION(ThrowsViolation, ReasonProfilerCallout);
2833 return m_pCallback2->Initialize(m_pProfToEE);
2834 }
2835}
2836
2837
2838HRESULT EEToProfInterfaceImpl::InitializeForAttach(void * pvClientData, UINT cbClientData)
2839{
2840 CONTRACTL
2841 {
2842 // Yay!
2843 NOTHROW;
2844
2845 // Yay!
2846 GC_TRIGGERS;
2847
2848 // Yay!
2849 MODE_PREEMPTIVE;
2850
2851 // Yay!
2852 CAN_TAKE_LOCK;
2853
2854 // Yay!
2855 ASSERT_NO_EE_LOCKS_HELD();
2856
2857 SO_NOT_MAINLINE;
2858 }
2859 CONTRACTL_END;
2860
2861 CLR_TO_PROFILER_ENTRYPOINT_EX(kEE2PAllowableWhileInitializing,
2862 (LF_CORPROF,
2863 LL_INFO10,
2864 "**PROF: Calling profiler's InitializeForAttach() method.\n"));
2865
2866 _ASSERTE(m_pProfToEE != NULL);
2867
2868 // Attach initialization occurs on the AttachThread, which does not have an EEThread
2869 // object
2870 _ASSERTE(GetThreadNULLOk() == NULL);
2871
2872 // Should only be called on profilers that support ICorProfilerCallback3
2873 _ASSERTE(m_pCallback3 != NULL);
2874
2875 HRESULT hr = E_UNEXPECTED;
2876
2877 // This wraps the profiler's InitializeForAttach callback in a try / catch. Although
2878 // most profiler calls are not protected, this initial callback IS, since it's cheap
2879 // to do so (this is only called once per attach of a profiler), and it would be nice to
2880 // avoid tearing down the entire process when attaching a profiler that may pass back
2881 // bogus vtables.
2882 EX_TRY
2883 {
2884 hr = m_pCallback3->InitializeForAttach(m_pProfToEE, pvClientData, cbClientData);
2885 }
2886 EX_CATCH
2887 {
2888 hr = E_UNEXPECTED;
2889 }
2890 // Intentionally swallowing all exceptions, as we don't want a poorly-written
2891 // profiler that throws or AVs on attach to cause the entire process to go away.
2892 EX_END_CATCH(SwallowAllExceptions);
2893
2894 return hr;
2895}
2896
2897HRESULT EEToProfInterfaceImpl::ProfilerAttachComplete()
2898{
2899 CONTRACTL
2900 {
2901 // Yay!
2902 NOTHROW;
2903
2904 // Yay!
2905 GC_TRIGGERS;
2906
2907 // Yay!
2908 MODE_PREEMPTIVE;
2909
2910 // Yay!
2911 CAN_TAKE_LOCK;
2912
2913 // Yay!
2914 ASSERT_NO_EE_LOCKS_HELD();
2915
2916 SO_NOT_MAINLINE;
2917 }
2918 CONTRACTL_END;
2919
2920 CLR_TO_PROFILER_ENTRYPOINT((LF_CORPROF,
2921 LL_INFO10,
2922 "**PROF: Calling profiler's ProfilerAttachComplete() method.\n"));
2923
2924 // Attach initialization occurs on the AttachThread, which does not have an EEThread
2925 // object
2926 _ASSERTE(GetThreadNULLOk() == NULL);
2927
2928 // Should only be called on profilers that support ICorProfilerCallback3
2929 _ASSERTE(m_pCallback3 != NULL);
2930
2931 HRESULT hr = E_UNEXPECTED;
2932
2933 // This wraps the profiler's ProfilerAttachComplete callback in a try / catch.
2934 // Although most profiler calls are not protected, this early callback IS, since it's
2935 // cheap to do so (this is only called once per attach of a profiler), and it would be
2936 // nice to avoid tearing down the entire process when attaching a profiler that has
2937 // serious troubles initializing itself (e.g., in this case, with processing catch-up
2938 // information).
2939 EX_TRY
2940 {
2941 hr = m_pCallback3->ProfilerAttachComplete();
2942 }
2943 EX_CATCH
2944 {
2945 hr = E_UNEXPECTED;
2946 }
2947 // Intentionally swallowing all exceptions, as we don't want a poorly-written
2948 // profiler that throws or AVs on attach to cause the entire process to go away.
2949 EX_END_CATCH(SwallowAllExceptions);
2950
2951 return hr;
2952}
2953
2954
2955//---------------------------------------------------------------------------------------
2956// THREAD EVENTS
2957//
2958
2959
2960HRESULT EEToProfInterfaceImpl::ThreadCreated(ThreadID threadId)
2961{
2962 CONTRACTL
2963 {
2964 // Yay!
2965 NOTHROW;
2966
2967 // Yay!
2968 GC_TRIGGERS;
2969
2970 // Preemptive mode is particularly important here. See comment in
2971 // EEToProfInterfaceImpl::ThreadDestroyed for more information.
2972 MODE_PREEMPTIVE;
2973
2974 // Yay!
2975 CAN_TAKE_LOCK;
2976
2977 // Yay!
2978 ASSERT_NO_EE_LOCKS_HELD();
2979
2980 SO_NOT_MAINLINE;
2981 }
2982 CONTRACTL_END;
2983
2984 // Normally these callback wrappers ask IsGCSpecial() and return without calling the
2985 // profiler if true. However, ThreadCreated() is the special case where no caller
2986 // should even get this far for GC Special threads, since our callers need to know to
2987 // avoid the GCX_PREEMP around the call to this function in the first place. See
2988 // code:Thread::m_fGCSpecial
2989 _ASSERTE(!reinterpret_cast<Thread *>(threadId)->IsGCSpecial());
2990
2991 CLR_TO_PROFILER_ENTRYPOINT_FOR_THREAD(threadId,
2992 (LF_CORPROF,
2993 LL_INFO100,
2994 "**PROF: Notifying profiler of created thread. ThreadId: 0x%p.\n",
2995 threadId));
2996
2997 // Notify the profiler of the newly created thread.
2998 {
2999 // All callbacks are really NOTHROW, but that's enforced partially by the profiler,
3000 // whose try/catch blocks aren't visible to the contract system
3001 PERMANENT_CONTRACT_VIOLATION(ThrowsViolation, ReasonProfilerCallout);
3002 return m_pCallback2->ThreadCreated(threadId);
3003 }
3004}
3005
3006HRESULT EEToProfInterfaceImpl::ThreadDestroyed(ThreadID threadId)
3007{
3008 CONTRACTL
3009 {
3010 // Yay!
3011 NOTHROW;
3012
3013 // Yay!
3014 GC_TRIGGERS;
3015
3016 // See comment below
3017 MODE_PREEMPTIVE;
3018
3019 // Yay!
3020 CAN_TAKE_LOCK;
3021
3022 // Thread store lock is typically held during this callback
3023
3024 SO_NOT_MAINLINE;
3025 }
3026 CONTRACTL_END;
3027
3028 if (reinterpret_cast<Thread *>(threadId)->IsGCSpecial())
3029 return S_OK;
3030
3031 // In general, we like callbacks to switch to preemptive before calling into the
3032 // profiler. And this is particularly important to do in the ThreadCreated &
3033 // ThreadDestroyed callbacks.
3034 //
3035 // The profiler will typically block in the ThreadDestroyed callback, because
3036 // it must coordinate the use of this threadid amongst all profiler
3037 // threads. For instance, if a separate thread A is walking "this" (via DoStackSnapshot),
3038 // then the profiler must block in ThreadDestroyed until A is finished. Otherwise,
3039 // "this" will complete its destruction before A's walk is complete.
3040 //
3041 // Since the profiler will block indefinitely in ThreadDestroyed, we need
3042 // to switch to preemptive mode. Otherwise, if another thread B needs to suspend
3043 // the runtime (due to appdomain unload, GC, etc.), thread B will block
3044 // waiting for "this" (assuming we allow "this" to remain in cooperative mode),
3045 // while the profiler forces "this" to block on thread A from
3046 // the example above. And thread A may need to block on thread B, since
3047 // the stackwalking occasionally needs to switch to cooperative to access a
3048 // hash map (thus DoStackSnapshot forces the switch to cooperative up-front, before
3049 // the target thread to be walked gets suspended (yet another deadlock possibility)),
3050 // and switching to cooperative requires a wait until an in-progress GC or
3051 // EE suspension is complete. In other words, allowing "this" to remain
3052 // in cooperative mode could lead to a 3-way deadlock:
3053 // "this" waits on A
3054 // A waits on B
3055 // B waits on "this".
3056 CLR_TO_PROFILER_ENTRYPOINT_FOR_THREAD(threadId,
3057 (LF_CORPROF,
3058 LL_INFO100,
3059 "**PROF: Notifying profiler of destroyed thread. ThreadId: 0x%p.\n",
3060 threadId));
3061
3062 // From now on, issue no more callbacks for this thread
3063 SetProfilerCallbacksAllowedForThread((Thread *) threadId, FALSE);
3064
3065 // Notify the profiler of the destroyed thread
3066 {
3067 // All callbacks are really NOTHROW, but that's enforced partially by the profiler,
3068 // whose try/catch blocks aren't visible to the contract system
3069 PERMANENT_CONTRACT_VIOLATION(ThrowsViolation, ReasonProfilerCallout);
3070 return m_pCallback2->ThreadDestroyed(threadId);
3071 }
3072}
3073
3074HRESULT EEToProfInterfaceImpl::ThreadAssignedToOSThread(ThreadID managedThreadId,
3075 DWORD osThreadId)
3076{
3077 CONTRACTL
3078 {
3079 // Yay!
3080 NOTHROW;
3081
3082 // Called by notrigger Thread::DetachThread & CorHost::SwitchOutLogicalThreadState
3083 // which do look to be dangerous times to be triggering a GC
3084 GC_NOTRIGGER;
3085
3086 // This is called in notrigger zones (see above), so it's not safe to switch to preemptive
3087 MODE_ANY;
3088
3089 // Yay!
3090 CAN_TAKE_LOCK;
3091
3092 // Yay!
3093 ASSERT_NO_EE_LOCKS_HELD();
3094
3095 SO_NOT_MAINLINE;
3096 }
3097 CONTRACTL_END;
3098
3099 if (reinterpret_cast<Thread *>(managedThreadId)->IsGCSpecial())
3100 return S_OK;
3101
3102 CLR_TO_PROFILER_ENTRYPOINT_FOR_THREAD_EX(
3103 kEE2PNoTrigger,
3104 managedThreadId,
3105 (LF_CORPROF,
3106 LL_INFO100,
3107 "**PROF: Notifying profiler of thread assignment. ThreadId: 0x%p, OSThreadId: 0x%08x\n",
3108 managedThreadId,
3109 osThreadId));
3110
3111 // Notify the profiler of the thread being assigned to the OS thread
3112 {
3113 // All callbacks are really NOTHROW, but that's enforced partially by the profiler,
3114 // whose try/catch blocks aren't visible to the contract system
3115 PERMANENT_CONTRACT_VIOLATION(ThrowsViolation, ReasonProfilerCallout);
3116 return m_pCallback2->ThreadAssignedToOSThread(managedThreadId, osThreadId);
3117 }
3118}
3119
3120HRESULT EEToProfInterfaceImpl::ThreadNameChanged(ThreadID managedThreadId,
3121 ULONG cchName,
3122 __in_ecount_opt(cchName) WCHAR name[])
3123{
3124 CONTRACTL
3125 {
3126 // Yay!
3127 NOTHROW;
3128
3129 // Yay!
3130 GC_TRIGGERS;
3131
3132 // Yay!
3133 MODE_PREEMPTIVE;
3134
3135 // Yay!
3136 CAN_TAKE_LOCK;
3137
3138 // Yay!
3139 ASSERT_NO_EE_LOCKS_HELD();
3140
3141 SO_NOT_MAINLINE;
3142 }
3143 CONTRACTL_END;
3144
3145 if (reinterpret_cast<Thread *>(managedThreadId)->IsGCSpecial())
3146 return S_OK;
3147
3148 CLR_TO_PROFILER_ENTRYPOINT_FOR_THREAD(managedThreadId,
3149 (LF_CORPROF,
3150 LL_INFO100,
3151 "**PROF: Notifying profiler of thread name change.\n"));
3152
3153 {
3154 // All callbacks are really NOTHROW, but that's enforced partially by the profiler,
3155 // whose try/catch blocks aren't visible to the contract system
3156 PERMANENT_CONTRACT_VIOLATION(ThrowsViolation, ReasonProfilerCallout);
3157 return m_pCallback2->ThreadNameChanged(managedThreadId, cchName, name);
3158 }
3159}
3160
3161//---------------------------------------------------------------------------------------
3162// EE STARTUP/SHUTDOWN EVENTS
3163//
3164
3165HRESULT EEToProfInterfaceImpl::Shutdown()
3166{
3167 CONTRACTL
3168 {
3169 // Yay!
3170 NOTHROW;
3171
3172 // Yay!
3173 GC_TRIGGERS;
3174
3175 // Yay!
3176 MODE_PREEMPTIVE;
3177
3178 // Yay!
3179 CAN_TAKE_LOCK;
3180
3181 // Yay!
3182 ASSERT_NO_EE_LOCKS_HELD();
3183
3184 SO_NOT_MAINLINE;
3185 }
3186 CONTRACTL_END;
3187
3188 CLR_TO_PROFILER_ENTRYPOINT((LF_CORPROF,
3189 LL_INFO10,
3190 "**PROF: Notifying profiler that shutdown is beginning.\n"));
3191
3192 {
3193 // All callbacks are really NOTHROW, but that's enforced partially by the profiler,
3194 // whose try/catch blocks aren't visible to the contract system
3195 PERMANENT_CONTRACT_VIOLATION(ThrowsViolation, ReasonProfilerCallout);
3196 return m_pCallback2->Shutdown();
3197 }
3198}
3199
3200//---------------------------------------------------------------------------------------
3201// JIT/FUNCTION EVENTS
3202//
3203
3204HRESULT EEToProfInterfaceImpl::FunctionUnloadStarted(FunctionID functionId)
3205{
3206 _ASSERTE(!"FunctionUnloadStarted() callback no longer issued");
3207 return S_OK;
3208}
3209
3210HRESULT EEToProfInterfaceImpl::JITCompilationFinished(FunctionID functionId,
3211 HRESULT hrStatus,
3212 BOOL fIsSafeToBlock)
3213{
3214 CONTRACTL
3215 {
3216 // Yay!
3217 NOTHROW;
3218
3219 // Yay!
3220 GC_TRIGGERS;
3221
3222 // Yay!
3223 MODE_PREEMPTIVE;
3224
3225 // Yay!
3226 CAN_TAKE_LOCK;
3227
3228 // The JIT / MethodDesc code likely hold locks while this callback is made
3229
3230 SO_NOT_MAINLINE;
3231 }
3232 CONTRACTL_END;
3233
3234 CLR_TO_PROFILER_ENTRYPOINT((LF_CORPROF,
3235 LL_INFO1000,
3236 "**PROF: JITCompilationFinished 0x%p, hr=0x%08x.\n",
3237 functionId,
3238 hrStatus));
3239
3240 _ASSERTE(functionId);
3241
3242 {
3243 // All callbacks are really NOTHROW, but that's enforced partially by the profiler,
3244 // whose try/catch blocks aren't visible to the contract system
3245 PERMANENT_CONTRACT_VIOLATION(ThrowsViolation, ReasonProfilerCallout);
3246 return m_pCallback2->JITCompilationFinished(functionId, hrStatus, fIsSafeToBlock);
3247 }
3248}
3249
3250
3251HRESULT EEToProfInterfaceImpl::JITCompilationStarted(FunctionID functionId,
3252 BOOL fIsSafeToBlock)
3253{
3254 CONTRACTL
3255 {
3256 // Yay!
3257 NOTHROW;
3258
3259 // Yay!
3260 GC_TRIGGERS;
3261
3262 // Yay!
3263 MODE_PREEMPTIVE;
3264
3265 // Yay!
3266 CAN_TAKE_LOCK;
3267
3268 // The JIT / MethodDesc code likely hold locks while this callback is made
3269
3270 SO_NOT_MAINLINE;
3271 }
3272 CONTRACTL_END;
3273
3274 CLR_TO_PROFILER_ENTRYPOINT((LF_CORPROF,
3275 LL_INFO1000,
3276 "**PROF: JITCompilationStarted 0x%p.\n",
3277 functionId));
3278
3279 // Currently JITCompilationStarted is always called with fIsSafeToBlock==TRUE. If this ever changes,
3280 // it's safe to remove this assert, but this should serve as a trigger to change our
3281 // public documentation to state that this callback is no longer called in preemptive mode all the time.
3282 _ASSERTE(fIsSafeToBlock);
3283
3284 _ASSERTE(functionId);
3285
3286 {
3287 // All callbacks are really NOTHROW, but that's enforced partially by the profiler,
3288 // whose try/catch blocks aren't visible to the contract system
3289 PERMANENT_CONTRACT_VIOLATION(ThrowsViolation, ReasonProfilerCallout);
3290 return m_pCallback2->JITCompilationStarted(functionId, fIsSafeToBlock);
3291 }
3292}
3293
3294HRESULT EEToProfInterfaceImpl::DynamicMethodUnloaded(FunctionID functionId)
3295{
3296 CONTRACTL
3297 {
3298 THROWS;
3299 GC_TRIGGERS;
3300 MODE_COOPERATIVE; // RuntimeMethodHandle::Destroy (the caller) moves from QCALL to GCX_COOP
3301 CAN_TAKE_LOCK;
3302 SO_TOLERANT;
3303 }
3304 CONTRACTL_END;
3305
3306 CLR_TO_PROFILER_ENTRYPOINT((LF_CORPROF,
3307 LL_INFO1000,
3308 "**PROF: DynamicMethodUnloaded 0x%p.\n",
3309 functionId));
3310
3311 _ASSERTE(functionId);
3312
3313 if (m_pCallback9 == NULL)
3314 {
3315 return S_OK;
3316 }
3317
3318 {
3319 // All callbacks are really NOTHROW, but that's enforced partially by the profiler,
3320 // whose try/catch blocks aren't visible to the contract system
3321 PERMANENT_CONTRACT_VIOLATION(ThrowsViolation, ReasonProfilerCallout);
3322 return m_pCallback9->DynamicMethodUnloaded(functionId);
3323 }
3324}
3325
3326HRESULT EEToProfInterfaceImpl::DynamicMethodJITCompilationFinished(FunctionID functionId,
3327 HRESULT hrStatus,
3328 BOOL fIsSafeToBlock)
3329{
3330 CONTRACTL
3331 {
3332 NOTHROW;
3333 GC_TRIGGERS;
3334 MODE_PREEMPTIVE;
3335 CAN_TAKE_LOCK;
3336
3337 // The JIT / MethodDesc code likely hold locks while this callback is made
3338
3339 SO_NOT_MAINLINE;
3340 }
3341 CONTRACTL_END;
3342
3343 CLR_TO_PROFILER_ENTRYPOINT((LF_CORPROF,
3344 LL_INFO1000,
3345 "**PROF: DynamicMethodJITCompilationFinished 0x%p.\n",
3346 functionId));
3347
3348 _ASSERTE(functionId);
3349
3350 if (m_pCallback8 == NULL)
3351 {
3352 return S_OK;
3353 }
3354
3355 {
3356 // All callbacks are really NOTHROW, but that's enforced partially by the profiler,
3357 // whose try/catch blocks aren't visible to the contract system
3358 PERMANENT_CONTRACT_VIOLATION(ThrowsViolation, ReasonProfilerCallout);
3359 return m_pCallback8->DynamicMethodJITCompilationFinished(functionId, hrStatus, fIsSafeToBlock);
3360 }
3361}
3362
3363HRESULT EEToProfInterfaceImpl::DynamicMethodJITCompilationStarted(FunctionID functionId,
3364 BOOL fIsSafeToBlock,
3365 LPCBYTE pILHeader,
3366 ULONG cbILHeader)
3367{
3368 CONTRACTL
3369 {
3370 NOTHROW;
3371 GC_TRIGGERS;
3372 MODE_PREEMPTIVE;
3373 CAN_TAKE_LOCK;
3374
3375 // The JIT / MethodDesc code likely hold locks while this callback is made
3376
3377 SO_NOT_MAINLINE;
3378 }
3379 CONTRACTL_END;
3380
3381 CLR_TO_PROFILER_ENTRYPOINT((LF_CORPROF,
3382 LL_INFO1000,
3383 "**PROF: DynamicMethodJITCompilationStarted 0x%p.\n",
3384 functionId));
3385
3386 _ASSERTE(functionId);
3387
3388 // Currently DynamicMethodJITCompilationStarted is always called with fIsSafeToBlock==TRUE. If this ever changes,
3389 // it's safe to remove this assert, but this should serve as a trigger to change our
3390 // public documentation to state that this callback is no longer called in preemptive mode all the time.
3391 _ASSERTE(fIsSafeToBlock);
3392
3393 if (m_pCallback8 == NULL)
3394 {
3395 return S_OK;
3396 }
3397
3398 {
3399 // All callbacks are really NOTHROW, but that's enforced partially by the profiler,
3400 // whose try/catch blocks aren't visible to the contract system
3401 PERMANENT_CONTRACT_VIOLATION(ThrowsViolation, ReasonProfilerCallout);
3402 return m_pCallback8->DynamicMethodJITCompilationStarted(functionId, fIsSafeToBlock, pILHeader, cbILHeader);
3403 }
3404}
3405
3406HRESULT EEToProfInterfaceImpl::JITCachedFunctionSearchStarted(
3407 /* [in] */ FunctionID functionId,
3408 /* [out] */ BOOL *pbUseCachedFunction)
3409{
3410 CONTRACTL
3411 {
3412 // Yay!
3413 NOTHROW;
3414
3415 // Yay!
3416 GC_TRIGGERS;
3417
3418 // Yay!
3419 MODE_PREEMPTIVE;
3420
3421 // Yay!
3422 CAN_TAKE_LOCK;
3423
3424 // The JIT / MethodDesc code likely hold locks while this callback is made
3425
3426 SO_NOT_MAINLINE;
3427 }
3428 CONTRACTL_END;
3429
3430 CLR_TO_PROFILER_ENTRYPOINT((LF_CORPROF,
3431 LL_INFO1000,
3432 "**PROF: JITCachedFunctionSearchStarted 0x%p.\n",
3433 functionId));
3434 _ASSERTE(functionId);
3435 _ASSERTE(pbUseCachedFunction != NULL);
3436
3437 {
3438 // All callbacks are really NOTHROW, but that's enforced partially by the profiler,
3439 // whose try/catch blocks aren't visible to the contract system
3440 PERMANENT_CONTRACT_VIOLATION(ThrowsViolation, ReasonProfilerCallout);
3441 return m_pCallback2->JITCachedFunctionSearchStarted(functionId, pbUseCachedFunction);
3442 }
3443}
3444
3445HRESULT EEToProfInterfaceImpl::JITCachedFunctionSearchFinished(
3446 /* [in] */ FunctionID functionId,
3447 /* [in] */ COR_PRF_JIT_CACHE result)
3448{
3449 CONTRACTL
3450 {
3451 // Yay!
3452 NOTHROW;
3453
3454 // Yay!
3455 GC_TRIGGERS;
3456
3457 // Yay!
3458 MODE_PREEMPTIVE;
3459
3460 // Yay!
3461 CAN_TAKE_LOCK;
3462
3463 // The JIT / MethodDesc code likely hold locks while this callback is made
3464
3465 SO_NOT_MAINLINE;
3466 }
3467 CONTRACTL_END;
3468
3469 CLR_TO_PROFILER_ENTRYPOINT((LF_CORPROF,
3470 LL_INFO1000,
3471 "**PROF: JITCachedFunctionSearchFinished 0x%p, %s.\n",
3472 functionId,
3473 (result == COR_PRF_CACHED_FUNCTION_FOUND ?
3474 "Cached function found" :
3475 "Cached function not found")));
3476
3477 _ASSERTE(functionId);
3478
3479 {
3480 // All callbacks are really NOTHROW, but that's enforced partially by the profiler,
3481 // whose try/catch blocks aren't visible to the contract system
3482 PERMANENT_CONTRACT_VIOLATION(ThrowsViolation, ReasonProfilerCallout);
3483 return m_pCallback2->JITCachedFunctionSearchFinished(functionId, result);
3484 }
3485}
3486
3487
3488HRESULT EEToProfInterfaceImpl::JITFunctionPitched(FunctionID functionId)
3489{
3490 _ASSERTE(!"JITFunctionPitched() callback no longer issued");
3491 return S_OK;
3492}
3493
3494HRESULT EEToProfInterfaceImpl::JITInlining(
3495 /* [in] */ FunctionID callerId,
3496 /* [in] */ FunctionID calleeId,
3497 /* [out] */ BOOL * pfShouldInline)
3498{
3499 CONTRACTL
3500 {
3501 // Yay!
3502 NOTHROW;
3503
3504 // Yay!
3505 GC_TRIGGERS;
3506
3507 // Yay!
3508 MODE_PREEMPTIVE;
3509
3510 // Yay!
3511 CAN_TAKE_LOCK;
3512
3513 // The JIT / MethodDesc code likely hold locks while this callback is made
3514
3515 SO_NOT_MAINLINE;
3516 }
3517 CONTRACTL_END;
3518
3519 CLR_TO_PROFILER_ENTRYPOINT((LF_CORPROF,
3520 LL_INFO1000,
3521 "**PROF: JITInlining caller: 0x%p, callee: 0x%p.\n",
3522 callerId,
3523 calleeId));
3524
3525 _ASSERTE(callerId);
3526 _ASSERTE(calleeId);
3527
3528 {
3529 // All callbacks are really NOTHROW, but that's enforced partially by the profiler,
3530 // whose try/catch blocks aren't visible to the contract system
3531 PERMANENT_CONTRACT_VIOLATION(ThrowsViolation, ReasonProfilerCallout);
3532 return m_pCallback2->JITInlining(callerId, calleeId, pfShouldInline);
3533 }
3534}
3535
3536HRESULT EEToProfInterfaceImpl::ReJITCompilationStarted(
3537 /* [in] */ FunctionID functionId,
3538 /* [in] */ ReJITID reJitId,
3539 /* [in] */ BOOL fIsSafeToBlock)
3540{
3541 CONTRACTL
3542 {
3543 // Yay!
3544 NOTHROW;
3545
3546 // Yay!
3547 GC_TRIGGERS;
3548
3549 // Yay!
3550 MODE_PREEMPTIVE;
3551
3552 // Yay!
3553 CAN_TAKE_LOCK;
3554
3555 // The JIT / MethodDesc code likely hold locks while this callback is made
3556
3557 SO_NOT_MAINLINE;
3558 }
3559 CONTRACTL_END;
3560
3561 CLR_TO_PROFILER_ENTRYPOINT((LF_CORPROF,
3562 LL_INFO1000,
3563 "**PROF: ReJITCompilationStarted 0x%p 0x%p.\n",
3564 functionId, reJitId));
3565
3566 // Should only be called on profilers that support ICorProfilerCallback4
3567 _ASSERTE(m_pCallback4 != NULL);
3568
3569 // Currently ReJITCompilationStarted is always called with fIsSafeToBlock==TRUE. If this ever changes,
3570 // it's safe to remove this assert, but this should serve as a trigger to change our
3571 // public documentation to state that this callback is no longer called in preemptive mode all the time.
3572 _ASSERTE(fIsSafeToBlock);
3573
3574 _ASSERTE(functionId);
3575 _ASSERTE(reJitId);
3576
3577 {
3578 // All callbacks are really NOTHROW, but that's enforced partially by the profiler,
3579 // whose try/catch blocks aren't visible to the contract system
3580 PERMANENT_CONTRACT_VIOLATION(ThrowsViolation, ReasonProfilerCallout);
3581 return m_pCallback4->ReJITCompilationStarted(functionId, reJitId, fIsSafeToBlock);
3582 }
3583}
3584
3585HRESULT EEToProfInterfaceImpl::GetReJITParameters(
3586 /* [in] */ ModuleID moduleId,
3587 /* [in] */ mdMethodDef methodId,
3588 /* [in] */ ICorProfilerFunctionControl *
3589 pFunctionControl)
3590{
3591 CONTRACTL
3592 {
3593 // Yay!
3594 NOTHROW;
3595
3596 // Yay!
3597 GC_TRIGGERS;
3598
3599 // Yay!
3600 MODE_PREEMPTIVE;
3601
3602 // Yay!
3603 CAN_TAKE_LOCK;
3604
3605 // The ReJIT code holds a lock while this callback is made
3606
3607 SO_NOT_MAINLINE;
3608 }
3609 CONTRACTL_END;
3610
3611 CLR_TO_PROFILER_ENTRYPOINT((LF_CORPROF,
3612 LL_INFO1000,
3613 "**PROF: GetReJITParameters 0x%p 0x%p.\n",
3614 moduleId, methodId));
3615
3616 // Should only be called on profilers that support ICorProfilerCallback4
3617 _ASSERTE(m_pCallback4 != NULL);
3618
3619 _ASSERTE(moduleId);
3620
3621 {
3622 // All callbacks are really NOTHROW, but that's enforced partially by the profiler,
3623 // whose try/catch blocks aren't visible to the contract system
3624 PERMANENT_CONTRACT_VIOLATION(ThrowsViolation, ReasonProfilerCallout);
3625 return m_pCallback4->GetReJITParameters(moduleId, methodId, pFunctionControl);
3626 }
3627}
3628
3629HRESULT EEToProfInterfaceImpl::ReJITCompilationFinished(
3630 /* [in] */ FunctionID functionId,
3631 /* [in] */ ReJITID reJitId,
3632 /* [in] */ HRESULT hrStatus,
3633 /* [in] */ BOOL fIsSafeToBlock)
3634{
3635 CONTRACTL
3636 {
3637 // Yay!
3638 NOTHROW;
3639
3640 // Yay!
3641 GC_TRIGGERS;
3642
3643 // Yay!
3644 MODE_PREEMPTIVE;
3645
3646 // Yay!
3647 CAN_TAKE_LOCK;
3648
3649 // ReJit holds a lock as well as possibly others...
3650
3651 SO_NOT_MAINLINE;
3652 }
3653 CONTRACTL_END;
3654
3655 CLR_TO_PROFILER_ENTRYPOINT((LF_CORPROF,
3656 LL_INFO1000,
3657 "**PROF: ReJITCompilationFinished 0x%p 0x%p hr=0x%x.\n",
3658 functionId, reJitId, hrStatus));
3659
3660 // Should only be called on profilers that support ICorProfilerCallback4
3661 _ASSERTE(m_pCallback4 != NULL);
3662
3663 _ASSERTE(functionId);
3664 _ASSERTE(reJitId);
3665
3666 {
3667 // All callbacks are really NOTHROW, but that's enforced partially by the profiler,
3668 // whose try/catch blocks aren't visible to the contract system
3669 PERMANENT_CONTRACT_VIOLATION(ThrowsViolation, ReasonProfilerCallout);
3670 return m_pCallback4->ReJITCompilationFinished(functionId, reJitId, hrStatus, fIsSafeToBlock);
3671 }
3672}
3673
3674
3675HRESULT EEToProfInterfaceImpl::ReJITError(
3676 /* [in] */ ModuleID moduleId,
3677 /* [in] */ mdMethodDef methodId,
3678 /* [in] */ FunctionID functionId,
3679 /* [in] */ HRESULT hrStatus)
3680{
3681 CONTRACTL
3682 {
3683 // Yay!
3684 NOTHROW;
3685
3686 // Yay!
3687 GC_TRIGGERS;
3688
3689 // Yay!
3690 MODE_PREEMPTIVE;
3691
3692 // Yay!
3693 CAN_TAKE_LOCK;
3694
3695 SO_NOT_MAINLINE;
3696 }
3697 CONTRACTL_END;
3698
3699 CLR_TO_PROFILER_ENTRYPOINT((LF_CORPROF,
3700 LL_INFO1000,
3701 "**PROF: ReJITError 0x%p 0x%x 0x%p 0x%x.\n",
3702 moduleId, methodId, functionId, hrStatus));
3703
3704 // Should only be called on profilers that support ICorProfilerCallback4
3705 _ASSERTE(m_pCallback4 != NULL);
3706
3707 {
3708 // All callbacks are really NOTHROW, but that's enforced partially by the profiler,
3709 // whose try/catch blocks aren't visible to the contract system
3710 PERMANENT_CONTRACT_VIOLATION(ThrowsViolation, ReasonProfilerCallout);
3711 return m_pCallback4->ReJITError(moduleId, methodId, functionId, hrStatus);
3712 }
3713}
3714
3715//---------------------------------------------------------------------------------------
3716// MODULE EVENTS
3717//
3718
3719HRESULT EEToProfInterfaceImpl::ModuleLoadStarted(ModuleID moduleId)
3720{
3721 CONTRACTL
3722 {
3723 // Yay!
3724 NOTHROW;
3725
3726 // Yay!
3727 GC_TRIGGERS;
3728
3729 // This has historically not run in preemptive, and is called from cooperative-mode
3730 // functions. However, since we're triggers, it might actually be safe to consider
3731 // letting this run in preemptive mode.
3732 MODE_COOPERATIVE;
3733
3734 // Yay!
3735 CAN_TAKE_LOCK;
3736
3737 // Yay!
3738 ASSERT_NO_EE_LOCKS_HELD();
3739
3740 SO_NOT_MAINLINE;
3741 }
3742 CONTRACTL_END;
3743
3744 CLR_TO_PROFILER_ENTRYPOINT((LF_CORPROF,
3745 LL_INFO10,
3746 "**PROF: ModuleLoadStarted 0x%p.\n",
3747 moduleId));
3748
3749 _ASSERTE(moduleId != 0);
3750
3751 {
3752 // All callbacks are really NOTHROW, but that's enforced partially by the profiler,
3753 // whose try/catch blocks aren't visible to the contract system
3754 PERMANENT_CONTRACT_VIOLATION(ThrowsViolation, ReasonProfilerCallout);
3755 return m_pCallback2->ModuleLoadStarted(moduleId);
3756 }
3757}
3758
3759
3760HRESULT EEToProfInterfaceImpl::ModuleLoadFinished(
3761 ModuleID moduleId,
3762 HRESULT hrStatus)
3763{
3764 CONTRACTL
3765 {
3766 // Yay!
3767 NOTHROW;
3768
3769 // Yay!
3770 GC_TRIGGERS;
3771
3772 // Yay!
3773 MODE_PREEMPTIVE;
3774
3775 // Yay!
3776 CAN_TAKE_LOCK;
3777
3778 // Yay!
3779 ASSERT_NO_EE_LOCKS_HELD();
3780
3781 SO_NOT_MAINLINE;
3782 }
3783 CONTRACTL_END;
3784
3785 CLR_TO_PROFILER_ENTRYPOINT((LF_CORPROF,
3786 LL_INFO10,
3787 "**PROF: ModuleLoadFinished 0x%p.\n",
3788 moduleId));
3789
3790 _ASSERTE(moduleId != 0);
3791
3792 {
3793 // All callbacks are really NOTHROW, but that's enforced partially by the profiler,
3794 // whose try/catch blocks aren't visible to the contract system
3795 PERMANENT_CONTRACT_VIOLATION(ThrowsViolation, ReasonProfilerCallout);
3796 return m_pCallback2->ModuleLoadFinished(moduleId, hrStatus);
3797 }
3798}
3799
3800
3801
3802HRESULT EEToProfInterfaceImpl::ModuleUnloadStarted(
3803 ModuleID moduleId)
3804{
3805 CONTRACTL
3806 {
3807 // Yay!
3808 NOTHROW;
3809
3810 // Yay!
3811 GC_TRIGGERS;
3812
3813 // Yay!
3814 MODE_PREEMPTIVE;
3815
3816 // Yay!
3817 CAN_TAKE_LOCK;
3818
3819 // Yay!
3820 ASSERT_NO_EE_LOCKS_HELD();
3821
3822 SO_NOT_MAINLINE;
3823 }
3824 CONTRACTL_END;
3825
3826 CLR_TO_PROFILER_ENTRYPOINT((LF_CORPROF,
3827 LL_INFO10,
3828 "**PROF: ModuleUnloadStarted 0x%p.\n",
3829 moduleId));
3830
3831 _ASSERTE(moduleId != 0);
3832
3833 {
3834 // All callbacks are really NOTHROW, but that's enforced partially by the profiler,
3835 // whose try/catch blocks aren't visible to the contract system
3836 PERMANENT_CONTRACT_VIOLATION(ThrowsViolation, ReasonProfilerCallout);
3837 return m_pCallback2->ModuleUnloadStarted(moduleId);
3838 }
3839}
3840
3841
3842HRESULT EEToProfInterfaceImpl::ModuleUnloadFinished(
3843 ModuleID moduleId,
3844 HRESULT hrStatus)
3845{
3846 CONTRACTL
3847 {
3848 // Yay!
3849 NOTHROW;
3850
3851 // Yay!
3852 GC_TRIGGERS;
3853
3854 // Yay!
3855 MODE_PREEMPTIVE;
3856
3857 // Yay!
3858 CAN_TAKE_LOCK;
3859
3860 // Yay!
3861 ASSERT_NO_EE_LOCKS_HELD();
3862
3863 SO_NOT_MAINLINE;
3864 }
3865 CONTRACTL_END;
3866
3867 CLR_TO_PROFILER_ENTRYPOINT((LF_CORPROF,
3868 LL_INFO10,
3869 "**PROF: ModuleUnloadFinished 0x%p.\n",
3870 moduleId));
3871 _ASSERTE(moduleId != 0);
3872 {
3873 // All callbacks are really NOTHROW, but that's enforced partially by the profiler,
3874 // whose try/catch blocks aren't visible to the contract system
3875 PERMANENT_CONTRACT_VIOLATION(ThrowsViolation, ReasonProfilerCallout);
3876 return m_pCallback2->ModuleUnloadFinished(moduleId, hrStatus);
3877 }
3878}
3879
3880
3881HRESULT EEToProfInterfaceImpl::ModuleAttachedToAssembly(
3882 ModuleID moduleId,
3883 AssemblyID AssemblyId)
3884{
3885 CONTRACTL
3886 {
3887 // Yay!
3888 NOTHROW;
3889
3890 // Yay!
3891 GC_TRIGGERS;
3892
3893 // Yay!
3894 MODE_PREEMPTIVE;
3895
3896 // Yay!
3897 CAN_TAKE_LOCK;
3898
3899 // Yay!
3900 ASSERT_NO_EE_LOCKS_HELD();
3901
3902 SO_NOT_MAINLINE;
3903 }
3904 CONTRACTL_END;
3905
3906 CLR_TO_PROFILER_ENTRYPOINT((LF_CORPROF,
3907 LL_INFO10,
3908 "**PROF: ModuleAttachedToAssembly 0x%p, 0x%p.\n",
3909 moduleId,
3910 AssemblyId));
3911
3912 _ASSERTE(moduleId != 0);
3913
3914 {
3915 // All callbacks are really NOTHROW, but that's enforced partially by the profiler,
3916 // whose try/catch blocks aren't visible to the contract system
3917 PERMANENT_CONTRACT_VIOLATION(ThrowsViolation, ReasonProfilerCallout);
3918 return m_pCallback2->ModuleAttachedToAssembly(moduleId, AssemblyId);
3919 }
3920}
3921
3922HRESULT EEToProfInterfaceImpl::ModuleInMemorySymbolsUpdated(ModuleID moduleId)
3923{
3924 CONTRACTL
3925 {
3926 // Yay!
3927 NOTHROW;
3928
3929 // Yay!
3930 GC_TRIGGERS;
3931
3932 // Yay!
3933 MODE_PREEMPTIVE;
3934
3935 // Yay!
3936 CAN_TAKE_LOCK;
3937
3938 SO_NOT_MAINLINE;
3939 }
3940 CONTRACTL_END;
3941
3942 CLR_TO_PROFILER_ENTRYPOINT((LF_CORPROF,
3943 LL_INFO10,
3944 "**PROF: ModuleInMemorySymbolsUpdated. moduleId: 0x%p.\n",
3945 moduleId
3946 ));
3947 HRESULT hr = S_OK;
3948
3949 _ASSERTE(IsCallback7Supported());
3950
3951 {
3952 // All callbacks are really NOTHROW, but that's enforced partially by the profiler,
3953 // whose try/catch blocks aren't visible to the contract system
3954 PERMANENT_CONTRACT_VIOLATION(ThrowsViolation, ReasonProfilerCallout);
3955 hr = m_pCallback7->ModuleInMemorySymbolsUpdated(moduleId);
3956 }
3957
3958 return hr;
3959}
3960
3961//---------------------------------------------------------------------------------------
3962// CLASS EVENTS
3963//
3964
3965HRESULT EEToProfInterfaceImpl::ClassLoadStarted(
3966 ClassID classId)
3967{
3968 CONTRACTL
3969 {
3970 // Yay!
3971 NOTHROW;
3972
3973 // Yay!
3974 GC_TRIGGERS;
3975
3976 // Yay!
3977 MODE_PREEMPTIVE;
3978
3979 // Yay!
3980 CAN_TAKE_LOCK;
3981
3982 // UnresolvedClassLock typically held during this callback
3983
3984 SO_NOT_MAINLINE;
3985 }
3986 CONTRACTL_END;
3987
3988 CLR_TO_PROFILER_ENTRYPOINT((LF_CORPROF,
3989 LL_INFO100,
3990 "**PROF: ClassLoadStarted 0x%p.\n",
3991 classId));
3992
3993 _ASSERTE(classId);
3994
3995 {
3996 // All callbacks are really NOTHROW, but that's enforced partially by the profiler,
3997 // whose try/catch blocks aren't visible to the contract system
3998 PERMANENT_CONTRACT_VIOLATION(ThrowsViolation, ReasonProfilerCallout);
3999 return m_pCallback2->ClassLoadStarted(classId);
4000 }
4001}
4002
4003
4004HRESULT EEToProfInterfaceImpl::ClassLoadFinished(
4005 ClassID classId,
4006 HRESULT hrStatus)
4007{
4008 CONTRACTL
4009 {
4010 // Yay!
4011 NOTHROW;
4012
4013 // Yay!
4014 GC_TRIGGERS;
4015
4016 // Yay!
4017 MODE_PREEMPTIVE;
4018
4019 // Yay!
4020 CAN_TAKE_LOCK;
4021
4022 // UnresolvedClassLock typically held during this callback
4023
4024 SO_NOT_MAINLINE;
4025 }
4026 CONTRACTL_END;
4027
4028 CLR_TO_PROFILER_ENTRYPOINT((LF_CORPROF,
4029 LL_INFO100,
4030 "**PROF: ClassLoadFinished 0x%p, 0x%08x.\n",
4031 classId,
4032 hrStatus));
4033
4034 _ASSERTE(classId);
4035
4036 {
4037 // All callbacks are really NOTHROW, but that's enforced partially by the profiler,
4038 // whose try/catch blocks aren't visible to the contract system
4039 PERMANENT_CONTRACT_VIOLATION(ThrowsViolation, ReasonProfilerCallout);
4040 return m_pCallback2->ClassLoadFinished(classId, hrStatus);
4041 }
4042}
4043
4044
4045HRESULT EEToProfInterfaceImpl::ClassUnloadStarted(
4046 ClassID classId)
4047{
4048 CONTRACTL
4049 {
4050 // Yay!
4051 NOTHROW;
4052
4053 // Yay!
4054 GC_TRIGGERS;
4055
4056 // Yay!
4057 MODE_PREEMPTIVE;
4058
4059 // Yay!
4060 CAN_TAKE_LOCK;
4061
4062 // Although not typical, it's possible for UnresolvedClassLock to be held
4063 // during this callback. This can occur if, during the class load, an
4064 // exception is thrown, and EEClass::Destruct is called from the catch clause
4065 // inside ClassLoader::CreateTypeHandleForTypeDefThrowing.
4066
4067 SO_NOT_MAINLINE;
4068 }
4069 CONTRACTL_END;
4070
4071 CLR_TO_PROFILER_ENTRYPOINT((LF_CORPROF,
4072 LL_INFO100,
4073 "**PROF: ClassUnloadStarted 0x%p.\n",
4074 classId));
4075
4076 _ASSERTE(classId);
4077
4078 {
4079 // All callbacks are really NOTHROW, but that's enforced partially by the profiler,
4080 // whose try/catch blocks aren't visible to the contract system
4081 PERMANENT_CONTRACT_VIOLATION(ThrowsViolation, ReasonProfilerCallout);
4082 return m_pCallback2->ClassUnloadStarted(classId);
4083 }
4084}
4085
4086
4087HRESULT EEToProfInterfaceImpl::ClassUnloadFinished(
4088 ClassID classId,
4089 HRESULT hrStatus)
4090{
4091 CONTRACTL
4092 {
4093 // Yay!
4094 NOTHROW;
4095
4096 // Yay!
4097 GC_TRIGGERS;
4098
4099 // Yay!
4100 MODE_PREEMPTIVE;
4101
4102 // Yay!
4103 CAN_TAKE_LOCK;
4104
4105 // Locks can be held when this is called. See comment in ClassUnloadStarted
4106
4107 SO_NOT_MAINLINE;
4108 }
4109 CONTRACTL_END;
4110
4111 CLR_TO_PROFILER_ENTRYPOINT((LF_CORPROF,
4112 LL_INFO100,
4113 "**PROF: ClassUnloadFinished 0x%p, 0x%08x.\n",
4114 classId,
4115 hrStatus));
4116
4117 _ASSERTE(classId);
4118
4119 {
4120 // All callbacks are really NOTHROW, but that's enforced partially by the profiler,
4121 // whose try/catch blocks aren't visible to the contract system
4122 PERMANENT_CONTRACT_VIOLATION(ThrowsViolation, ReasonProfilerCallout);
4123 return m_pCallback2->ClassUnloadFinished(classId, hrStatus);
4124 }
4125}
4126
4127//---------------------------------------------------------------------------------------
4128// APPDOMAIN EVENTS
4129//
4130
4131HRESULT EEToProfInterfaceImpl::AppDomainCreationStarted(
4132 AppDomainID appDomainId)
4133{
4134 CONTRACTL
4135 {
4136 // Yay!
4137 NOTHROW;
4138
4139 // Yay!
4140 GC_TRIGGERS;
4141
4142 // Yay!
4143 MODE_PREEMPTIVE;
4144
4145 // Yay!
4146 CAN_TAKE_LOCK;
4147
4148 // Yay!
4149 ASSERT_NO_EE_LOCKS_HELD();
4150
4151 SO_NOT_MAINLINE;
4152 }
4153 CONTRACTL_END;
4154
4155 CLR_TO_PROFILER_ENTRYPOINT((LF_CORPROF,
4156 LL_INFO10,
4157 "**PROF: AppDomainCreationStarted 0x%p.\n",
4158 appDomainId));
4159
4160 _ASSERTE(appDomainId != 0);
4161
4162 {
4163 // All callbacks are really NOTHROW, but that's enforced partially by the profiler,
4164 // whose try/catch blocks aren't visible to the contract system
4165 PERMANENT_CONTRACT_VIOLATION(ThrowsViolation, ReasonProfilerCallout);
4166 return m_pCallback2->AppDomainCreationStarted(appDomainId);
4167 }
4168}
4169
4170
4171HRESULT EEToProfInterfaceImpl::AppDomainCreationFinished(
4172 AppDomainID appDomainId,
4173 HRESULT hrStatus)
4174{
4175 CONTRACTL
4176 {
4177 // Yay!
4178 NOTHROW;
4179
4180 // Yay!
4181 GC_TRIGGERS;
4182
4183 // Yay!
4184 MODE_PREEMPTIVE;
4185
4186 // Yay!
4187 CAN_TAKE_LOCK;
4188
4189 // Yay!
4190 ASSERT_NO_EE_LOCKS_HELD();
4191
4192 SO_NOT_MAINLINE;
4193 }
4194 CONTRACTL_END;
4195
4196 CLR_TO_PROFILER_ENTRYPOINT((LF_CORPROF,
4197 LL_INFO10,
4198 "**PROF: AppDomainCreationFinished 0x%p, 0x%08x.\n",
4199 appDomainId,
4200 hrStatus));
4201
4202 _ASSERTE(appDomainId != 0);
4203
4204 {
4205 // All callbacks are really NOTHROW, but that's enforced partially by the profiler,
4206 // whose try/catch blocks aren't visible to the contract system
4207 PERMANENT_CONTRACT_VIOLATION(ThrowsViolation, ReasonProfilerCallout);
4208 return m_pCallback2->AppDomainCreationFinished(appDomainId, hrStatus);
4209 }
4210}
4211
4212HRESULT EEToProfInterfaceImpl::AppDomainShutdownStarted(
4213 AppDomainID appDomainId)
4214{
4215 CONTRACTL
4216 {
4217 // Yay!
4218 NOTHROW;
4219
4220 // Yay!
4221 GC_TRIGGERS;
4222
4223 // Yay!
4224 MODE_PREEMPTIVE;
4225
4226 // Yay!
4227 CAN_TAKE_LOCK;
4228
4229 // Yay!
4230 ASSERT_NO_EE_LOCKS_HELD();
4231
4232 SO_NOT_MAINLINE;
4233 }
4234 CONTRACTL_END;
4235
4236 CLR_TO_PROFILER_ENTRYPOINT((LF_CORPROF,
4237 LL_INFO10,
4238 "**PROF: AppDomainShutdownStarted 0x%p.\n",
4239 appDomainId));
4240
4241 _ASSERTE(appDomainId != 0);
4242
4243 {
4244 // All callbacks are really NOTHROW, but that's enforced partially by the profiler,
4245 // whose try/catch blocks aren't visible to the contract system
4246 PERMANENT_CONTRACT_VIOLATION(ThrowsViolation, ReasonProfilerCallout);
4247 return m_pCallback2->AppDomainShutdownStarted(appDomainId);
4248 }
4249}
4250
4251HRESULT EEToProfInterfaceImpl::AppDomainShutdownFinished(
4252 AppDomainID appDomainId,
4253 HRESULT hrStatus)
4254{
4255 CONTRACTL
4256 {
4257 // Yay!
4258 NOTHROW;
4259
4260 // Yay!
4261 GC_TRIGGERS;
4262
4263 // Yay!
4264 MODE_PREEMPTIVE;
4265
4266 // Yay!
4267 CAN_TAKE_LOCK;
4268
4269 // Yay!
4270 ASSERT_NO_EE_LOCKS_HELD();
4271
4272 SO_NOT_MAINLINE;
4273 }
4274 CONTRACTL_END;
4275
4276 CLR_TO_PROFILER_ENTRYPOINT((LF_CORPROF,
4277 LL_INFO10,
4278 "**PROF: AppDomainShutdownFinished 0x%p, 0x%08x.\n",
4279 appDomainId,
4280 hrStatus));
4281
4282 _ASSERTE(appDomainId != 0);
4283
4284 {
4285 // All callbacks are really NOTHROW, but that's enforced partially by the profiler,
4286 // whose try/catch blocks aren't visible to the contract system
4287 PERMANENT_CONTRACT_VIOLATION(ThrowsViolation, ReasonProfilerCallout);
4288 return m_pCallback2->AppDomainShutdownFinished(appDomainId, hrStatus);
4289 }
4290}
4291
4292//---------------------------------------------------------------------------------------
4293// ASSEMBLY EVENTS
4294//
4295
4296HRESULT EEToProfInterfaceImpl::AssemblyLoadStarted(
4297 AssemblyID assemblyId)
4298{
4299 CONTRACTL
4300 {
4301 // Yay!
4302 NOTHROW;
4303
4304 // Yay!
4305 GC_TRIGGERS;
4306
4307 // This has historically not run in preemptive, and is called from cooperative-mode
4308 // functions. However, since we're triggers, it might actually be safe to consider
4309 // letting this run in preemptive mode.
4310 MODE_COOPERATIVE;
4311
4312 // Yay!
4313 CAN_TAKE_LOCK;
4314
4315 // Yay!
4316 ASSERT_NO_EE_LOCKS_HELD();
4317
4318 SO_NOT_MAINLINE;
4319 }
4320 CONTRACTL_END;
4321
4322 CLR_TO_PROFILER_ENTRYPOINT((LF_CORPROF,
4323 LL_INFO10,
4324 "**PROF: AssemblyLoadStarted 0x%p.\n",
4325 assemblyId));
4326
4327 _ASSERTE(assemblyId != 0);
4328
4329 {
4330 // All callbacks are really NOTHROW, but that's enforced partially by the profiler,
4331 // whose try/catch blocks aren't visible to the contract system
4332 PERMANENT_CONTRACT_VIOLATION(ThrowsViolation, ReasonProfilerCallout);
4333 return m_pCallback2->AssemblyLoadStarted(assemblyId);
4334 }
4335}
4336
4337HRESULT EEToProfInterfaceImpl::AssemblyLoadFinished(
4338 AssemblyID assemblyId,
4339 HRESULT hrStatus)
4340{
4341 CONTRACTL
4342 {
4343 // Yay!
4344 NOTHROW;
4345
4346 // Yay!
4347 GC_TRIGGERS;
4348
4349 // This has historically not run in preemptive, and is called from cooperative-mode
4350 // functions. However, since we're triggers, it might actually be safe to consider
4351 // letting this run in preemptive mode.
4352 MODE_COOPERATIVE;
4353
4354 // Yay!
4355 CAN_TAKE_LOCK;
4356
4357 // Yay!
4358 ASSERT_NO_EE_LOCKS_HELD();
4359
4360 SO_NOT_MAINLINE;
4361 }
4362 CONTRACTL_END;
4363
4364 CLR_TO_PROFILER_ENTRYPOINT((LF_CORPROF,
4365 LL_INFO10,
4366 "**PROF: AssemblyLoadFinished 0x%p, 0x%08x.\n",
4367 assemblyId,
4368 hrStatus));
4369
4370 _ASSERTE(assemblyId != 0);
4371
4372 {
4373 // All callbacks are really NOTHROW, but that's enforced partially by the profiler,
4374 // whose try/catch blocks aren't visible to the contract system
4375 PERMANENT_CONTRACT_VIOLATION(ThrowsViolation, ReasonProfilerCallout);
4376 return m_pCallback2->AssemblyLoadFinished(assemblyId, hrStatus);
4377 }
4378}
4379
4380HRESULT EEToProfInterfaceImpl::AssemblyUnloadStarted(
4381 AssemblyID assemblyId)
4382{
4383 CONTRACTL
4384 {
4385 // Yay!
4386 NOTHROW;
4387
4388 // Yay!
4389 GC_TRIGGERS;
4390
4391 // Yay!
4392 MODE_PREEMPTIVE;
4393
4394 // Yay!
4395 CAN_TAKE_LOCK;
4396
4397 // Yay!
4398 ASSERT_NO_EE_LOCKS_HELD();
4399
4400 SO_NOT_MAINLINE;
4401 }
4402 CONTRACTL_END;
4403
4404 CLR_TO_PROFILER_ENTRYPOINT((LF_CORPROF,
4405 LL_INFO10,
4406 "**PROF: AssemblyUnloadStarted 0x%p.\n",
4407 assemblyId));
4408
4409 _ASSERTE(assemblyId != 0);
4410
4411 {
4412 // All callbacks are really NOTHROW, but that's enforced partially by the profiler,
4413 // whose try/catch blocks aren't visible to the contract system
4414 PERMANENT_CONTRACT_VIOLATION(ThrowsViolation, ReasonProfilerCallout);
4415 return m_pCallback2->AssemblyUnloadStarted(assemblyId);
4416 }
4417}
4418
4419HRESULT EEToProfInterfaceImpl::AssemblyUnloadFinished(
4420 AssemblyID assemblyId,
4421 HRESULT hrStatus)
4422{
4423 CONTRACTL
4424 {
4425 // Yay!
4426 NOTHROW;
4427
4428 // Yay!
4429 GC_TRIGGERS;
4430
4431 // Yay!
4432 MODE_PREEMPTIVE;
4433
4434 // Yay!
4435 CAN_TAKE_LOCK;
4436
4437 // Yay!
4438 ASSERT_NO_EE_LOCKS_HELD();
4439
4440 SO_NOT_MAINLINE;
4441 }
4442 CONTRACTL_END;
4443
4444 CLR_TO_PROFILER_ENTRYPOINT((LF_CORPROF,
4445 LL_INFO10,
4446 "**PROF: AssemblyUnloadFinished 0x%p, 0x%08x.\n",
4447 assemblyId,
4448 hrStatus));
4449
4450 _ASSERTE(assemblyId != 0);
4451
4452 {
4453 // All callbacks are really NOTHROW, but that's enforced partially by the profiler,
4454 // whose try/catch blocks aren't visible to the contract system
4455 PERMANENT_CONTRACT_VIOLATION(ThrowsViolation, ReasonProfilerCallout);
4456 return m_pCallback2->AssemblyUnloadFinished(assemblyId, hrStatus);
4457 }
4458}
4459
4460//---------------------------------------------------------------------------------------
4461// TRANSITION EVENTS
4462//
4463
4464HRESULT EEToProfInterfaceImpl::UnmanagedToManagedTransition(
4465 FunctionID functionId,
4466 COR_PRF_TRANSITION_REASON reason)
4467{
4468 CONTRACTL
4469 {
4470 // Yay!
4471 NOTHROW;
4472
4473 // Yay!
4474 GC_TRIGGERS;
4475
4476 // Yay!
4477 MODE_PREEMPTIVE;
4478
4479 // Yay!
4480 CAN_TAKE_LOCK;
4481
4482 // Yay!
4483 ASSERT_NO_EE_LOCKS_HELD();
4484
4485 SO_NOT_MAINLINE;
4486 }
4487 CONTRACTL_END;
4488
4489 CLR_TO_PROFILER_ENTRYPOINT((LF_CORPROF,
4490 LL_INFO10000,
4491 "**PROF: UnmanagedToManagedTransition 0x%p.\n",
4492 functionId));
4493
4494 _ASSERTE(reason == COR_PRF_TRANSITION_CALL || reason == COR_PRF_TRANSITION_RETURN);
4495
4496 {
4497 // All callbacks are really NOTHROW, but that's enforced partially by the profiler,
4498 // whose try/catch blocks aren't visible to the contract system
4499 PERMANENT_CONTRACT_VIOLATION(ThrowsViolation, ReasonProfilerCallout);
4500 return m_pCallback2->UnmanagedToManagedTransition(functionId, reason);
4501 }
4502}
4503
4504HRESULT EEToProfInterfaceImpl::ManagedToUnmanagedTransition(
4505 FunctionID functionId,
4506 COR_PRF_TRANSITION_REASON reason)
4507{
4508 CONTRACTL
4509 {
4510 // Yay!
4511 NOTHROW;
4512
4513 // Yay!
4514 GC_TRIGGERS;
4515
4516 // Yay!
4517 MODE_PREEMPTIVE;
4518
4519 // Yay!
4520 CAN_TAKE_LOCK;
4521
4522 // Yay!
4523 ASSERT_NO_EE_LOCKS_HELD();
4524
4525 SO_NOT_MAINLINE;
4526 }
4527 CONTRACTL_END;
4528
4529 _ASSERTE(reason == COR_PRF_TRANSITION_CALL || reason == COR_PRF_TRANSITION_RETURN);
4530
4531 CLR_TO_PROFILER_ENTRYPOINT((LF_CORPROF,
4532 LL_INFO10000,
4533 "**PROF: ManagedToUnmanagedTransition 0x%p.\n",
4534 functionId));
4535
4536 {
4537 // All callbacks are really NOTHROW, but that's enforced partially by the profiler,
4538 // whose try/catch blocks aren't visible to the contract system
4539 PERMANENT_CONTRACT_VIOLATION(ThrowsViolation, ReasonProfilerCallout);
4540 return m_pCallback2->ManagedToUnmanagedTransition(functionId, reason);
4541 }
4542}
4543
4544//---------------------------------------------------------------------------------------
4545// EXCEPTION EVENTS
4546//
4547
4548HRESULT EEToProfInterfaceImpl::ExceptionThrown(
4549 ObjectID thrownObjectId)
4550{
4551 CONTRACTL
4552 {
4553 // Yay!
4554 NOTHROW;
4555
4556 // Yay!
4557 GC_TRIGGERS;
4558
4559 // Preemptive mode would be bad, dude. There's an objectId in the param list!
4560 MODE_COOPERATIVE;
4561
4562 // Yay!
4563 CAN_TAKE_LOCK;
4564
4565 // Yay!
4566 ASSERT_NO_EE_LOCKS_HELD();
4567
4568 SO_NOT_MAINLINE;
4569 }
4570 CONTRACTL_END;
4571
4572 CLR_TO_PROFILER_ENTRYPOINT((LF_CORPROF,
4573 LL_INFO1000,
4574 "**PROF: ExceptionThrown. ObjectID: 0x%p. ThreadID: 0x%p\n",
4575 thrownObjectId,
4576 GetThread()));
4577
4578 {
4579 // All callbacks are really NOTHROW, but that's enforced partially by the profiler,
4580 // whose try/catch blocks aren't visible to the contract system
4581 PERMANENT_CONTRACT_VIOLATION(ThrowsViolation, ReasonProfilerCallout);
4582 return m_pCallback2->ExceptionThrown(thrownObjectId);
4583 }
4584}
4585
4586HRESULT EEToProfInterfaceImpl::ExceptionSearchFunctionEnter(
4587 FunctionID functionId)
4588{
4589 CONTRACTL
4590 {
4591 // Yay!
4592 NOTHROW;
4593
4594 // Yay!
4595 GC_TRIGGERS;
4596
4597 // Yay!
4598 MODE_PREEMPTIVE;
4599
4600 // Yay!
4601 CAN_TAKE_LOCK;
4602
4603 // Yay!
4604 ASSERT_NO_EE_LOCKS_HELD();
4605
4606 SO_NOT_MAINLINE;
4607 }
4608 CONTRACTL_END;
4609
4610 CLR_TO_PROFILER_ENTRYPOINT((LF_CORPROF,
4611 LL_INFO1000,
4612 "**PROF: ExceptionSearchFunctionEnter. ThreadID: 0x%p, functionId: 0x%p\n",
4613 GetThread(),
4614 functionId));
4615
4616 {
4617 // All callbacks are really NOTHROW, but that's enforced partially by the profiler,
4618 // whose try/catch blocks aren't visible to the contract system
4619 PERMANENT_CONTRACT_VIOLATION(ThrowsViolation, ReasonProfilerCallout);
4620 return m_pCallback2->ExceptionSearchFunctionEnter(functionId);
4621 }
4622}
4623
4624HRESULT EEToProfInterfaceImpl::ExceptionSearchFunctionLeave()
4625{
4626 CONTRACTL
4627 {
4628 // Yay!
4629 NOTHROW;
4630
4631 // Yay!
4632 GC_TRIGGERS;
4633
4634 // Yay!
4635 MODE_PREEMPTIVE;
4636
4637 // Yay!
4638 CAN_TAKE_LOCK;
4639
4640 // Yay!
4641 ASSERT_NO_EE_LOCKS_HELD();
4642
4643 SO_NOT_MAINLINE;
4644 }
4645 CONTRACTL_END;
4646
4647 CLR_TO_PROFILER_ENTRYPOINT((LF_CORPROF,
4648 LL_INFO1000,
4649 "**PROF: ExceptionSearchFunctionLeave. ThreadID: 0x%p\n",
4650 GetThread()));
4651
4652 {
4653 // All callbacks are really NOTHROW, but that's enforced partially by the profiler,
4654 // whose try/catch blocks aren't visible to the contract system
4655 PERMANENT_CONTRACT_VIOLATION(ThrowsViolation, ReasonProfilerCallout);
4656 return m_pCallback2->ExceptionSearchFunctionLeave();
4657 }
4658}
4659
4660HRESULT EEToProfInterfaceImpl::ExceptionSearchFilterEnter(FunctionID functionId)
4661{
4662 CONTRACTL
4663 {
4664 // Yay!
4665 NOTHROW;
4666
4667 // Yay!
4668 GC_TRIGGERS;
4669
4670 // Yay!
4671 MODE_PREEMPTIVE;
4672
4673 // Yay!
4674 CAN_TAKE_LOCK;
4675
4676 // Yay!
4677 ASSERT_NO_EE_LOCKS_HELD();
4678
4679 SO_NOT_MAINLINE;
4680 }
4681 CONTRACTL_END;
4682
4683 CLR_TO_PROFILER_ENTRYPOINT((LF_CORPROF,
4684 LL_INFO1000,
4685 "**PROF: ExceptionSearchFilterEnter. ThreadID: 0x%p, functionId: 0x%p\n",
4686 GetThread(),
4687 functionId));
4688
4689 {
4690 // All callbacks are really NOTHROW, but that's enforced partially by the profiler,
4691 // whose try/catch blocks aren't visible to the contract system
4692 PERMANENT_CONTRACT_VIOLATION(ThrowsViolation, ReasonProfilerCallout);
4693 return m_pCallback2->ExceptionSearchFilterEnter(functionId);
4694 }
4695}
4696
4697HRESULT EEToProfInterfaceImpl::ExceptionSearchFilterLeave()
4698{
4699 CONTRACTL
4700 {
4701 // Yay!
4702 NOTHROW;
4703
4704 // Yay!
4705 GC_TRIGGERS;
4706
4707 // Yay!
4708 MODE_PREEMPTIVE;
4709
4710 // Yay!
4711 CAN_TAKE_LOCK;
4712
4713 // Yay!
4714 ASSERT_NO_EE_LOCKS_HELD();
4715
4716 SO_NOT_MAINLINE;
4717 }
4718 CONTRACTL_END;
4719
4720 CLR_TO_PROFILER_ENTRYPOINT((LF_CORPROF,
4721 LL_INFO1000,
4722 "**PROF: ExceptionFilterLeave. ThreadID: 0x%p\n",
4723 GetThread()));
4724
4725 {
4726 // All callbacks are really NOTHROW, but that's enforced partially by the profiler,
4727 // whose try/catch blocks aren't visible to the contract system
4728 PERMANENT_CONTRACT_VIOLATION(ThrowsViolation, ReasonProfilerCallout);
4729 return m_pCallback2->ExceptionSearchFilterLeave();
4730 }
4731}
4732
4733HRESULT EEToProfInterfaceImpl::ExceptionSearchCatcherFound(FunctionID functionId)
4734{
4735 CONTRACTL
4736 {
4737 // Yay!
4738 NOTHROW;
4739
4740 // Yay!
4741 GC_TRIGGERS;
4742
4743 // Yay!
4744 MODE_PREEMPTIVE;
4745
4746 // Yay!
4747 CAN_TAKE_LOCK;
4748
4749 // Yay!
4750 ASSERT_NO_EE_LOCKS_HELD();
4751
4752 SO_NOT_MAINLINE;
4753 }
4754 CONTRACTL_END;
4755
4756 CLR_TO_PROFILER_ENTRYPOINT((LF_CORPROF,
4757 LL_INFO1000,
4758 "**PROF: ExceptionSearchCatcherFound. ThreadID: 0x%p\n",
4759 GetThread()));
4760
4761 {
4762 // All callbacks are really NOTHROW, but that's enforced partially by the profiler,
4763 // whose try/catch blocks aren't visible to the contract system
4764 PERMANENT_CONTRACT_VIOLATION(ThrowsViolation, ReasonProfilerCallout);
4765 return m_pCallback2->ExceptionSearchCatcherFound(functionId);
4766 }
4767}
4768
4769HRESULT EEToProfInterfaceImpl::ExceptionOSHandlerEnter(FunctionID functionId)
4770{
4771 _ASSERTE(!"ExceptionOSHandlerEnter() callback no longer issued");
4772 return S_OK;
4773}
4774
4775HRESULT EEToProfInterfaceImpl::ExceptionOSHandlerLeave(FunctionID functionId)
4776{
4777 _ASSERTE(!"ExceptionOSHandlerLeave() callback no longer issued");
4778 return S_OK;
4779}
4780
4781HRESULT EEToProfInterfaceImpl::ExceptionUnwindFunctionEnter(FunctionID functionId)
4782{
4783 CONTRACTL
4784 {
4785 // Yay!
4786 NOTHROW;
4787
4788 // Called by COMPlusUnwindCallback, which is notrigger
4789 GC_NOTRIGGER;
4790
4791 // Cannot enable preemptive GC here, since the stack may not be in a GC-friendly state.
4792 // Thus, the profiler cannot block on this call.
4793 MODE_ANY;
4794
4795 // Yay!
4796 CAN_TAKE_LOCK;
4797
4798 // Yay!
4799 ASSERT_NO_EE_LOCKS_HELD();
4800
4801 SO_NOT_MAINLINE;
4802 }
4803 CONTRACTL_END;
4804
4805 CLR_TO_PROFILER_ENTRYPOINT_EX(
4806 kEE2PNoTrigger,
4807 (LF_CORPROF,
4808 LL_INFO1000,
4809 "**PROF: ExceptionUnwindFunctionEnter. ThreadID: 0x%p, functionId: 0x%p\n",
4810 GetThread(),
4811 functionId));
4812
4813 {
4814 // All callbacks are really NOTHROW, but that's enforced partially by the profiler,
4815 // whose try/catch blocks aren't visible to the contract system
4816 PERMANENT_CONTRACT_VIOLATION(ThrowsViolation, ReasonProfilerCallout);
4817 return m_pCallback2->ExceptionUnwindFunctionEnter(functionId);
4818 }
4819}
4820
4821HRESULT EEToProfInterfaceImpl::ExceptionUnwindFunctionLeave()
4822{
4823 CONTRACTL
4824 {
4825 // Yay!
4826 NOTHROW;
4827
4828 // Called by COMPlusUnwindCallback, which is notrigger
4829 GC_NOTRIGGER;
4830
4831 // Cannot enable preemptive GC here, since the stack may not be in a GC-friendly state.
4832 // Thus, the profiler cannot block on this call.
4833 MODE_ANY;
4834
4835 // Yay!
4836 CAN_TAKE_LOCK;
4837
4838 // Yay!
4839 ASSERT_NO_EE_LOCKS_HELD();
4840
4841 SO_NOT_MAINLINE;
4842 }
4843 CONTRACTL_END;
4844
4845 CLR_TO_PROFILER_ENTRYPOINT_EX(
4846 kEE2PNoTrigger,
4847 (LF_CORPROF,
4848 LL_INFO1000,
4849 "**PROF: ExceptionUnwindFunctionLeave. ThreadID: 0x%p\n",
4850 GetThread()));
4851
4852 {
4853 // All callbacks are really NOTHROW, but that's enforced partially by the profiler,
4854 // whose try/catch blocks aren't visible to the contract system
4855 PERMANENT_CONTRACT_VIOLATION(ThrowsViolation, ReasonProfilerCallout);
4856 return m_pCallback2->ExceptionUnwindFunctionLeave();
4857 }
4858}
4859
4860HRESULT EEToProfInterfaceImpl::ExceptionUnwindFinallyEnter(FunctionID functionId)
4861{
4862 CONTRACTL
4863 {
4864 // Yay!
4865 NOTHROW;
4866
4867 // Called by COMPlusUnwindCallback, which is notrigger
4868 GC_NOTRIGGER;
4869
4870 // Cannot enable preemptive GC here, since the stack may not be in a GC-friendly state.
4871 // Thus, the profiler cannot block on this call.
4872 MODE_COOPERATIVE;
4873
4874 // Yay!
4875 CAN_TAKE_LOCK;
4876
4877 // Yay!
4878 ASSERT_NO_EE_LOCKS_HELD();
4879
4880 SO_NOT_MAINLINE;
4881 }
4882 CONTRACTL_END;
4883
4884 CLR_TO_PROFILER_ENTRYPOINT_EX(
4885 kEE2PNoTrigger,
4886 (LF_CORPROF,
4887 LL_INFO1000,
4888 "**PROF: ExceptionUnwindFinallyEnter. ThreadID: 0x%p, functionId: 0x%p\n",
4889 GetThread(),
4890 functionId));
4891
4892 {
4893 // All callbacks are really NOTHROW, but that's enforced partially by the profiler,
4894 // whose try/catch blocks aren't visible to the contract system
4895 PERMANENT_CONTRACT_VIOLATION(ThrowsViolation, ReasonProfilerCallout);
4896 return m_pCallback2->ExceptionUnwindFinallyEnter(functionId);
4897 }
4898}
4899
4900HRESULT EEToProfInterfaceImpl::ExceptionUnwindFinallyLeave()
4901{
4902 CONTRACTL
4903 {
4904 // Yay!
4905 NOTHROW;
4906
4907 // Called by COMPlusUnwindCallback, which is notrigger
4908 GC_NOTRIGGER;
4909
4910 // Cannot enable preemptive GC here, since the stack may not be in a GC-friendly state.
4911 // Thus, the profiler cannot block on this call.
4912 MODE_COOPERATIVE;
4913
4914 // Yay!
4915 CAN_TAKE_LOCK;
4916
4917 // Yay!
4918 ASSERT_NO_EE_LOCKS_HELD();
4919
4920 SO_NOT_MAINLINE;
4921 }
4922 CONTRACTL_END;
4923
4924 CLR_TO_PROFILER_ENTRYPOINT_EX(
4925 kEE2PNoTrigger,
4926 (LF_CORPROF,
4927 LL_INFO1000,
4928 "**PROF: ExceptionUnwindFinallyLeave. ThreadID: 0x%p\n",
4929 GetThread()));
4930
4931 {
4932 // All callbacks are really NOTHROW, but that's enforced partially by the profiler,
4933 // whose try/catch blocks aren't visible to the contract system
4934 PERMANENT_CONTRACT_VIOLATION(ThrowsViolation, ReasonProfilerCallout);
4935 return m_pCallback2->ExceptionUnwindFinallyLeave();
4936 }
4937}
4938
4939HRESULT EEToProfInterfaceImpl::ExceptionCatcherEnter(FunctionID functionId, ObjectID objectId)
4940{
4941 CONTRACTL
4942 {
4943 // Yay!
4944 NOTHROW;
4945
4946 // Called by COMPlusUnwindCallback, which is notrigger
4947 GC_NOTRIGGER;
4948
4949 // Cannot enable preemptive GC here, since the stack may not be in a GC-friendly state.
4950 // Thus, the profiler cannot block on this call.
4951 MODE_COOPERATIVE;
4952
4953 // Yay!
4954 CAN_TAKE_LOCK;
4955
4956 // Yay!
4957 ASSERT_NO_EE_LOCKS_HELD();
4958
4959 SO_NOT_MAINLINE;
4960 }
4961 CONTRACTL_END;
4962
4963 CLR_TO_PROFILER_ENTRYPOINT_EX(
4964 kEE2PNoTrigger,
4965 (LF_CORPROF,
4966 LL_INFO1000, "**PROF: ExceptionCatcherEnter. ThreadID: 0x%p, functionId: 0x%p\n",
4967 GetThread(),
4968 functionId));
4969
4970 {
4971 // All callbacks are really NOTHROW, but that's enforced partially by the profiler,
4972 // whose try/catch blocks aren't visible to the contract system
4973 PERMANENT_CONTRACT_VIOLATION(ThrowsViolation, ReasonProfilerCallout);
4974 return m_pCallback2->ExceptionCatcherEnter(functionId, objectId);
4975 }
4976}
4977
4978HRESULT EEToProfInterfaceImpl::ExceptionCatcherLeave()
4979{
4980 CONTRACTL
4981 {
4982 // Yay!
4983 NOTHROW;
4984
4985 // Yay!
4986 GC_TRIGGERS;
4987
4988 // Cannot enable preemptive GC here, since the stack may not be in a GC-friendly state.
4989 // Thus, the profiler cannot block on this call.
4990 MODE_COOPERATIVE;
4991
4992 // Yay!
4993 CAN_TAKE_LOCK;
4994
4995 // Yay!
4996 ASSERT_NO_EE_LOCKS_HELD();
4997
4998 SO_NOT_MAINLINE;
4999 }
5000 CONTRACTL_END;
5001
5002 CLR_TO_PROFILER_ENTRYPOINT((LF_CORPROF,
5003 LL_INFO1000,
5004 "**PROF: ExceptionCatcherLeave. ThreadID: 0x%p\n",
5005 GetThread()));
5006
5007
5008 {
5009 // All callbacks are really NOTHROW, but that's enforced partially by the profiler,
5010 // whose try/catch blocks aren't visible to the contract system
5011 PERMANENT_CONTRACT_VIOLATION(ThrowsViolation, ReasonProfilerCallout);
5012 return m_pCallback2->ExceptionCatcherLeave();
5013 }
5014}
5015
5016
5017//---------------------------------------------------------------------------------------
5018// COM Callable Wrapper EVENTS
5019//
5020
5021HRESULT EEToProfInterfaceImpl::COMClassicVTableCreated(
5022 /* [in] */ ClassID classId,
5023 /* [in] */ REFGUID implementedIID,
5024 /* [in] */ void *pVTable,
5025 /* [in] */ ULONG cSlots)
5026{
5027 CONTRACTL
5028 {
5029 // Yay!
5030 NOTHROW;
5031
5032 // Yay!
5033 GC_TRIGGERS;
5034
5035 // Yay!
5036 MODE_PREEMPTIVE;
5037
5038 // Yay!
5039 CAN_TAKE_LOCK;
5040
5041 // Yay!
5042 ASSERT_NO_EE_LOCKS_HELD();
5043
5044 SO_NOT_MAINLINE;
5045 }
5046 CONTRACTL_END;
5047
5048 CLR_TO_PROFILER_ENTRYPOINT((LF_CORPROF,
5049 LL_INFO100,
5050 "**PROF: COMClassicWrapperCreated %#x %#08x... %#x %d.\n",
5051 classId,
5052 implementedIID.Data1,
5053 pVTable,
5054 cSlots));
5055
5056 {
5057 // All callbacks are really NOTHROW, but that's enforced partially by the profiler,
5058 // whose try/catch blocks aren't visible to the contract system
5059 PERMANENT_CONTRACT_VIOLATION(ThrowsViolation, ReasonProfilerCallout);
5060 return m_pCallback2->COMClassicVTableCreated(classId, implementedIID, pVTable, cSlots);
5061 }
5062}
5063
5064HRESULT EEToProfInterfaceImpl::COMClassicVTableDestroyed(
5065 /* [in] */ ClassID classId,
5066 /* [in] */ REFGUID implementedIID,
5067 /* [in] */ void *pVTable)
5068{
5069 CONTRACTL
5070 {
5071 // Yay!
5072 NOTHROW;
5073
5074 // Yay!
5075 GC_TRIGGERS;
5076
5077 // Yay!
5078 MODE_PREEMPTIVE;
5079
5080 // Yay!
5081 CAN_TAKE_LOCK;
5082
5083 // Yay!
5084 ASSERT_NO_EE_LOCKS_HELD();
5085
5086 SO_NOT_MAINLINE;
5087 }
5088 CONTRACTL_END;
5089
5090 // NOTE: There is no problem with this code, and it is ready and willing
5091 // to be called. However, this callback is intentionally not being
5092 // issued currently. See comment in ComMethodTable::Cleanup() for more
5093 // information.
5094
5095 CLR_TO_PROFILER_ENTRYPOINT((LF_CORPROF,
5096 LL_INFO100,
5097 "**PROF: COMClassicWrapperDestroyed %#x %#08x... %#x.\n",
5098 classId,
5099 implementedIID.Data1,
5100 pVTable));
5101
5102 {
5103 // All callbacks are really NOTHROW, but that's enforced partially by the profiler,
5104 // whose try/catch blocks aren't visible to the contract system
5105 PERMANENT_CONTRACT_VIOLATION(ThrowsViolation, ReasonProfilerCallout);
5106 return m_pCallback2->COMClassicVTableDestroyed(classId, implementedIID, pVTable);
5107 }
5108}
5109
5110
5111//---------------------------------------------------------------------------------------
5112// GC THREADING EVENTS
5113//
5114
5115HRESULT EEToProfInterfaceImpl::RuntimeSuspendStarted(
5116 COR_PRF_SUSPEND_REASON suspendReason)
5117{
5118 CONTRACTL
5119 {
5120 // Yay!
5121 NOTHROW;
5122
5123 // Although the contract system doesn't yell if I mark this GC_TRIGGERS, it's safest
5124 // not to allow a GC to occur while we're suspending / resuming the runtime, this is
5125 // the thread trying to do a GC. So if the profiler tries to trigger another GC from
5126 // this thread at this time, we might see potential recursion or deadlock.
5127 GC_NOTRIGGER;
5128
5129 MODE_ANY;
5130
5131 // Yay!
5132 CAN_TAKE_LOCK;
5133
5134 // Thread store lock is typically held during this callback
5135
5136 SO_NOT_MAINLINE;
5137 }
5138 CONTRACTL_END;
5139
5140 CLR_TO_PROFILER_ENTRYPOINT_EX(
5141 kEE2PNoTrigger,
5142 (LF_CORPROF,
5143 LL_INFO100,
5144 "**PROF: RuntimeSuspendStarted. ThreadID 0x%p.\n",
5145 GetThread()));
5146
5147 {
5148 // All callbacks are really NOTHROW, but that's enforced partially by the profiler,
5149 // whose try/catch blocks aren't visible to the contract system
5150 PERMANENT_CONTRACT_VIOLATION(ThrowsViolation, ReasonProfilerCallout);
5151 return m_pCallback2->RuntimeSuspendStarted(suspendReason);
5152 }
5153}
5154
5155HRESULT EEToProfInterfaceImpl::RuntimeSuspendFinished()
5156{
5157 CONTRACTL
5158 {
5159 // Yay!
5160 NOTHROW;
5161
5162 // Although the contract system doesn't yell if I mark this GC_TRIGGERS, it's safest
5163 // not to allow a GC to occur while we're suspending / resuming the runtime, this is
5164 // the thread trying to do a GC. So if the profiler tries to trigger another GC from
5165 // this thread at this time, we might see potential recursion or deadlock.
5166 GC_NOTRIGGER;
5167
5168 MODE_ANY;
5169
5170 // Yay!
5171 CAN_TAKE_LOCK;
5172
5173 // Thread store lock is typically held during this callback
5174
5175 SO_NOT_MAINLINE;
5176 }
5177 CONTRACTL_END;
5178
5179 CLR_TO_PROFILER_ENTRYPOINT_EX(
5180 kEE2PNoTrigger,
5181 (LF_CORPROF,
5182 LL_INFO100,
5183 "**PROF: RuntimeSuspendFinished. ThreadID 0x%p.\n",
5184 GetThread()));
5185
5186
5187 {
5188 // All callbacks are really NOTHROW, but that's enforced partially by the profiler,
5189 // whose try/catch blocks aren't visible to the contract system
5190 PERMANENT_CONTRACT_VIOLATION(ThrowsViolation, ReasonProfilerCallout);
5191 return m_pCallback2->RuntimeSuspendFinished();
5192 }
5193}
5194
5195HRESULT EEToProfInterfaceImpl::RuntimeSuspendAborted()
5196{
5197 CONTRACTL
5198 {
5199 // Yay!
5200 NOTHROW;
5201
5202 // Although the contract system doesn't yell if I mark this GC_TRIGGERS, it's safest
5203 // not to allow a GC to occur while we're suspending / resuming the runtime, this is
5204 // the thread trying to do a GC. So if the profiler tries to trigger another GC from
5205 // this thread at this time, we might see potential recursion or deadlock.
5206 GC_NOTRIGGER;
5207
5208 // NOTE: I have no empirical data for gc mode: none of the self-host BVTs call this
5209 // So for now, assume this is callable in any mode.
5210 // This has historically not caused a mode change to preemptive, and is called from
5211 // cooperative-mode functions. Also, switching to preemptive while we're suspending
5212 // the runtime just seems like a bad idea.
5213 MODE_ANY;
5214
5215 // Yay!
5216 CAN_TAKE_LOCK;
5217
5218 // Thread store lock is typically held during this callback
5219
5220 SO_NOT_MAINLINE;
5221 }
5222 CONTRACTL_END;
5223
5224 CLR_TO_PROFILER_ENTRYPOINT_EX(
5225 kEE2PNoTrigger,
5226 (LF_CORPROF,
5227 LL_INFO100,
5228 "**PROF: RuntimeSuspendAborted. ThreadID 0x%p.\n",
5229 GetThread()));
5230
5231 {
5232 // All callbacks are really NOTHROW, but that's enforced partially by the profiler,
5233 // whose try/catch blocks aren't visible to the contract system
5234 PERMANENT_CONTRACT_VIOLATION(ThrowsViolation, ReasonProfilerCallout);
5235 return m_pCallback2->RuntimeSuspendAborted();
5236 }
5237}
5238
5239HRESULT EEToProfInterfaceImpl::RuntimeResumeStarted()
5240{
5241 CONTRACTL
5242 {
5243 // Yay!
5244 NOTHROW;
5245
5246 // Yay!
5247 GC_TRIGGERS;
5248
5249 // This has historically not caused a mode change to preemptive, and is called from
5250 // cooperative-mode functions. Also, switching to preemptive while we're resuming
5251 // the runtime just seems like a bad idea.
5252 MODE_ANY;
5253
5254 // Yay!
5255 CAN_TAKE_LOCK;
5256
5257 // Thread store lock is typically held during this callback
5258
5259 SO_NOT_MAINLINE;
5260 }
5261 CONTRACTL_END;
5262
5263 CLR_TO_PROFILER_ENTRYPOINT((LF_CORPROF,
5264 LL_INFO100,
5265 "**PROF: RuntimeResumeStarted. ThreadID 0x%p.\n",
5266 GetThread()));
5267
5268 {
5269 // All callbacks are really NOTHROW, but that's enforced partially by the profiler,
5270 // whose try/catch blocks aren't visible to the contract system
5271 PERMANENT_CONTRACT_VIOLATION(ThrowsViolation, ReasonProfilerCallout);
5272 return m_pCallback2->RuntimeResumeStarted();
5273 }
5274}
5275
5276HRESULT EEToProfInterfaceImpl::RuntimeResumeFinished()
5277{
5278 CONTRACTL
5279 {
5280 // Yay!
5281 NOTHROW;
5282
5283 // Yay!
5284 GC_TRIGGERS;
5285
5286 // Yay!
5287 MODE_PREEMPTIVE;
5288
5289 // Yay!
5290 CAN_TAKE_LOCK;
5291
5292 // Thread store lock is typically held during this callback
5293
5294 SO_NOT_MAINLINE;
5295 }
5296 CONTRACTL_END;
5297
5298 CLR_TO_PROFILER_ENTRYPOINT((LF_CORPROF,
5299 LL_INFO100,
5300 "**PROF: RuntimeResumeFinished. ThreadID 0x%p.\n",
5301 GetThread()));
5302
5303 {
5304 // All callbacks are really NOTHROW, but that's enforced partially by the profiler,
5305 // whose try/catch blocks aren't visible to the contract system
5306 PERMANENT_CONTRACT_VIOLATION(ThrowsViolation, ReasonProfilerCallout);
5307 return m_pCallback2->RuntimeResumeFinished();
5308 }
5309}
5310
5311HRESULT EEToProfInterfaceImpl::RuntimeThreadSuspended(ThreadID suspendedThreadId)
5312{
5313 CONTRACTL
5314 {
5315 // Yay!
5316 NOTHROW;
5317
5318 // Called by Thread::SuspendThread, which is notrigger.
5319 GC_NOTRIGGER;
5320
5321 // Although I've verified we're called from both coop and preemp, we need to
5322 // avoid switching to preemptive to satisfy our notrigger paths.
5323 MODE_ANY;
5324
5325 // Yay!
5326 CAN_TAKE_LOCK;
5327
5328 // Thread store lock is typically held during this callback
5329
5330 SO_NOT_MAINLINE;
5331 }
5332 CONTRACTL_END;
5333
5334 if (reinterpret_cast<Thread *>(suspendedThreadId)->IsGCSpecial())
5335 return S_OK;
5336
5337 // NOTE: We cannot use the standard CLR_TO_PROFILER_ENTRYPOINT macro here because
5338 // we might be called at a time when profiler callbacks have been disallowed for
5339 // this thread. So we cannot simply ASSERT that callbacks are allowed (as this macro
5340 // does). Instead, we must explicitly check for this condition and return gracefully
5341 // if callbacks are disallowed. So the macro is unwrapped here manually
5342
5343 CHECK_PROFILER_STATUS(kEE2PNone);
5344
5345 LOG((LF_CORPROF, LL_INFO1000, "**PROF: RuntimeThreadSuspended. ThreadID 0x%p.\n",
5346 suspendedThreadId));
5347
5348 // NOTE: We're notrigger, so we cannot switch to preemptive mode.
5349
5350 // We may have already indicated to the profiler that this thread has died, but
5351 // the runtime may continue to suspend this thread during the process of destroying
5352 // the thread, so we do not want to indicate to the profiler these suspensions.
5353 if (!ProfilerCallbacksAllowedForThread((Thread *) suspendedThreadId))
5354 {
5355 return S_OK;
5356 }
5357
5358 // Remaining essentials from our entrypoint macros with kEE2PNoTrigger flag
5359 SetCallbackStateFlagsHolder csf(COR_PRF_CALLBACKSTATE_INCALLBACK);
5360 REMOVE_STACK_GUARD_FOR_PROFILER_CALL;
5361 _ASSERTE(m_pCallback2 != NULL);
5362
5363 {
5364 // SCOPE: ForbidSuspendThreadHolder
5365
5366 // The ForbidSuspendThreadHolder prevents deadlocks under the following scenario:
5367 // 1) Thread A blocks waiting for the current GC to complete (this can happen if A is trying to
5368 // switch to cooperative during a GC).
5369 // 2) This causes us to send a RuntimeThreadSuspended callback to the profiler. (Although
5370 // A isn't technically being "suspended", this blocking is considered suspension as far as the
5371 // profapi is concerned.)
5372 // 3) Profiler, in turn, may take one of its own private locks to synchronize this callback with
5373 // the profiler's attempt to hijack thread A. Specifically, the profiler knows it's not allowed
5374 // to hijack A if A is getting suspended by the runtime, because this suspension might be due to
5375 // the GC trying to hijack A. And if the GC tries to hijack A at the same time as the profiler
5376 // hijacking A and the profiler wins, then GC asserts because A is no longer at the IP that
5377 // the GC thought (VsWhidbey 428477, 429741)
5378 // 4) Meanwhile, thread B (GC thread) is suspending the runtime, and calls Thread::SuspendThread()
5379 // on A. This is the bad thing we're trying to avoid, because when this happens, we call into
5380 // the profiler AGAIN with RuntimeThreadSuspended for thread A, and the profiler again
5381 // tries to grab the lock it acquired in step 3). Yes, at this point we now have two simultaneous
5382 // calls into the profiler's RuntimeThreadSuspended() callback. One saying A is suspending A
5383 // (3 above), and one saying B is suspending A (this step (4)). The problem is that A is now officially
5384 // hard suspended, OS-style, so the lock acquired on 3) ain't never getting released until
5385 // A is resumed. But A won't be resumed until B resumes it. And B won't resume A until
5386 // the profiler returns from its RuntimeThreadSuspended callback. And the profiler
5387 // can't return from its RuntimeThreadSuspended callback until it acquires this lock it tried to
5388 // acquire in 4). And it can't acquire this lock until A is finally resumed so that the acquire
5389 // from 3) is released. Have we gone in a circle yet?
5390 // In order to avoid 4) we inc the ForbidSuspendThread count during 3) to prevent the hard suspension
5391 // (4) from occurring until 3) is completely done. It's sufficient to determine we're in 3) by noting
5392 // whether the callback is reporting that a thread is "suspending itself" (i.e., suspendedThreadId == threadId)
5393
5394 ForbidSuspendThreadHolder forbidSuspendThread((Thread *) suspendedThreadId == GetThread());
5395
5396 {
5397 // All callbacks are really NOTHROW, but that's enforced partially by the profiler,
5398 // whose try/catch blocks aren't visible to the contract system
5399 PERMANENT_CONTRACT_VIOLATION(ThrowsViolation, ReasonProfilerCallout);
5400 return m_pCallback2->RuntimeThreadSuspended(suspendedThreadId);
5401 }
5402 }
5403}
5404
5405HRESULT EEToProfInterfaceImpl::RuntimeThreadResumed(ThreadID resumedThreadId)
5406{
5407 CONTRACTL
5408 {
5409 // Yay!
5410 NOTHROW;
5411
5412 // This gets called in response to another profapi function:
5413 // ICorProfilerInfo2::DoStackSnapshot! And that dude is called asynchronously and
5414 // must therefore never cause a GC.
5415 // Other reasons for notrigger: also called by notrigger dudes Thread::SysStartSuspendForDebug,
5416 // CheckSuspended, Thread::IsRunningIn, Thread::IsExecutingWithinCer, Thread::IsExecutingWithinCer,
5417 // UnwindFrames
5418 GC_NOTRIGGER;
5419
5420 // Although we cannot trigger, verified empirically that this called coop & preemp
5421 MODE_ANY;
5422
5423 // Yay!
5424 CAN_TAKE_LOCK;
5425
5426 // Thread store lock is typically held during this callback
5427
5428 SO_NOT_MAINLINE;
5429 }
5430 CONTRACTL_END;
5431
5432 if (reinterpret_cast<Thread *>(resumedThreadId)->IsGCSpecial())
5433 return S_OK;
5434
5435 // NOTE: We cannot use the standard CLR_TO_PROFILER_ENTRYPOINT macro here because
5436 // we might be called at a time when profiler callbacks have been disallowed for
5437 // this thread. So we cannot simply ASSERT that callbacks are allowed (as this macro
5438 // does). Instead, we must explicitly check for this condition and return gracefully
5439 // if callbacks are disallowed. So the macro is unwrapped here manually
5440
5441 CHECK_PROFILER_STATUS(kEE2PNone);
5442
5443 LOG((LF_CORPROF, LL_INFO1000, "**PROF: RuntimeThreadResumed. ThreadID 0x%p.\n", resumedThreadId));
5444
5445 // NOTE: We're notrigger, so we cannot switch to preemptive mode.
5446
5447 // We may have already indicated to the profiler that this thread has died, but
5448 // the runtime may resume this thread during the process of destroying
5449 // the thread, so we do not want to indicate to the profiler these resumes.
5450 if (!ProfilerCallbacksAllowedForThread((Thread *) resumedThreadId))
5451 {
5452 return S_OK;
5453 }
5454
5455 // Remaining essentials from our entrypoint macros with kEE2PNoTrigger flag
5456 SetCallbackStateFlagsHolder csf(COR_PRF_CALLBACKSTATE_INCALLBACK);
5457 REMOVE_STACK_GUARD_FOR_PROFILER_CALL;
5458 _ASSERTE(m_pCallback2 != NULL);
5459
5460 {
5461 // All callbacks are really NOTHROW, but that's enforced partially by the profiler,
5462 // whose try/catch blocks aren't visible to the contract system
5463 PERMANENT_CONTRACT_VIOLATION(ThrowsViolation, ReasonProfilerCallout);
5464 return m_pCallback2->RuntimeThreadResumed(resumedThreadId);
5465 }
5466}
5467
5468//---------------------------------------------------------------------------------------
5469// REMOTING
5470//
5471
5472HRESULT EEToProfInterfaceImpl::RemotingClientInvocationStarted()
5473{
5474 CONTRACTL
5475 {
5476 // Yay!
5477 NOTHROW;
5478
5479 // Yay!
5480 GC_TRIGGERS;
5481
5482 // Yay!
5483 MODE_PREEMPTIVE;
5484
5485 // Yay!
5486 CAN_TAKE_LOCK;
5487
5488 // Yay!
5489 ASSERT_NO_EE_LOCKS_HELD();
5490
5491 SO_NOT_MAINLINE;
5492 }
5493 CONTRACTL_END;
5494
5495 CLR_TO_PROFILER_ENTRYPOINT((LF_CORPROF,
5496 LL_INFO1000,
5497 "**PROF: RemotingClientInvocationStarted. ThreadID: 0x%p\n",
5498 GetThread()));
5499
5500 {
5501 // All callbacks are really NOTHROW, but that's enforced partially by the profiler,
5502 // whose try/catch blocks aren't visible to the contract system
5503 PERMANENT_CONTRACT_VIOLATION(ThrowsViolation, ReasonProfilerCallout);
5504 return m_pCallback2->RemotingClientInvocationStarted();
5505 }
5506}
5507
5508HRESULT EEToProfInterfaceImpl::RemotingClientSendingMessage(GUID *pCookie, BOOL fIsAsync)
5509{
5510 CONTRACTL
5511 {
5512 // Yay!
5513 NOTHROW;
5514
5515 // Yay!
5516 GC_TRIGGERS;
5517
5518 // Yay!
5519 MODE_PREEMPTIVE;
5520
5521 // Yay!
5522 CAN_TAKE_LOCK;
5523
5524 // Yay!
5525 ASSERT_NO_EE_LOCKS_HELD();
5526
5527 SO_NOT_MAINLINE;
5528 }
5529 CONTRACTL_END;
5530
5531 CLR_TO_PROFILER_ENTRYPOINT((LF_CORPROF,
5532 LL_INFO1000,
5533 "**PROF: RemotingClientSendingMessage. ThreadID: 0x%p\n",
5534 GetThread()));
5535
5536 {
5537 // All callbacks are really NOTHROW, but that's enforced partially by the profiler,
5538 // whose try/catch blocks aren't visible to the contract system
5539 PERMANENT_CONTRACT_VIOLATION(ThrowsViolation, ReasonProfilerCallout);
5540 return m_pCallback2->RemotingClientSendingMessage(pCookie, fIsAsync);
5541 }
5542}
5543
5544HRESULT EEToProfInterfaceImpl::RemotingClientReceivingReply(GUID * pCookie, BOOL fIsAsync)
5545{
5546 CONTRACTL
5547 {
5548 // Yay!
5549 NOTHROW;
5550
5551 // Yay!
5552 GC_TRIGGERS;
5553
5554 // Yay!
5555 MODE_PREEMPTIVE;
5556
5557 // Yay!
5558 CAN_TAKE_LOCK;
5559
5560 // Yay!
5561 ASSERT_NO_EE_LOCKS_HELD();
5562
5563 SO_NOT_MAINLINE;
5564 }
5565 CONTRACTL_END;
5566
5567 CLR_TO_PROFILER_ENTRYPOINT((LF_CORPROF,
5568 LL_INFO1000,
5569 "**PROF: RemotingClientReceivingReply. ThreadID: 0x%p\n",
5570 GetThread()));
5571
5572 {
5573 // All callbacks are really NOTHROW, but that's enforced partially by the profiler,
5574 // whose try/catch blocks aren't visible to the contract system
5575 PERMANENT_CONTRACT_VIOLATION(ThrowsViolation, ReasonProfilerCallout);
5576 return m_pCallback2->RemotingClientReceivingReply(pCookie, fIsAsync);
5577 }
5578}
5579
5580HRESULT EEToProfInterfaceImpl::RemotingClientInvocationFinished()
5581{
5582 CONTRACTL
5583 {
5584 // Yay!
5585 NOTHROW;
5586
5587 // Yay!
5588 GC_TRIGGERS;
5589
5590 // Yay!
5591 MODE_PREEMPTIVE;
5592
5593 // Yay!
5594 CAN_TAKE_LOCK;
5595
5596 // Yay!
5597 ASSERT_NO_EE_LOCKS_HELD();
5598
5599 SO_NOT_MAINLINE;
5600 }
5601 CONTRACTL_END;
5602
5603 CLR_TO_PROFILER_ENTRYPOINT((LF_CORPROF,
5604 LL_INFO1000,
5605 "**PROF: RemotingClientInvocationFinished. ThreadID: 0x%p\n",
5606 GetThread()));
5607
5608 {
5609 // All callbacks are really NOTHROW, but that's enforced partially by the profiler,
5610 // whose try/catch blocks aren't visible to the contract system
5611 PERMANENT_CONTRACT_VIOLATION(ThrowsViolation, ReasonProfilerCallout);
5612 return m_pCallback2->RemotingClientInvocationFinished();
5613 }
5614}
5615
5616HRESULT EEToProfInterfaceImpl::RemotingServerReceivingMessage(GUID *pCookie, BOOL fIsAsync)
5617{
5618 CONTRACTL
5619 {
5620 // Yay!
5621 NOTHROW;
5622
5623 // Yay!
5624 GC_TRIGGERS;
5625
5626 // Yay!
5627 MODE_PREEMPTIVE;
5628
5629 // Yay!
5630 CAN_TAKE_LOCK;
5631
5632 // Yay!
5633 ASSERT_NO_EE_LOCKS_HELD();
5634
5635 SO_NOT_MAINLINE;
5636 }
5637 CONTRACTL_END;
5638
5639 CLR_TO_PROFILER_ENTRYPOINT((LF_CORPROF,
5640 LL_INFO1000,
5641 "**PROF: RemotingServerReceivingMessage. ThreadID: 0x%p\n",
5642 GetThread()));
5643
5644 {
5645 // All callbacks are really NOTHROW, but that's enforced partially by the profiler,
5646 // whose try/catch blocks aren't visible to the contract system
5647 PERMANENT_CONTRACT_VIOLATION(ThrowsViolation, ReasonProfilerCallout);
5648 return m_pCallback2->RemotingServerReceivingMessage(pCookie, fIsAsync);
5649 }
5650}
5651
5652HRESULT EEToProfInterfaceImpl::RemotingServerInvocationStarted()
5653{
5654 CONTRACTL
5655 {
5656 // Yay!
5657 NOTHROW;
5658
5659 // Yay!
5660 GC_TRIGGERS;
5661
5662 // Yay!
5663 MODE_PREEMPTIVE;
5664
5665 // Yay!
5666 CAN_TAKE_LOCK;
5667
5668 // Yay!
5669 ASSERT_NO_EE_LOCKS_HELD();
5670
5671 SO_NOT_MAINLINE;
5672 }
5673 CONTRACTL_END;
5674
5675 CLR_TO_PROFILER_ENTRYPOINT((LF_CORPROF,
5676 LL_INFO1000,
5677 "**PROF: RemotingServerInvocationStarted. ThreadID: 0x%p\n",
5678 GetThread()));
5679
5680 {
5681 // All callbacks are really NOTHROW, but that's enforced partially by the profiler,
5682 // whose try/catch blocks aren't visible to the contract system
5683 PERMANENT_CONTRACT_VIOLATION(ThrowsViolation, ReasonProfilerCallout);
5684 return m_pCallback2->RemotingServerInvocationStarted();
5685 }
5686}
5687
5688HRESULT EEToProfInterfaceImpl::RemotingServerInvocationReturned()
5689{
5690 CONTRACTL
5691 {
5692 // Yay!
5693 NOTHROW;
5694
5695 // Yay!
5696 GC_TRIGGERS;
5697
5698 // Yay!
5699 MODE_PREEMPTIVE;
5700
5701 // Yay!
5702 CAN_TAKE_LOCK;
5703
5704 // Yay!
5705 ASSERT_NO_EE_LOCKS_HELD();
5706
5707 SO_NOT_MAINLINE;
5708 }
5709 CONTRACTL_END;
5710
5711 CLR_TO_PROFILER_ENTRYPOINT((LF_CORPROF,
5712 LL_INFO1000,
5713 "**PROF: RemotingServerInvocationReturned. ThreadID: 0x%p\n",
5714 GetThread()));
5715
5716 {
5717 // All callbacks are really NOTHROW, but that's enforced partially by the profiler,
5718 // whose try/catch blocks aren't visible to the contract system
5719 PERMANENT_CONTRACT_VIOLATION(ThrowsViolation, ReasonProfilerCallout);
5720 return m_pCallback2->RemotingServerInvocationReturned();
5721 }
5722}
5723
5724HRESULT EEToProfInterfaceImpl::RemotingServerSendingReply(GUID *pCookie, BOOL fIsAsync)
5725{
5726 CONTRACTL
5727 {
5728 // Yay!
5729 NOTHROW;
5730
5731 // Yay!
5732 GC_TRIGGERS;
5733
5734 // Yay!
5735 MODE_PREEMPTIVE;
5736
5737 // Yay!
5738 CAN_TAKE_LOCK;
5739
5740 // Yay!
5741 ASSERT_NO_EE_LOCKS_HELD();
5742
5743 SO_NOT_MAINLINE;
5744 }
5745 CONTRACTL_END;
5746
5747 CLR_TO_PROFILER_ENTRYPOINT((LF_CORPROF,
5748 LL_INFO1000,
5749 "**PROF: RemotingServerSendingReply. ThreadID: 0x%p\n",
5750 GetThread()));
5751
5752 {
5753 // All callbacks are really NOTHROW, but that's enforced partially by the profiler,
5754 // whose try/catch blocks aren't visible to the contract system
5755 PERMANENT_CONTRACT_VIOLATION(ThrowsViolation, ReasonProfilerCallout);
5756 return m_pCallback2->RemotingServerSendingReply(pCookie, fIsAsync);
5757 }
5758}
5759
5760//---------------------------------------------------------------------------------------
5761// GC EVENTS
5762//
5763
5764HRESULT EEToProfInterfaceImpl::ObjectAllocated(
5765 /* [in] */ ObjectID objectId,
5766 /* [in] */ ClassID classId)
5767{
5768 CONTRACTL
5769 {
5770 // Yay!
5771 NOTHROW;
5772
5773 // Yay!
5774 GC_TRIGGERS;
5775
5776 // Preemptive mode would be bad, dude. There's an objectId in the param list!
5777 MODE_COOPERATIVE;
5778
5779 // Yay!
5780 CAN_TAKE_LOCK;
5781
5782 // CrstAppDomainHandleTable can be held while this is called
5783
5784 SO_NOT_MAINLINE;
5785 }
5786 CONTRACTL_END;
5787
5788 CLR_TO_PROFILER_ENTRYPOINT((LF_CORPROF,
5789 LL_INFO1000,
5790 "**PROF: ObjectAllocated. ObjectID: 0x%p. ClassID: 0x%p\n",
5791 objectId,
5792 classId));
5793
5794 {
5795 // All callbacks are really NOTHROW, but that's enforced partially by the profiler,
5796 // whose try/catch blocks aren't visible to the contract system
5797 PERMANENT_CONTRACT_VIOLATION(ThrowsViolation, ReasonProfilerCallout);
5798 return m_pCallback2->ObjectAllocated(objectId, classId);
5799 }
5800}
5801
5802
5803HRESULT EEToProfInterfaceImpl::MovedReferences(GCReferencesData *pData)
5804{
5805 CONTRACTL
5806 {
5807 // Yay!
5808 NOTHROW;
5809
5810 // This is called by the thread doing a GC WHILE it does the GC
5811 GC_NOTRIGGER;
5812
5813 // This is called by the thread doing a GC WHILE it does the GC
5814 if (GetThreadNULLOk()) { MODE_COOPERATIVE; }
5815
5816 // Yay!
5817 CAN_TAKE_LOCK;
5818
5819 // Thread store lock normally held during this callback
5820
5821 SO_NOT_MAINLINE;
5822 }
5823 CONTRACTL_END;
5824
5825 CLR_TO_PROFILER_ENTRYPOINT_EX(
5826 kEE2PNoTrigger,
5827 (LF_CORPROF,
5828 LL_INFO10000,
5829 "**PROF: MovedReferences.\n"));
5830
5831 _ASSERTE(!GCHeapUtilities::GetGCHeap()->IsConcurrentGCEnabled());
5832
5833 if (pData->curIdx == 0)
5834 {
5835 return S_OK;
5836 }
5837
5838 HRESULT hr = S_OK;
5839
5840 if (pData->compactingCount != 0)
5841 {
5842 _ASSERTE(pData->curIdx == pData->compactingCount);
5843
5844 if (m_pCallback4 != NULL)
5845 {
5846 // All callbacks are really NOTHROW, but that's enforced partially by the profiler,
5847 // whose try/catch blocks aren't visible to the contract system
5848 PERMANENT_CONTRACT_VIOLATION(ThrowsViolation, ReasonProfilerCallout);
5849 hr = m_pCallback4->MovedReferences2((ULONG)pData->curIdx,
5850 (ObjectID *)pData->arrpbMemBlockStartOld,
5851 (ObjectID *)pData->arrpbMemBlockStartNew,
5852 (SIZE_T *)pData->arrMemBlockSize);
5853 if (FAILED(hr))
5854 return hr;
5855 }
5856
5857#ifdef _WIN64
5858 // Recompute sizes as ULONGs for legacy callback
5859 for (ULONG i = 0; i < pData->curIdx; i++)
5860 pData->arrULONG[i] = (pData->arrMemBlockSize[i] > ULONG_MAX) ? ULONG_MAX : (ULONG)pData->arrMemBlockSize[i];
5861#endif
5862
5863 {
5864 // All callbacks are really NOTHROW, but that's enforced partially by the profiler,
5865 // whose try/catch blocks aren't visible to the contract system
5866 PERMANENT_CONTRACT_VIOLATION(ThrowsViolation, ReasonProfilerCallout);
5867 hr = m_pCallback2->MovedReferences((ULONG)pData->curIdx,
5868 (ObjectID *)pData->arrpbMemBlockStartOld,
5869 (ObjectID *)pData->arrpbMemBlockStartNew,
5870 pData->arrULONG);
5871 }
5872 }
5873 else
5874 {
5875 if (m_pCallback4 != NULL)
5876 {
5877 // All callbacks are really NOTHROW, but that's enforced partially by the profiler,
5878 // whose try/catch blocks aren't visible to the contract system
5879 PERMANENT_CONTRACT_VIOLATION(ThrowsViolation, ReasonProfilerCallout);
5880 hr = m_pCallback4->SurvivingReferences2((ULONG)pData->curIdx,
5881 (ObjectID *)pData->arrpbMemBlockStartOld,
5882 (SIZE_T *)pData->arrMemBlockSize);
5883 if (FAILED(hr))
5884 return hr;
5885 }
5886
5887#ifdef _WIN64
5888 // Recompute sizes as ULONGs for legacy callback
5889 for (ULONG i = 0; i < pData->curIdx; i++)
5890 pData->arrULONG[i] = (pData->arrMemBlockSize[i] > ULONG_MAX) ? ULONG_MAX : (ULONG)pData->arrMemBlockSize[i];
5891#endif
5892
5893 {
5894 // All callbacks are really NOTHROW, but that's enforced partially by the profiler,
5895 // whose try/catch blocks aren't visible to the contract system
5896 PERMANENT_CONTRACT_VIOLATION(ThrowsViolation, ReasonProfilerCallout);
5897 hr = m_pCallback2->SurvivingReferences((ULONG)pData->curIdx,
5898 (ObjectID *)pData->arrpbMemBlockStartOld,
5899 pData->arrULONG);
5900 }
5901 }
5902
5903 return hr;
5904}
5905
5906HRESULT EEToProfInterfaceImpl::NotifyAllocByClass(AllocByClassData *pData)
5907{
5908 CONTRACTL
5909 {
5910 // Yay!
5911 NOTHROW;
5912
5913 // This is called by the thread doing a GC WHILE it does the GC
5914 GC_NOTRIGGER;
5915
5916 // This is called by the thread doing a GC WHILE it does the GC
5917 if (GetThreadNULLOk()) { MODE_COOPERATIVE; }
5918
5919 // Yay!
5920 CAN_TAKE_LOCK;
5921
5922 // Thread store lock normally held during this callback
5923
5924 SO_NOT_MAINLINE;
5925 }
5926 CONTRACTL_END;
5927
5928 CLR_TO_PROFILER_ENTRYPOINT_EX(
5929 kEE2PNoTrigger,
5930 (LF_CORPROF,
5931 LL_INFO10000,
5932 "**PROF: ObjectsAllocatedByClass.\n"));
5933
5934 _ASSERTE(pData != NULL);
5935 _ASSERTE(pData->iHash > 0);
5936
5937 // If the arrays are not long enough, get rid of them.
5938 if (pData->cLength != 0 && pData->iHash > pData->cLength)
5939 {
5940 _ASSERTE(pData->arrClsId != NULL && pData->arrcObjects != NULL);
5941 delete [] pData->arrClsId;
5942 delete [] pData->arrcObjects;
5943 pData->cLength = 0;
5944 }
5945
5946 // If there are no arrays, must allocate them.
5947 if (pData->cLength == 0)
5948 {
5949 pData->arrClsId = new (nothrow) ClassID[pData->iHash];
5950 if (pData->arrClsId == NULL)
5951 {
5952 return E_OUTOFMEMORY;
5953 }
5954
5955 pData->arrcObjects = new (nothrow) ULONG[pData->iHash];
5956 if (pData->arrcObjects == NULL)
5957 {
5958 delete [] pData->arrClsId;
5959 pData->arrClsId= NULL;
5960
5961 return E_OUTOFMEMORY;
5962 }
5963
5964 // Indicate that the memory was successfully allocated
5965 pData->cLength = pData->iHash;
5966 }
5967
5968 // Now copy all the data
5969 HASHFIND hFind;
5970 CLASSHASHENTRY * pCur = (CLASSHASHENTRY *) pData->pHashTable->FindFirstEntry(&hFind);
5971 size_t iCur = 0; // current index for arrays
5972
5973 while (pCur != NULL)
5974 {
5975 _ASSERTE(iCur < pData->iHash);
5976
5977 pData->arrClsId[iCur] = pCur->m_clsId;
5978 pData->arrcObjects[iCur] = (DWORD) pCur->m_count;
5979
5980 // Move to the next entry
5981 iCur++;
5982 pCur = (CLASSHASHENTRY *) pData->pHashTable->FindNextEntry(&hFind);
5983 }
5984
5985 _ASSERTE(iCur == pData->iHash);
5986
5987 // Now communicate the results to the profiler
5988 {
5989 // All callbacks are really NOTHROW, but that's enforced partially by the profiler,
5990 // whose try/catch blocks aren't visible to the contract system
5991 PERMANENT_CONTRACT_VIOLATION(ThrowsViolation, ReasonProfilerCallout);
5992 return m_pCallback2->ObjectsAllocatedByClass((ULONG)pData->iHash, pData->arrClsId, pData->arrcObjects);
5993 }
5994}
5995
5996HRESULT EEToProfInterfaceImpl::ObjectReference(ObjectID objId,
5997 ClassID classId,
5998 ULONG cNumRefs,
5999 ObjectID *arrObjRef)
6000{
6001 CONTRACTL
6002 {
6003 // Yay!
6004 NOTHROW;
6005
6006 // This is called by the thread doing a GC WHILE it does the GC
6007 GC_NOTRIGGER;
6008
6009 // This is called by the thread doing a GC WHILE it does the GC
6010 if (GetThreadNULLOk()) { MODE_COOPERATIVE; }
6011
6012 // Yay!
6013 CAN_TAKE_LOCK;
6014
6015 // Thread store lock normally held during this callback
6016
6017 SO_NOT_MAINLINE;
6018 }
6019 CONTRACTL_END;
6020
6021 CLR_TO_PROFILER_ENTRYPOINT_EX(
6022 kEE2PNoTrigger,
6023 (LF_CORPROF,
6024 LL_INFO100000,
6025 "**PROF: ObjectReferences.\n"));
6026
6027 _ASSERTE(!GCHeapUtilities::GetGCHeap()->IsConcurrentGCEnabled());
6028
6029 {
6030 // All callbacks are really NOTHROW, but that's enforced partially by the profiler,
6031 // whose try/catch blocks aren't visible to the contract system
6032 PERMANENT_CONTRACT_VIOLATION(ThrowsViolation, ReasonProfilerCallout);
6033 return m_pCallback2->ObjectReferences(objId, classId, cNumRefs, arrObjRef);
6034 }
6035}
6036
6037
6038HRESULT EEToProfInterfaceImpl::FinalizeableObjectQueued(BOOL isCritical, ObjectID objectID)
6039{
6040 CONTRACTL
6041 {
6042 // Yay!
6043 NOTHROW;
6044
6045 // Yay!
6046 GC_TRIGGERS;
6047
6048 // Can't be in preemptive when we're dealing in objectIDs!
6049 // However, it's possible we're on a non-EE Thread--that happens when this
6050 // is a server-mode GC thread.
6051 MODE_COOPERATIVE;
6052
6053 // Yay!
6054 CAN_TAKE_LOCK;
6055
6056 // Thread store lock normally held during this callback
6057
6058 SO_NOT_MAINLINE;
6059 }
6060 CONTRACTL_END;
6061
6062 CLR_TO_PROFILER_ENTRYPOINT((LF_CORPROF,
6063 LL_INFO100,
6064 "**PROF: Notifying profiler of finalizeable object.\n"));
6065
6066 _ASSERTE(!GCHeapUtilities::GetGCHeap()->IsConcurrentGCEnabled());
6067
6068 {
6069 // All callbacks are really NOTHROW, but that's enforced partially by the profiler,
6070 // whose try/catch blocks aren't visible to the contract system
6071 PERMANENT_CONTRACT_VIOLATION(ThrowsViolation, ReasonProfilerCallout);
6072 return m_pCallback2->FinalizeableObjectQueued(isCritical ? COR_PRF_FINALIZER_CRITICAL : 0, objectID);
6073 }
6074}
6075
6076
6077HRESULT EEToProfInterfaceImpl::RootReferences2(GCReferencesData *pData)
6078{
6079 CONTRACTL
6080 {
6081 // Yay!
6082 NOTHROW;
6083
6084 // This is called by the thread doing a GC WHILE it does the GC
6085 GC_NOTRIGGER;
6086
6087 // This is called by the thread doing a GC WHILE it does the GC
6088 if (GetThreadNULLOk()) { MODE_COOPERATIVE; }
6089
6090 // Yay!
6091 CAN_TAKE_LOCK;
6092
6093 // Thread store lock normally held during this callback
6094
6095 SO_NOT_MAINLINE;
6096 }
6097 CONTRACTL_END;
6098
6099 CLR_TO_PROFILER_ENTRYPOINT_EX(
6100 kEE2PNoTrigger,
6101 (LF_CORPROF,
6102 LL_INFO10000,
6103 "**PROF: RootReferences2.\n"));
6104
6105 _ASSERTE(!GCHeapUtilities::GetGCHeap()->IsConcurrentGCEnabled());
6106
6107 HRESULT hr = S_OK;
6108
6109 COR_PRF_GC_ROOT_FLAGS flags[kcReferencesMax];
6110
6111 _ASSERTE(pData->curIdx <= kcReferencesMax);
6112 for (ULONG i = 0; i < pData->curIdx; i++)
6113 {
6114 flags[i] = (COR_PRF_GC_ROOT_FLAGS)(pData->arrULONG[i] & 0xffff);
6115 pData->arrULONG[i] >>= 16;
6116 }
6117
6118 {
6119 // All callbacks are really NOTHROW, but that's enforced partially by the profiler,
6120 // whose try/catch blocks aren't visible to the contract system
6121 PERMANENT_CONTRACT_VIOLATION(ThrowsViolation, ReasonProfilerCallout);
6122 hr = m_pCallback2->RootReferences2((ULONG)pData->curIdx,
6123 (ObjectID *)pData->arrpbMemBlockStartOld,
6124 (COR_PRF_GC_ROOT_KIND *)pData->arrULONG,
6125 flags,
6126 (ObjectID *)pData->arrpbMemBlockStartNew);
6127 if (FAILED(hr))
6128 return hr;
6129 }
6130
6131 {
6132 // All callbacks are really NOTHROW, but that's enforced partially by the profiler,
6133 // whose try/catch blocks aren't visible to the contract system
6134 PERMANENT_CONTRACT_VIOLATION(ThrowsViolation, ReasonProfilerCallout);
6135 hr = m_pCallback2->RootReferences((ULONG)pData->curIdx, (ObjectID *)pData->arrpbMemBlockStartOld);
6136 }
6137
6138 return hr;
6139}
6140
6141
6142HRESULT EEToProfInterfaceImpl::ConditionalWeakTableElementReferences(GCReferencesData * pData)
6143{
6144 CONTRACTL
6145 {
6146 // Yay!
6147 NOTHROW;
6148
6149 // This is called by the thread doing a GC WHILE it does the GC
6150 GC_NOTRIGGER;
6151
6152 // This is called by the thread doing a GC WHILE it does the GC
6153 if (GetThreadNULLOk()) { MODE_COOPERATIVE; }
6154
6155 // Yay!
6156 CAN_TAKE_LOCK;
6157
6158 // Thread store lock normally held during this callback
6159
6160 SO_NOT_MAINLINE;
6161 }
6162 CONTRACTL_END;
6163
6164 CLR_TO_PROFILER_ENTRYPOINT_EX(
6165 kEE2PNoTrigger,
6166 (LF_CORPROF,
6167 LL_INFO10000,
6168 "**PROF: ConditionalWeakTableElementReferences.\n"));
6169
6170 _ASSERTE(!GCHeapUtilities::GetGCHeap()->IsConcurrentGCEnabled());
6171
6172 HRESULT hr = S_OK;
6173
6174 _ASSERTE(pData->curIdx <= kcReferencesMax);
6175
6176 {
6177 // All callbacks are really NOTHROW, but that's enforced partially by the profiler,
6178 // whose try/catch blocks aren't visible to the contract system
6179 PERMANENT_CONTRACT_VIOLATION(ThrowsViolation, ReasonProfilerCallout);
6180 hr = m_pCallback5->ConditionalWeakTableElementReferences(
6181 (ULONG)pData->curIdx,
6182 (ObjectID *)pData->arrpbMemBlockStartOld,
6183 (ObjectID *)pData->arrpbMemBlockStartNew,
6184 (GCHandleID *)pData->arrpbRootId);
6185 }
6186
6187 return hr;
6188}
6189
6190HRESULT EEToProfInterfaceImpl::HandleCreated(UINT_PTR handleId, ObjectID initialObjectId)
6191{
6192 CONTRACTL
6193 {
6194 // Yay!
6195 NOTHROW;
6196
6197 // Called by HndCreateHandle which is notrigger
6198 GC_NOTRIGGER;
6199
6200 // This can be called in preemptive mode if initialObjectId is NULL.
6201 // Otherwise, this will be in cooperative mode. Note that, although this
6202 // can be called in preemptive, when it's called in cooperative we must not
6203 // switch to preemptive (as we normally do in callbacks) and must not trigger,
6204 // as this would really tick off some of our callers (as well as invalidating
6205 // initialObjectId).
6206 if (initialObjectId != NULL)
6207 {
6208 MODE_COOPERATIVE;
6209 }
6210 else
6211 {
6212 MODE_ANY;
6213 }
6214
6215 // Yay!
6216 CAN_TAKE_LOCK;
6217
6218 // CrstAppDomainHandleTable can be held during this callback
6219
6220 SO_NOT_MAINLINE;
6221 }
6222 CONTRACTL_END;
6223
6224 CLR_TO_PROFILER_ENTRYPOINT_EX(
6225 kEE2PNoTrigger,
6226 (LF_CORPROF,
6227 LL_INFO10000,
6228 "**PROF: HandleCreated.\n"));
6229
6230 {
6231 // All callbacks are really NOTHROW, but that's enforced partially by the profiler,
6232 // whose try/catch blocks aren't visible to the contract system
6233 PERMANENT_CONTRACT_VIOLATION(ThrowsViolation, ReasonProfilerCallout);
6234 return m_pCallback2->HandleCreated(handleId, initialObjectId);
6235 }
6236}
6237
6238HRESULT EEToProfInterfaceImpl::HandleDestroyed(UINT_PTR handleId)
6239{
6240 CONTRACTL
6241 {
6242 // Yay!
6243 NOTHROW;
6244
6245 // Called by HndDestroyHandle, which is notrigger. But HndDestroyHandle is also
6246 // MODE_ANY, so perhaps we can change the whole call path to be triggers?
6247 GC_NOTRIGGER;
6248
6249 // Although we're called from a notrigger function, I verified empirically that
6250 // this is called coop & preemp
6251 MODE_ANY;
6252
6253 // Yay!
6254 CAN_TAKE_LOCK;
6255
6256 // Thread store lock is typically held during this callback
6257
6258 SO_NOT_MAINLINE;
6259 }
6260 CONTRACTL_END;
6261
6262 CLR_TO_PROFILER_ENTRYPOINT_EX(
6263 kEE2PNoTrigger,
6264 (LF_CORPROF,
6265 LL_INFO10000,
6266 "**PROF: HandleDestroyed.\n"));
6267
6268 {
6269 // All callbacks are really NOTHROW, but that's enforced partially by the profiler,
6270 // whose try/catch blocks aren't visible to the contract system
6271 PERMANENT_CONTRACT_VIOLATION(ThrowsViolation, ReasonProfilerCallout);
6272 return m_pCallback2->HandleDestroyed(handleId);
6273 }
6274}
6275
6276HRESULT EEToProfInterfaceImpl::GarbageCollectionStarted(int cGenerations, BOOL generationCollected[], COR_PRF_GC_REASON reason)
6277{
6278 CONTRACTL
6279 {
6280 // Yay!
6281 NOTHROW;
6282
6283 // This is called by the thread doing a GC WHILE it does the GC
6284 GC_NOTRIGGER;
6285
6286 // This is called by the thread doing a GC WHILE it does the GC
6287 if (GetThreadNULLOk()) { MODE_COOPERATIVE; }
6288
6289 // Yay!
6290 CAN_TAKE_LOCK;
6291
6292 // Thread store lock normally held during this callback
6293
6294 SO_NOT_MAINLINE;
6295 }
6296 CONTRACTL_END;
6297
6298 CLR_TO_PROFILER_ENTRYPOINT_EX(
6299 kEE2PNoTrigger,
6300 (LF_CORPROF,
6301 LL_INFO10000,
6302 "**PROF: GarbageCollectionStarted.\n"));
6303
6304 _ASSERTE(!GCHeapUtilities::GetGCHeap()->IsConcurrentGCEnabled());
6305
6306 {
6307 // All callbacks are really NOTHROW, but that's enforced partially by the profiler,
6308 // whose try/catch blocks aren't visible to the contract system
6309 PERMANENT_CONTRACT_VIOLATION(ThrowsViolation, ReasonProfilerCallout);
6310 return m_pCallback2->GarbageCollectionStarted(cGenerations, generationCollected, reason);
6311 }
6312}
6313
6314HRESULT EEToProfInterfaceImpl::GarbageCollectionFinished()
6315{
6316 CONTRACTL
6317 {
6318 // Yay!
6319 NOTHROW;
6320
6321 // This is called by the thread doing a GC WHILE it does the GC
6322 GC_NOTRIGGER;
6323
6324 // This is called by the thread doing a GC WHILE it does the GC
6325 if (GetThreadNULLOk()) { MODE_COOPERATIVE; }
6326
6327 // Yay!
6328 CAN_TAKE_LOCK;
6329
6330 // Thread store lock normally held during this callback
6331
6332 SO_NOT_MAINLINE;
6333 }
6334 CONTRACTL_END;
6335
6336 CLR_TO_PROFILER_ENTRYPOINT_EX(
6337 kEE2PNoTrigger,
6338 (LF_CORPROF,
6339 LL_INFO10000,
6340 "**PROF: GarbageCollectionFinished.\n"));
6341
6342 _ASSERTE(!GCHeapUtilities::GetGCHeap()->IsConcurrentGCEnabled());
6343
6344 {
6345 // All callbacks are really NOTHROW, but that's enforced partially by the profiler,
6346 // whose try/catch blocks aren't visible to the contract system
6347 PERMANENT_CONTRACT_VIOLATION(ThrowsViolation, ReasonProfilerCallout);
6348 return m_pCallback2->GarbageCollectionFinished();
6349 }
6350}
6351
6352HRESULT EEToProfInterfaceImpl::ProfilerDetachSucceeded()
6353{
6354 CONTRACTL
6355 {
6356 // Yay!
6357 NOTHROW;
6358
6359 // Yay!
6360 GC_TRIGGERS;
6361
6362 // Yay!
6363 MODE_PREEMPTIVE;
6364
6365 // Yay!
6366 CAN_TAKE_LOCK;
6367
6368 // ProfilingAPIUtility::s_csStatus is held while this callback is issued.
6369
6370 SO_NOT_MAINLINE;
6371 }
6372 CONTRACTL_END;
6373
6374 CLR_TO_PROFILER_ENTRYPOINT_EX(kEE2PAllowableWhileDetaching,
6375 (LF_CORPROF,
6376 LL_INFO10,
6377 "**PROF: ProfilerDetachSucceeded.\n"));
6378
6379 // Should only be called on profilers that support ICorProfilerCallback3
6380 _ASSERTE(m_pCallback3 != NULL);
6381
6382 {
6383 // All callbacks are really NOTHROW, but that's enforced partially by the profiler,
6384 // whose try/catch blocks aren't visible to the contract system
6385 PERMANENT_CONTRACT_VIOLATION(ThrowsViolation, ReasonProfilerCallout);
6386 return m_pCallback3->ProfilerDetachSucceeded();
6387 }
6388}
6389
6390
6391
6392HRESULT EEToProfInterfaceImpl::GetAssemblyReferences(LPCWSTR wszAssemblyPath, IAssemblyBindingClosure * pClosure, AssemblyReferenceClosureWalkContextForProfAPI * pContext)
6393{
6394 CONTRACTL
6395 {
6396 // Yay!
6397 NOTHROW;
6398
6399 // Yay!
6400 GC_TRIGGERS;
6401
6402 // Yay!
6403 MODE_PREEMPTIVE;
6404
6405 // Yay!
6406 CAN_TAKE_LOCK;
6407
6408 SO_NOT_MAINLINE;
6409 }
6410 CONTRACTL_END;
6411
6412 CLR_TO_PROFILER_ENTRYPOINT((LF_CORPROF,
6413 LL_INFO10,
6414 "**PROF: AssemblyReferenceClosureWalkStarted. wszAssemblyPath: 0x%p.\n",
6415 wszAssemblyPath
6416 ));
6417 HRESULT hr = S_OK;
6418
6419
6420 return hr;
6421}
6422
6423
6424#endif // PROFILING_SUPPORTED
6425