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
18FORCEINLINE 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
35FORCEINLINE 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//
66inline 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
109inline 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
145inline 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
171inline 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
212inline /* 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
230FORCEINLINE 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
257FORCEINLINE 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