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// FILE: ProfToEEInterfaceImpl.inl
6//
7// Inline implementation of portions of the code used to help implement the
8// ICorProfilerInfo* interfaces, which allow the Profiler to communicate with the EE.
9//
10
11//
12// ======================================================================================
13
14#ifndef __PROFTOEEINTERFACEIMPL_INL__
15#define __PROFTOEEINTERFACEIMPL_INL__
16
17#ifdef PROFILING_SUPPORTED
18
19//---------------------------------------------------------------------------------------
20// Helpers
21
22
23//---------------------------------------------------------------------------------------
24//
25// "Callback flags" are typically set on the current EE Thread object just before we
26// call into a profiler (see SetCallbackStateFlagsHolder). This helps us remember that
27// we deliberately called into the profiler, as opposed to the profiler gaining control
28// by hijacking a thread. This helper function is used in PROFILER_TO_CLR_ENTRYPOINT_SYNC
29// to test the flags in order to authorize a profiler's call into us. The macro is
30// placed at the top of any call that's supposed to be synchronous-only. If no flags are
31// set, that implies the profiler hijacked the thread, so we reject the call. In
32// contrast, PROFILER_TO_CLR_ENTRYPOINT_ASYNC does NOT call this helper function, and
33// thus deliberately allows the hijacked thread to continue calling back into the runtime.
34//
35// Arguments:
36// dwFlags - Flags to test
37//
38// Return Value:
39// If no EE Thread object: nonzero
40// If EE Thread object AND any of the specified flags are set on it: nonzero
41// Else zero (FALSE)
42//
43
44inline BOOL AreCallbackStateFlagsSet(DWORD dwFlags)
45{
46 CONTRACTL
47 {
48 NOTHROW;
49 GC_NOTRIGGER;
50 MODE_ANY;
51 CANNOT_TAKE_LOCK;
52 EE_THREAD_NOT_REQUIRED;
53 SO_NOT_MAINLINE;
54 }
55 CONTRACTL_END;
56
57 Thread * pThread = GetThreadNULLOk();
58 if (pThread == NULL)
59 {
60 // Not a managed thread; profiler can do whatever it wants
61 return TRUE;
62 }
63
64 BOOL fRet;
65 BEGIN_GETTHREAD_ALLOWED_IN_NO_THROW_REGION;
66 DWORD dwProfilerCallbackFullStateFlags = pThread->GetProfilerCallbackFullState();
67 if ((dwProfilerCallbackFullStateFlags & COR_PRF_CALLBACKSTATE_FORCEGC_WAS_CALLED) != 0)
68 {
69 // Threads on which ForceGC() was successfully called should be treated just
70 // like native threads. Profiler can do whatever it wants
71 return TRUE;
72 }
73
74 fRet = ((dwProfilerCallbackFullStateFlags & dwFlags) == dwFlags);
75 END_GETTHREAD_ALLOWED_IN_NO_THROW_REGION;
76 return fRet;
77}
78
79
80//---------------------------------------------------------------------------------------
81//
82// Simple helper that returns nonzero iff the currently-executing function was called
83// asynchronously (i.e., from outside of a callback, as hijacking profilers do)
84//
85
86inline BOOL IsCalledAsynchronously()
87{
88 LIMITED_METHOD_CONTRACT;
89 return !(AreCallbackStateFlagsSet(COR_PRF_CALLBACKSTATE_INCALLBACK));
90}
91
92
93//---------------------------------------------------------------------------------------
94//
95// Simple helper that decides whether we should avoid calling into the host. Generally,
96// host calls should be avoided if the current Info method was called asynchronously
97// (i.e., from an F1-style hijack), for fear of re-entering the host (mainly SQL).
98//
99// Server GC threads are native (non-EE) threads, which therefore do not track enough
100// state for us to determine if a call is made asynhronously on those threads. So we
101// pessimistically assume that the current call on a server GC thread is from a hijack
102// for the purposes of determining whether we may enter the host. Reasoning for this:
103// * SQL enables server-mode GC
104// * server GC threads are responsible for performing runtime suspension, and thus
105// call Thread::SuspendThread() which yields/sleeps and thus enters the host. So
106// server GC threads are examples of non-EE Threads that actually do spend time
107// in the host (this otherwise almost never happens for other non-EE threads).
108// * In spite of this pessimism, the effect on the profiler should be minimal. The
109// host calls we're avoiding are from the code manager's lock, which:
110// * a) Is only used when doing stack walks or translating IPs to functions
111// * b) Is only affected if it tries to yield/sleep when the code manager
112// writer lock is taken, and that happens for incredibly tiny windows of
113// time.
114//
115
116inline BOOL ShouldAvoidHostCalls()
117{
118 LIMITED_METHOD_CONTRACT;
119
120 return
121 (
122 IsCalledAsynchronously() ||
123 (
124 (GetThreadNULLOk() == NULL) && IsGCSpecialThread()
125 )
126 );
127}
128
129
130//---------------------------------------------------------------------------------------
131//
132// Simple helper that returns nonzero iff the current thread is a non-EE thread in the
133// process of doing a GC
134//
135
136inline BOOL NativeThreadInGC()
137{
138 LIMITED_METHOD_CONTRACT;
139 return ((g_profControlBlock.fGCInProgress) && (GetThreadNULLOk() == NULL));
140}
141
142//---------------------------------------------------------------------------------------
143//
144// ProfToEE functions can use these overloads to determine whether a Thread should be
145// visible to a profiler and thus be suitable for querying information about, by a
146// profiler. If the Thread is non-NULL and is NOT a GCSpecial thread, then it's
147// considered "managed", and is thus visible to the profiler.
148//
149// Arguments:
150// pThread or threadId - Thread to check
151//
152// Return Value:
153// nonzero iff the thread can run managed code
154
155// Notes:
156// See code:Thread::m_fGCSpecial for more information
157//
158
159inline BOOL IsManagedThread(Thread * pThread)
160{
161 LIMITED_METHOD_CONTRACT;
162 return ((pThread != NULL) && (!pThread->IsGCSpecial()));
163}
164
165inline BOOL IsManagedThread(ThreadID threadId)
166{
167 LIMITED_METHOD_CONTRACT;
168 return IsManagedThread(reinterpret_cast<Thread *>(threadId));
169}
170
171//---------------------------------------------------------------------------------------
172//
173// ProfToEEInterfaceImpl ctor.
174//
175
176inline ProfToEEInterfaceImpl::ProfToEEInterfaceImpl()
177{
178 LIMITED_METHOD_CONTRACT;
179};
180
181
182inline BOOL IsClassOfMethodTableInited(MethodTable * pMethodTable, AppDomain * pAppDomain)
183{
184 LIMITED_METHOD_CONTRACT;
185
186 return (pMethodTable->IsRestored() &&
187 (pMethodTable->GetModuleForStatics() != NULL) &&
188 (pMethodTable->GetDomainLocalModule(pAppDomain) != NULL) &&
189 pMethodTable->IsClassInited(pAppDomain));
190}
191
192
193#endif // PROFILING_SUPPORTED
194
195#endif // __PROFTOEEINTERFACEIMPL_INL__
196