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 | // ProfilingHelper.inl |
6 | // |
7 | |
8 | // |
9 | // Inlined implementation of some helper class methods used for |
10 | // miscellaneous purposes within the profiling API |
11 | // |
12 | |
13 | // ====================================================================================== |
14 | |
15 | #ifndef __PROFILING_HELPER_INL__ |
16 | #define __PROFILING_HELPER_INL__ |
17 | |
18 | FORCEINLINE SetCallbackStateFlagsHolder::SetCallbackStateFlagsHolder(DWORD dwFlags) |
19 | { |
20 | // This is called before entering a profiler. We set the specified dwFlags on |
21 | // the Thread object, and remember the previous flags for later. |
22 | BEGIN_GETTHREAD_ALLOWED_IN_NO_THROW_REGION; |
23 | m_pThread = GetThread(); |
24 | if (m_pThread != NULL) |
25 | { |
26 | m_dwOriginalFullState = m_pThread->SetProfilerCallbackStateFlags(dwFlags); |
27 | } |
28 | else |
29 | { |
30 | m_dwOriginalFullState = 0; |
31 | } |
32 | END_GETTHREAD_ALLOWED_IN_NO_THROW_REGION; |
33 | } |
34 | |
35 | FORCEINLINE SetCallbackStateFlagsHolder::~SetCallbackStateFlagsHolder() |
36 | { |
37 | CONTRACTL |
38 | { |
39 | NOTHROW; |
40 | GC_NOTRIGGER; |
41 | SO_TOLERANT; |
42 | MODE_ANY; |
43 | } |
44 | CONTRACTL_END; |
45 | // This is called after the profiler returns to us. We reinstate the |
46 | // original flag set here. |
47 | if (m_pThread != NULL) |
48 | { |
49 | BEGIN_GETTHREAD_ALLOWED_IN_NO_THROW_REGION; |
50 | m_pThread->SetProfilerCallbackFullState(m_dwOriginalFullState); |
51 | END_GETTHREAD_ALLOWED_IN_NO_THROW_REGION; |
52 | } |
53 | } |
54 | |
55 | #ifdef ENABLE_CONTRACTS |
56 | //--------------------------------------------------------------------------------------- |
57 | // |
58 | // This function, used only on debug builds, fetches the triggers bits from the contract |
59 | // to help verify that the contract is compatible with the flags passed in to the |
60 | // entrypoint macros. |
61 | // |
62 | // Arguments: |
63 | // * fTriggers - If nonzero, this function asserts the contract says GC_TRIGGERS, |
64 | // else this function asserts the contract says GC_NOTRIGGER |
65 | // |
66 | inline void AssertTriggersContract(BOOL fTriggers) |
67 | { |
68 | // NOTE: This function cannot have contract, as this function needs to inspect the |
69 | // contract of the calling function |
70 | |
71 | ClrDebugState * pClrDbgState = GetClrDebugState(FALSE); |
72 | if ((pClrDbgState == NULL) || (pClrDbgState->GetContractStackTrace() == NULL)) |
73 | { |
74 | return; |
75 | } |
76 | |
77 | UINT testMask = pClrDbgState->GetContractStackTrace()->m_testmask; |
78 | |
79 | if (fTriggers) |
80 | { |
81 | // If this assert fires, the contract says GC_NOTRIGGER (or is disabled), but the |
82 | // PROFILER_TO_CLR_ENTRYPOINT* / CLR_TO_PROFILER_ENTRYPOINT* macro implies triggers |
83 | _ASSERTE((testMask & Contract::GC_Mask) == Contract::GC_Triggers); |
84 | } |
85 | else |
86 | { |
87 | // If this assert fires, the contract says GC_TRIGGERS, but the |
88 | // PROFILER_TO_CLR_ENTRYPOINT* / CLR_TO_PROFILER_ENTRYPOINT* macro implies no |
89 | // trigger |
90 | _ASSERTE(((testMask & Contract::GC_Mask) == Contract::GC_NoTrigger) || |
91 | ((testMask & Contract::GC_Disabled) != 0)); |
92 | |
93 | } |
94 | } |
95 | #endif //ENABLE_CONTRACTS |
96 | |
97 | // ---------------------------------------------------------------------------- |
98 | // ProfilingAPIUtility::LogNoInterfaceError |
99 | // |
100 | // Description: |
101 | // Simple helper to log an IDS_E_PROF_NO_CALLBACK_IFACE event |
102 | // |
103 | // Arguments: |
104 | // * iidRequested - IID to convert to string and log (as insertion string) |
105 | // * wszCLSID - CLSID to log (as insertion string) |
106 | // |
107 | |
108 | // static |
109 | inline void ProfilingAPIUtility::LogNoInterfaceError(REFIID iidRequested, LPCWSTR wszCLSID) |
110 | { |
111 | CONTRACTL |
112 | { |
113 | THROWS; |
114 | GC_TRIGGERS; |
115 | SO_INTOLERANT; |
116 | MODE_ANY; |
117 | } |
118 | CONTRACTL_END; |
119 | |
120 | WCHAR wszIidRequested[39]; |
121 | if (StringFromGUID2(iidRequested, wszIidRequested, lengthof(wszIidRequested)) == 0) |
122 | { |
123 | // This is a little super-paranoid; but just use an empty string if GUIDs |
124 | // get bigger than we expect. |
125 | _ASSERTE(!"IID buffer too small." ); |
126 | wszIidRequested[0] = L'\0'; |
127 | } |
128 | ProfilingAPIUtility::LogProfError(IDS_E_PROF_NO_CALLBACK_IFACE, wszCLSID, wszIidRequested); |
129 | } |
130 | |
131 | #ifdef _DEBUG |
132 | |
133 | // ---------------------------------------------------------------------------- |
134 | // ProfilingAPIUtility::ShouldInjectProfAPIFault |
135 | // |
136 | // Description: |
137 | // Determines whether COMPlus_ProfAPIFault is set to a bitmask value |
138 | // with the specified flag set |
139 | // |
140 | // Return Value: |
141 | // Nonzero if the specified fault flag is set; 0 otherwise. |
142 | // |
143 | |
144 | // static |
145 | inline BOOL ProfilingAPIUtility::ShouldInjectProfAPIFault(ProfAPIFaultFlags faultFlag) |
146 | { |
147 | return ((CLRConfig::GetConfigValue(CLRConfig::INTERNAL_ProfAPIFault) & faultFlag) != 0); |
148 | } |
149 | |
150 | #endif // _DEBUG |
151 | |
152 | |
153 | // ---------------------------------------------------------------------------- |
154 | // ProfilingAPIUtility::LoadProfilerForAttach |
155 | // |
156 | // Description: |
157 | // Simple, public wrapper around code:ProfilingAPIUtility::LoadProfiler to load a |
158 | // profiler in response to an Attach request. |
159 | // |
160 | // Arguments: |
161 | // * pClsid - Profiler's CLSID |
162 | // * wszProfilerDLL - Profiler's DLL |
163 | // * pvClientData - Client data received from trigger, to send to profiler DLL |
164 | // * cbClientData - Size of client data |
165 | // |
166 | // Return Value: |
167 | // HRESULT indicating success or failure |
168 | // |
169 | |
170 | // static |
171 | inline HRESULT ProfilingAPIUtility::LoadProfilerForAttach( |
172 | const CLSID * pClsid, |
173 | LPCWSTR wszProfilerDLL, |
174 | LPVOID pvClientData, |
175 | UINT cbClientData, |
176 | DWORD dwConcurrentGCWaitTimeoutInMs) |
177 | { |
178 | CONTRACTL |
179 | { |
180 | THROWS; |
181 | GC_TRIGGERS; |
182 | |
183 | // This causes events to be logged, which loads resource strings, |
184 | // which takes locks. |
185 | CAN_TAKE_LOCK; |
186 | |
187 | MODE_PREEMPTIVE; |
188 | } |
189 | CONTRACTL_END; |
190 | |
191 | // Need string version of CLSID for event log messages |
192 | WCHAR wszClsid[40]; |
193 | if (StringFromGUID2(*pClsid, wszClsid, _countof(wszClsid)) == 0) |
194 | { |
195 | _ASSERTE(!"StringFromGUID2 failed!" ); |
196 | return E_UNEXPECTED; |
197 | } |
198 | |
199 | // Inform user we're about to try attaching the profiler |
200 | ProfilingAPIUtility::LogProfInfo(IDS_PROF_ATTACH_REQUEST_RECEIVED, wszClsid); |
201 | |
202 | return LoadProfiler( |
203 | kAttachLoad, |
204 | pClsid, |
205 | wszClsid, |
206 | wszProfilerDLL, |
207 | pvClientData, |
208 | cbClientData, |
209 | dwConcurrentGCWaitTimeoutInMs); |
210 | } |
211 | |
212 | inline /* static */ CRITSEC_COOKIE ProfilingAPIUtility::GetStatusCrst() |
213 | { |
214 | LIMITED_METHOD_CONTRACT; |
215 | return s_csStatus; |
216 | } |
217 | |
218 | #ifdef FEATURE_PROFAPI_ATTACH_DETACH |
219 | |
220 | // ---------------------------------------------------------------------------- |
221 | // ProfilingAPIUtility::IncEvacuationCounter |
222 | // |
223 | // Description: |
224 | // Simple helper to increase the evacuation counter inside an EE thread by one |
225 | // |
226 | // Arguments: |
227 | // * pThread - pointer to an EE Thread |
228 | // |
229 | // static |
230 | FORCEINLINE void ProfilingAPIUtility::IncEvacuationCounter(Thread * pThread) |
231 | { |
232 | CONTRACTL |
233 | { |
234 | NOTHROW; |
235 | GC_NOTRIGGER; |
236 | FORBID_FAULT; |
237 | MODE_ANY; |
238 | CANNOT_TAKE_LOCK; |
239 | SO_NOT_MAINLINE; |
240 | } |
241 | CONTRACTL_END; |
242 | |
243 | if (pThread) |
244 | pThread->IncProfilerEvacuationCounter(); |
245 | } |
246 | |
247 | // ---------------------------------------------------------------------------- |
248 | // ProfilingAPIUtility::DecEvacuationCounter |
249 | // |
250 | // Description: |
251 | // Simple helper to decrease the evacuation counter inside an EE thread by one |
252 | // |
253 | // Arguments: |
254 | // * pThread - pointer to an EE Thread |
255 | // |
256 | // static |
257 | FORCEINLINE void ProfilingAPIUtility::DecEvacuationCounter(Thread * pThread) |
258 | { |
259 | CONTRACTL |
260 | { |
261 | NOTHROW; |
262 | GC_NOTRIGGER; |
263 | FORBID_FAULT; |
264 | MODE_ANY; |
265 | CANNOT_TAKE_LOCK; |
266 | SO_NOT_MAINLINE; |
267 | } |
268 | CONTRACTL_END; |
269 | |
270 | if (pThread) |
271 | pThread->DecProfilerEvacuationCounter(); |
272 | } |
273 | |
274 | #endif // FEATURE_PROFAPI_ATTACH_DETACH |
275 | |
276 | #endif //__PROFILING_HELPER_INL__ |
277 | |