| 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 | |