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 | |
44 | inline 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 | |
86 | inline 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 | |
116 | inline 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 | |
136 | inline 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 | |
159 | inline BOOL IsManagedThread(Thread * pThread) |
160 | { |
161 | LIMITED_METHOD_CONTRACT; |
162 | return ((pThread != NULL) && (!pThread->IsGCSpecial())); |
163 | } |
164 | |
165 | inline 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 | |
176 | inline ProfToEEInterfaceImpl::ProfToEEInterfaceImpl() |
177 | { |
178 | LIMITED_METHOD_CONTRACT; |
179 | }; |
180 | |
181 | |
182 | inline 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 | |