| 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.cpp |
| 6 | // |
| 7 | |
| 8 | // |
| 9 | // Implementation of helper classes used for miscellaneous purposes within the profiling |
| 10 | // API |
| 11 | // |
| 12 | // ====================================================================================== |
| 13 | |
| 14 | // |
| 15 | // #LoadUnloadCallbackSynchronization |
| 16 | // |
| 17 | // There is synchronization around loading profilers, unloading profilers, and issuing |
| 18 | // callbacks to profilers, to ensure that we know when it's safe to detach profilers or |
| 19 | // to call into profilers. The synchronization scheme is intentionally lockless on the |
| 20 | // mainline path (issuing callbacks into the profiler), with heavy locking on the |
| 21 | // non-mainline path (loading / unloading profilers). |
| 22 | // |
| 23 | // PROTECTED DATA |
| 24 | // |
| 25 | // The synchronization protects the following data: |
| 26 | // |
| 27 | // * ProfilingAPIDetach::s_profilerDetachInfo |
| 28 | // * (volatile) g_profControlBlock.curProfStatus.m_profStatus |
| 29 | // * (volatile) g_profControlBlock.pProfInterface |
| 30 | // * latter implies the profiler DLL's load status is protected as well, as |
| 31 | // pProfInterface changes between non-NULL and NULL as a profiler DLL is |
| 32 | // loaded and unloaded, respectively. |
| 33 | // |
| 34 | // SYNCHRONIZATION COMPONENTS |
| 35 | // |
| 36 | // * Simple Crst: code:ProfilingAPIUtility::s_csStatus |
| 37 | // * Lockless, volatile per-thread counters: code:EvacuationCounterHolder |
| 38 | // * Profiler status transition invariants and CPU buffer flushing: |
| 39 | // code:CurrentProfilerStatus::Set |
| 40 | // |
| 41 | // WRITERS |
| 42 | // |
| 43 | // The above data is considered to be "written to" when a profiler is loaded or unloaded, |
| 44 | // or the status changes (see code:ProfilerStatus), or a request to detach the profiler |
| 45 | // is received (see code:ProfilingAPIDetach::RequestProfilerDetach), or the DetachThread |
| 46 | // consumes or modifies the contents of code:ProfilingAPIDetach::s_profilerDetachInfo. |
| 47 | // All these cases are serialized with each other by the simple Crst: |
| 48 | // code:ProfilingAPIUtility::s_csStatus |
| 49 | // |
| 50 | // READERS |
| 51 | // |
| 52 | // Readers are the mainline case and are lockless. A "reader" is anyone who wants to |
| 53 | // issue a profiler callback. Readers are scattered throughout the runtime, and have the |
| 54 | // following format: |
| 55 | // { |
| 56 | // BEGIN_PIN_PROFILER(CORProfilerTrackAppDomainLoads()); |
| 57 | // g_profControlBlock.pProfInterface->AppDomainCreationStarted(MyAppDomainID); |
| 58 | // END_PIN_PROFILER(); |
| 59 | // } |
| 60 | // The BEGIN / END macros do the following: |
| 61 | // * Evaluate the expression argument (e.g., CORProfilerTrackAppDomainLoads()). This is a |
| 62 | // "dirty read" as the profiler could be detached at any moment during or after that |
| 63 | // evaluation. |
| 64 | // * If true, push a code:EvacuationCounterHolder on the stack, which increments the |
| 65 | // per-thread evacuation counter (not interlocked). |
| 66 | // * Re-evaluate the expression argument. This time, it's a "clean read" (see below for |
| 67 | // why). |
| 68 | // * If still true, execute the statements inside the BEGIN/END block. Inside that block, |
| 69 | // the profiler is guaranteed to remain loaded, because the evacuation counter |
| 70 | // remains nonzero (again, see below). |
| 71 | // * Once the BEGIN/END block is exited, the evacuation counter is decremented, and the |
| 72 | // profiler is unpinned and allowed to detach. |
| 73 | // |
| 74 | // READER / WRITER COORDINATION |
| 75 | // |
| 76 | // The above ensures that a reader never touches g_profControlBlock.pProfInterface and |
| 77 | // all it embodies (including the profiler DLL code and callback implementations) unless |
| 78 | // the reader was able to increment its thread's evacuation counter AND re-verify that |
| 79 | // the profiler's status is still active (the status check is included in the macro's |
| 80 | // expression argument, such as CORProfilerTrackAppDomainLoads()). |
| 81 | // |
| 82 | // At the same time, a profiler DLL is never unloaded (nor |
| 83 | // g_profControlBlock.pProfInterface deleted and NULLed out) UNLESS the writer performs |
| 84 | // these actions: |
| 85 | // * (a) Set the profiler's status to a non-active state like kProfStatusDetaching or |
| 86 | // kProfStatusNone |
| 87 | // * (b) Call FlushProcessWriteBuffers() |
| 88 | // * (c) Grab thread store lock, iterate through all threads, and verify each per-thread |
| 89 | // evacuation counter is zero. |
| 90 | // |
| 91 | // The above steps are why it's considered a "clean read" if a reader first increments |
| 92 | // its evacuation counter and then checks the profiler status. Once the writer flushes |
| 93 | // the CPU buffers (b), the reader will see the updated status (from a) and know not to |
| 94 | // use g_profControlBlock.pProfInterface. And if the reader clean-reads the status before |
| 95 | // the buffers were flushed, then the reader will have incremented its evacuation counter |
| 96 | // first, which the writer will be sure to see in (c). For more details about how the |
| 97 | // evacuation counters work, see code:ProfilingAPIDetach::IsProfilerEvacuated. |
| 98 | // |
| 99 | // WHEN ARE BEGIN/END_PIN_PROFILER REQUIRED? |
| 100 | // |
| 101 | // In general, any time you access g_profControlBlock.pProfInterface, you must be inside |
| 102 | // a BEGIN/END_PIN_PROFILER block. This is pretty much always true throughout the EE, but |
| 103 | // there are some exceptions inside the profiling API code itself, where the BEGIN / END |
| 104 | // macros are unnecessary: |
| 105 | // * If you are inside a public ICorProfilerInfo function's implementation, the |
| 106 | // profiler is already pinned. This is because the profiler called the Info |
| 107 | // function from either: |
| 108 | // * a callback implemented inside of g_profControlBlock.pProfInterface, in which |
| 109 | // case the BEGIN/END macros are already in place around the call to that |
| 110 | // callback, OR |
| 111 | // * a hijacked thread or a thread of the profiler's own creation. In either |
| 112 | // case, it's the profiler's responsibility to end hijacking and end its own |
| 113 | // threads before requesting a detach. So the profiler DLL is guaranteed not |
| 114 | // to disappear while hijacking or profiler-created threads are in action. |
| 115 | // * If you're executing while code:ProfilingAPIUtility::s_csStatus is held, then |
| 116 | // you're explicitly serialized against all code that might unload the profiler's |
| 117 | // DLL and delete g_profControlBlock.pProfInterface. So the profiler is therefore |
| 118 | // still guaranteed not to disappear. |
| 119 | // * If slow ELT helpers, fast ELT hooks, or profiler-instrumented code is on the |
| 120 | // stack, then the profiler cannot be detached yet anyway. Today, we outright |
| 121 | // refuse a detach request from a profiler that instrumented code or enabled ELT. |
| 122 | // Once rejit / revert is implemented, the evacuation checks will ensure all |
| 123 | // instrumented code (including ELT) are reverted and off all stacks before |
| 124 | // attempting to unload the profielr. |
| 125 | |
| 126 | |
| 127 | #include "common.h" |
| 128 | |
| 129 | #ifdef PROFILING_SUPPORTED |
| 130 | |
| 131 | #include "eeprofinterfaces.h" |
| 132 | #include "eetoprofinterfaceimpl.h" |
| 133 | #include "eetoprofinterfaceimpl.inl" |
| 134 | #include "corprof.h" |
| 135 | #include "proftoeeinterfaceimpl.h" |
| 136 | #include "proftoeeinterfaceimpl.inl" |
| 137 | #include "profilinghelper.h" |
| 138 | #include "profilinghelper.inl" |
| 139 | #include "eemessagebox.h" |
| 140 | |
| 141 | |
| 142 | #ifdef FEATURE_PROFAPI_ATTACH_DETACH |
| 143 | #include "profattach.h" |
| 144 | #include "profdetach.h" |
| 145 | #endif // FEATURE_PROFAPI_ATTACH_DETACH |
| 146 | |
| 147 | #include "utilcode.h" |
| 148 | |
| 149 | #ifndef FEATURE_PAL |
| 150 | #include "securitywrapper.h" |
| 151 | #endif // !FEATURE_PAL |
| 152 | |
| 153 | //--------------------------------------------------------------------------------------- |
| 154 | // Normally, this would go in profilepriv.inl, but it's not easily inlineable because of |
| 155 | // the use of BEGIN/END_PIN_PROFILER |
| 156 | // |
| 157 | // Return Value: |
| 158 | // TRUE iff security transparency checks in full trust assemblies should be disabled |
| 159 | // due to the profiler. |
| 160 | // |
| 161 | BOOL CORProfilerBypassSecurityChecks() |
| 162 | { |
| 163 | CONTRACTL |
| 164 | { |
| 165 | NOTHROW; |
| 166 | GC_NOTRIGGER; |
| 167 | CANNOT_TAKE_LOCK; |
| 168 | SO_NOT_MAINLINE; |
| 169 | } |
| 170 | CONTRACTL_END; |
| 171 | |
| 172 | { |
| 173 | BEGIN_PIN_PROFILER(CORProfilerPresent()); |
| 174 | |
| 175 | // V2 profiler binaries, for compatibility purposes, should bypass transparency |
| 176 | // checks in full trust assemblies. |
| 177 | if (!(&g_profControlBlock)->pProfInterface->IsCallback3Supported()) |
| 178 | return TRUE; |
| 179 | |
| 180 | // V4 profiler binaries must opt in to bypasssing transparency checks in full trust |
| 181 | // assemblies. |
| 182 | if (((&g_profControlBlock)->dwEventMask & COR_PRF_DISABLE_TRANSPARENCY_CHECKS_UNDER_FULL_TRUST) != 0) |
| 183 | return TRUE; |
| 184 | |
| 185 | END_PIN_PROFILER(); |
| 186 | } |
| 187 | |
| 188 | // All other cases, including no profiler loaded at all: Don't bypass |
| 189 | return FALSE; |
| 190 | } |
| 191 | |
| 192 | // ---------------------------------------------------------------------------- |
| 193 | // CurrentProfilerStatus methods |
| 194 | |
| 195 | |
| 196 | //--------------------------------------------------------------------------------------- |
| 197 | // |
| 198 | // Updates the value indicating the profiler's current status |
| 199 | // |
| 200 | // Arguments: |
| 201 | // profStatus - New value (from enum ProfilerStatus) to set. |
| 202 | // |
| 203 | // Notes: |
| 204 | // Sets the status under a lock, and performs a debug-only check to verify that the |
| 205 | // status transition is a legal one. Also performs a FlushStoreBuffers() after |
| 206 | // changing the status when necessary. |
| 207 | // |
| 208 | |
| 209 | void CurrentProfilerStatus::Set(ProfilerStatus newProfStatus) |
| 210 | { |
| 211 | CONTRACTL |
| 212 | { |
| 213 | NOTHROW; |
| 214 | GC_TRIGGERS; |
| 215 | MODE_ANY; |
| 216 | CAN_TAKE_LOCK; |
| 217 | } |
| 218 | CONTRACTL_END; |
| 219 | |
| 220 | _ASSERTE(ProfilingAPIUtility::GetStatusCrst() != NULL); |
| 221 | |
| 222 | { |
| 223 | // Need to serialize attempts to transition the profiler status. For example, a |
| 224 | // profiler in one thread could request a detach, while the CLR in another |
| 225 | // thread is transitioning the profiler from kProfStatusInitializing* to |
| 226 | // kProfStatusActive |
| 227 | CRITSEC_Holder csh(ProfilingAPIUtility::GetStatusCrst()); |
| 228 | |
| 229 | // Based on what the old status is, verify the new status is a legal transition. |
| 230 | switch(m_profStatus) |
| 231 | { |
| 232 | default: |
| 233 | _ASSERTE(!"Unknown ProfilerStatus" ); |
| 234 | break; |
| 235 | |
| 236 | case kProfStatusNone: |
| 237 | _ASSERTE((newProfStatus == kProfStatusInitializingForStartupLoad) || |
| 238 | (newProfStatus == kProfStatusInitializingForAttachLoad)); |
| 239 | break; |
| 240 | |
| 241 | case kProfStatusDetaching: |
| 242 | _ASSERTE(newProfStatus == kProfStatusNone); |
| 243 | break; |
| 244 | |
| 245 | case kProfStatusInitializingForStartupLoad: |
| 246 | case kProfStatusInitializingForAttachLoad: |
| 247 | _ASSERTE((newProfStatus == kProfStatusActive) || |
| 248 | (newProfStatus == kProfStatusNone)); |
| 249 | break; |
| 250 | |
| 251 | case kProfStatusActive: |
| 252 | _ASSERTE((newProfStatus == kProfStatusNone) || |
| 253 | (newProfStatus == kProfStatusDetaching)); |
| 254 | break; |
| 255 | } |
| 256 | |
| 257 | m_profStatus = newProfStatus; |
| 258 | } |
| 259 | |
| 260 | #if !defined(DACCESS_COMPILE) |
| 261 | if (((newProfStatus == kProfStatusNone) || |
| 262 | (newProfStatus == kProfStatusDetaching) || |
| 263 | (newProfStatus == kProfStatusActive))) |
| 264 | { |
| 265 | // Flush the store buffers on all CPUs, to ensure other threads see that |
| 266 | // g_profControlBlock.curProfStatus has changed. The important status changes to |
| 267 | // flush are: |
| 268 | // * to kProfStatusNone or kProfStatusDetaching so other threads know to stop |
| 269 | // making calls into the profiler |
| 270 | // * to kProfStatusActive, to ensure callbacks can be issued by the time an |
| 271 | // attaching profiler receives ProfilerAttachComplete(), so the profiler |
| 272 | // can safely perform catchup at that time (see |
| 273 | // code:#ProfCatchUpSynchronization). |
| 274 | // |
| 275 | ::FlushProcessWriteBuffers(); |
| 276 | } |
| 277 | #endif // !defined(DACCESS_COMPILE) |
| 278 | } |
| 279 | |
| 280 | |
| 281 | //--------------------------------------------------------------------------------------- |
| 282 | // ProfilingAPIUtility members |
| 283 | |
| 284 | |
| 285 | // See code:#LoadUnloadCallbackSynchronization. |
| 286 | CRITSEC_COOKIE ProfilingAPIUtility::s_csStatus = NULL; |
| 287 | |
| 288 | |
| 289 | SidBuffer * ProfilingAPIUtility::s_pSidBuffer = NULL; |
| 290 | |
| 291 | // ---------------------------------------------------------------------------- |
| 292 | // ProfilingAPIUtility::AppendSupplementaryInformation |
| 293 | // |
| 294 | // Description: |
| 295 | // Helper to the event logging functions to append the process ID and string |
| 296 | // resource ID to the end of the message. |
| 297 | // |
| 298 | // Arguments: |
| 299 | // * iStringResource - [in] String resource ID to append to message. |
| 300 | // * pString - [in/out] On input, the string to log so far. On output, the original |
| 301 | // string with the process ID info appended. |
| 302 | // |
| 303 | |
| 304 | // static |
| 305 | void ProfilingAPIUtility::AppendSupplementaryInformation(int iStringResource, SString * pString) |
| 306 | { |
| 307 | CONTRACTL |
| 308 | { |
| 309 | THROWS; |
| 310 | GC_TRIGGERS; |
| 311 | MODE_ANY; |
| 312 | |
| 313 | // This loads resource strings, which takes locks. |
| 314 | CAN_TAKE_LOCK; |
| 315 | } |
| 316 | CONTRACTL_END; |
| 317 | |
| 318 | StackSString supplementaryInformation; |
| 319 | |
| 320 | if (!supplementaryInformation.LoadResource( |
| 321 | CCompRC::Debugging, |
| 322 | IDS_PROF_SUPPLEMENTARY_INFO |
| 323 | )) |
| 324 | { |
| 325 | // Resource not found; should never happen. |
| 326 | return; |
| 327 | } |
| 328 | |
| 329 | pString->Append(W(" " )); |
| 330 | pString->AppendPrintf( |
| 331 | supplementaryInformation, |
| 332 | GetCurrentProcessId(), |
| 333 | iStringResource); |
| 334 | } |
| 335 | |
| 336 | //--------------------------------------------------------------------------------------- |
| 337 | // |
| 338 | // Helper function to log publicly-viewable errors about profiler loading and |
| 339 | // initialization. |
| 340 | // |
| 341 | // |
| 342 | // Arguments: |
| 343 | // * iStringResourceID - resource ID of string containing message to log |
| 344 | // * wEventType - same constant used in win32 to specify the type of event: |
| 345 | // usually EVENTLOG_ERROR_TYPE, EVENTLOG_WARNING_TYPE, or |
| 346 | // EVENTLOG_INFORMATION_TYPE |
| 347 | // * insertionArgs - 0 or more values to be inserted into the string to be logged |
| 348 | // (>0 only if iStringResourceID contains format arguments (%)). |
| 349 | // |
| 350 | |
| 351 | // static |
| 352 | void ProfilingAPIUtility::LogProfEventVA( |
| 353 | int iStringResourceID, |
| 354 | WORD wEventType, |
| 355 | va_list insertionArgs) |
| 356 | { |
| 357 | CONTRACTL |
| 358 | { |
| 359 | THROWS; |
| 360 | GC_TRIGGERS; |
| 361 | MODE_ANY; |
| 362 | |
| 363 | // This loads resource strings, which takes locks. |
| 364 | CAN_TAKE_LOCK; |
| 365 | } |
| 366 | CONTRACTL_END; |
| 367 | |
| 368 | StackSString messageFromResource; |
| 369 | StackSString messageToLog; |
| 370 | |
| 371 | if (!messageFromResource.LoadResource( |
| 372 | CCompRC::Debugging, |
| 373 | iStringResourceID |
| 374 | )) |
| 375 | { |
| 376 | // Resource not found; should never happen. |
| 377 | return; |
| 378 | } |
| 379 | |
| 380 | messageToLog.VPrintf(messageFromResource, insertionArgs); |
| 381 | |
| 382 | AppendSupplementaryInformation(iStringResourceID, &messageToLog); |
| 383 | |
| 384 | // Ouput debug strings for diagnostic messages. |
| 385 | WszOutputDebugString(messageToLog); |
| 386 | } |
| 387 | |
| 388 | // See code:ProfilingAPIUtility.LogProfEventVA for description of arguments. |
| 389 | // static |
| 390 | void ProfilingAPIUtility::LogProfError(int iStringResourceID, ...) |
| 391 | { |
| 392 | CONTRACTL |
| 393 | { |
| 394 | THROWS; |
| 395 | GC_TRIGGERS; |
| 396 | MODE_ANY; |
| 397 | |
| 398 | // This loads resource strings, which takes locks. |
| 399 | CAN_TAKE_LOCK; |
| 400 | } |
| 401 | CONTRACTL_END; |
| 402 | |
| 403 | va_list insertionArgs; |
| 404 | va_start(insertionArgs, iStringResourceID); |
| 405 | LogProfEventVA( |
| 406 | iStringResourceID, |
| 407 | EVENTLOG_ERROR_TYPE, |
| 408 | insertionArgs); |
| 409 | va_end(insertionArgs); |
| 410 | } |
| 411 | |
| 412 | // See code:ProfilingAPIUtility.LogProfEventVA for description of arguments. |
| 413 | // static |
| 414 | void ProfilingAPIUtility::LogProfInfo(int iStringResourceID, ...) |
| 415 | { |
| 416 | CONTRACTL |
| 417 | { |
| 418 | THROWS; |
| 419 | GC_TRIGGERS; |
| 420 | MODE_ANY; |
| 421 | |
| 422 | // This loads resource strings, which takes locks. |
| 423 | CAN_TAKE_LOCK; |
| 424 | } |
| 425 | CONTRACTL_END; |
| 426 | |
| 427 | va_list insertionArgs; |
| 428 | va_start(insertionArgs, iStringResourceID); |
| 429 | LogProfEventVA( |
| 430 | iStringResourceID, |
| 431 | EVENTLOG_INFORMATION_TYPE, |
| 432 | insertionArgs); |
| 433 | va_end(insertionArgs); |
| 434 | } |
| 435 | |
| 436 | #ifdef PROF_TEST_ONLY_FORCE_ELT |
| 437 | // Special forward-declarations of the profiling API's slow-path enter/leave/tailcall |
| 438 | // hooks. These need to be forward-declared here so that they may be referenced in |
| 439 | // InitializeProfiling() below solely for the debug-only, test-only code to allow |
| 440 | // enter/leave/tailcall to be turned on at startup without a profiler. See |
| 441 | // code:ProfControlBlock#TestOnlyELT |
| 442 | EXTERN_C void STDMETHODCALLTYPE ProfileEnterNaked(UINT_PTR clientData); |
| 443 | EXTERN_C void STDMETHODCALLTYPE ProfileLeaveNaked(UINT_PTR clientData); |
| 444 | EXTERN_C void STDMETHODCALLTYPE ProfileTailcallNaked(UINT_PTR clientData); |
| 445 | #endif //PROF_TEST_ONLY_FORCE_ELT |
| 446 | |
| 447 | // ---------------------------------------------------------------------------- |
| 448 | // ProfilingAPIUtility::InitializeProfiling |
| 449 | // |
| 450 | // This is the top-most level of profiling API initialization, and is called directly by |
| 451 | // EEStartupHelper() (in ceemain.cpp). This initializes internal structures relating to the |
| 452 | // Profiling API. This also orchestrates loading the profiler and initializing it (if |
| 453 | // its GUID is specified in the environment). |
| 454 | // |
| 455 | // Return Value: |
| 456 | // HRESULT indicating success or failure. This is generally very lenient about internal |
| 457 | // failures, as we don't want them to prevent the startup of the app: |
| 458 | // S_OK = Environment didn't request a profiler, or |
| 459 | // Environment did request a profiler, and it was loaded successfully |
| 460 | // S_FALSE = There was a problem loading the profiler, but that shouldn't prevent the app |
| 461 | // from starting up |
| 462 | // else (failure) = There was a serious problem that should be dealt with by the caller |
| 463 | // |
| 464 | // Notes: |
| 465 | // This function (or one of its callees) will log an error to the event log |
| 466 | // if there is a failure |
| 467 | // |
| 468 | // Assumptions: |
| 469 | // InitializeProfiling is called during startup, AFTER the host has initialized its |
| 470 | // settings and the config variables have been read, but BEFORE the finalizer thread |
| 471 | // has entered its first wait state. ASSERTs are placed in |
| 472 | // code:ProfilingAPIAttachDetach::Initialize (which is called by this function, and |
| 473 | // which depends on these assumptions) to verify. |
| 474 | |
| 475 | // static |
| 476 | HRESULT ProfilingAPIUtility::InitializeProfiling() |
| 477 | { |
| 478 | CONTRACTL |
| 479 | { |
| 480 | THROWS; |
| 481 | GC_TRIGGERS; |
| 482 | |
| 483 | // This causes events to be logged, which loads resource strings, |
| 484 | // which takes locks. |
| 485 | CAN_TAKE_LOCK; |
| 486 | |
| 487 | MODE_PREEMPTIVE; |
| 488 | } |
| 489 | CONTRACTL_END; |
| 490 | |
| 491 | InitializeLogging(); |
| 492 | |
| 493 | // NULL out / initialize members of the global profapi structure |
| 494 | g_profControlBlock.Init(); |
| 495 | |
| 496 | if (IsCompilationProcess()) |
| 497 | { |
| 498 | LOG((LF_CORPROF, LL_INFO10, "**PROF: Profiling disabled for ngen process.\n" )); |
| 499 | return S_OK; |
| 500 | } |
| 501 | |
| 502 | AttemptLoadProfilerForStartup(); |
| 503 | // For now, the return value from AttemptLoadProfilerForStartup is of no use to us. |
| 504 | // Any event has been logged already by AttemptLoadProfilerForStartup, and |
| 505 | // regardless of whether a profiler got loaded, we still need to continue. |
| 506 | |
| 507 | |
| 508 | #ifdef PROF_TEST_ONLY_FORCE_ELT |
| 509 | // Test-only, debug-only code to enable ELT on startup regardless of whether a |
| 510 | // startup profiler is loaded. See code:ProfControlBlock#TestOnlyELT. |
| 511 | DWORD dwEnableSlowELTHooks = CLRConfig::GetConfigValue(CLRConfig::INTERNAL_TestOnlyEnableSlowELTHooks); |
| 512 | if (dwEnableSlowELTHooks != 0) |
| 513 | { |
| 514 | (&g_profControlBlock)->fTestOnlyForceEnterLeave = TRUE; |
| 515 | SetJitHelperFunction(CORINFO_HELP_PROF_FCN_ENTER, (void *) ProfileEnterNaked); |
| 516 | SetJitHelperFunction(CORINFO_HELP_PROF_FCN_LEAVE, (void *) ProfileLeaveNaked); |
| 517 | SetJitHelperFunction(CORINFO_HELP_PROF_FCN_TAILCALL, (void *) ProfileTailcallNaked); |
| 518 | LOG((LF_CORPROF, LL_INFO10, "**PROF: Enabled test-only slow ELT hooks.\n" )); |
| 519 | } |
| 520 | #endif //PROF_TEST_ONLY_FORCE_ELT |
| 521 | |
| 522 | #ifdef PROF_TEST_ONLY_FORCE_OBJECT_ALLOCATED |
| 523 | // Test-only, debug-only code to enable ObjectAllocated callbacks on startup regardless of whether a |
| 524 | // startup profiler is loaded. See code:ProfControlBlock#TestOnlyObjectAllocated. |
| 525 | DWORD dwEnableObjectAllocated = CLRConfig::GetConfigValue(CLRConfig::INTERNAL_TestOnlyEnableObjectAllocatedHook); |
| 526 | if (dwEnableObjectAllocated != 0) |
| 527 | { |
| 528 | (&g_profControlBlock)->fTestOnlyForceObjectAllocated = TRUE; |
| 529 | LOG((LF_CORPROF, LL_INFO10, "**PROF: Enabled test-only object ObjectAllocated hooks.\n" )); |
| 530 | } |
| 531 | #endif //PROF_TEST_ONLY_FORCE_ELT |
| 532 | |
| 533 | |
| 534 | #ifdef _DEBUG |
| 535 | // Test-only, debug-only code to allow attaching profilers to call ICorProfilerInfo inteface, |
| 536 | // which would otherwise be disallowed for attaching profilers |
| 537 | DWORD dwTestOnlyEnableICorProfilerInfo = CLRConfig::GetConfigValue(CLRConfig::INTERNAL_TestOnlyEnableICorProfilerInfo); |
| 538 | if (dwTestOnlyEnableICorProfilerInfo != 0) |
| 539 | { |
| 540 | (&g_profControlBlock)->fTestOnlyEnableICorProfilerInfo = TRUE; |
| 541 | } |
| 542 | #endif // _DEBUG |
| 543 | |
| 544 | return S_OK; |
| 545 | } |
| 546 | |
| 547 | |
| 548 | // ---------------------------------------------------------------------------- |
| 549 | // ProfilingAPIUtility::ProfilerCLSIDFromString |
| 550 | // |
| 551 | // Description: |
| 552 | // Takes a string form of a CLSID (or progid, believe it or not), and returns the |
| 553 | // corresponding CLSID structure. |
| 554 | // |
| 555 | // Arguments: |
| 556 | // * wszClsid - [in / out] CLSID string to convert. This may also be a progid. This |
| 557 | // ensures our behavior is backward-compatible with previous CLR versions. I don't |
| 558 | // know why previous versions allowed the user to set a progid in the environment, |
| 559 | // but well whatever. On [out], this string is normalized in-place (e.g., |
| 560 | // double-quotes around progid are removed). |
| 561 | // * pClsid - [out] CLSID structure corresponding to wszClsid |
| 562 | // |
| 563 | // Return Value: |
| 564 | // HRESULT indicating success or failure. |
| 565 | // |
| 566 | // Notes: |
| 567 | // * An event is logged if there is a failure. |
| 568 | // |
| 569 | |
| 570 | // static |
| 571 | HRESULT ProfilingAPIUtility::ProfilerCLSIDFromString( |
| 572 | __inout_z LPWSTR wszClsid, |
| 573 | CLSID * pClsid) |
| 574 | { |
| 575 | CONTRACTL |
| 576 | { |
| 577 | THROWS; |
| 578 | GC_TRIGGERS; |
| 579 | |
| 580 | // This causes events to be logged, which loads resource strings, |
| 581 | // which takes locks. |
| 582 | CAN_TAKE_LOCK; |
| 583 | |
| 584 | MODE_PREEMPTIVE; |
| 585 | } |
| 586 | CONTRACTL_END; |
| 587 | |
| 588 | _ASSERTE(wszClsid != NULL); |
| 589 | _ASSERTE(pClsid != NULL); |
| 590 | |
| 591 | HRESULT hr; |
| 592 | |
| 593 | // Translate the string into a CLSID |
| 594 | if (*wszClsid == W('{')) |
| 595 | { |
| 596 | hr = IIDFromString(wszClsid, pClsid); |
| 597 | } |
| 598 | else |
| 599 | { |
| 600 | #ifndef FEATURE_PAL |
| 601 | WCHAR *szFrom, *szTo; |
| 602 | |
| 603 | #ifdef _PREFAST_ |
| 604 | #pragma warning(push) |
| 605 | #pragma warning(disable:26000) // "espX thinks there is an overflow here, but there isn't any" |
| 606 | #endif |
| 607 | for (szFrom=szTo=wszClsid; *szFrom; ) |
| 608 | { |
| 609 | if (*szFrom == W('"')) |
| 610 | { |
| 611 | ++szFrom; |
| 612 | continue; |
| 613 | } |
| 614 | *szTo++ = *szFrom++; |
| 615 | } |
| 616 | *szTo = 0; |
| 617 | hr = CLSIDFromProgID(wszClsid, pClsid); |
| 618 | #ifdef _PREFAST_ |
| 619 | #pragma warning(pop) |
| 620 | #endif /*_PREFAST_*/ |
| 621 | |
| 622 | #else // !FEATURE_PAL |
| 623 | // ProgID not supported on FEATURE_PAL |
| 624 | hr = E_INVALIDARG; |
| 625 | #endif // !FEATURE_PAL |
| 626 | } |
| 627 | |
| 628 | if (FAILED(hr)) |
| 629 | { |
| 630 | LOG(( |
| 631 | LF_CORPROF, |
| 632 | LL_INFO10, |
| 633 | "**PROF: Invalid CLSID or ProgID (%S). hr=0x%x.\n" , |
| 634 | wszClsid, |
| 635 | hr)); |
| 636 | ProfilingAPIUtility::LogProfError(IDS_E_PROF_BAD_CLSID, wszClsid, hr); |
| 637 | return hr; |
| 638 | } |
| 639 | |
| 640 | return S_OK; |
| 641 | } |
| 642 | |
| 643 | // ---------------------------------------------------------------------------- |
| 644 | // ProfilingAPIUtility::AttemptLoadProfilerForStartup |
| 645 | // |
| 646 | // Description: |
| 647 | // Checks environment or registry to see if the app is configured to run with a |
| 648 | // profiler loaded on startup. If so, this calls LoadProfiler() to load it up. |
| 649 | // |
| 650 | // Arguments: |
| 651 | // |
| 652 | // Return Value: |
| 653 | // * S_OK: Startup-profiler has been loaded |
| 654 | // * S_FALSE: No profiler is configured for startup load |
| 655 | // * else, HRESULT indicating failure that occurred |
| 656 | // |
| 657 | // Assumptions: |
| 658 | // * This should be called on startup, after g_profControlBlock is initialized, but |
| 659 | // before any attach infrastructure is initialized. This ensures we don't receive |
| 660 | // an attach request while startup-loading a profiler. |
| 661 | // |
| 662 | // Notes: |
| 663 | // * This or its callees will ensure an event is logged on failure (though will be |
| 664 | // silent if no profiler is configured for startup load (which causes S_FALSE to |
| 665 | // be returned) |
| 666 | // |
| 667 | |
| 668 | // static |
| 669 | HRESULT ProfilingAPIUtility::AttemptLoadProfilerForStartup() |
| 670 | { |
| 671 | CONTRACTL |
| 672 | { |
| 673 | THROWS; |
| 674 | GC_TRIGGERS; |
| 675 | |
| 676 | // This causes events to be logged, which loads resource strings, |
| 677 | // which takes locks. |
| 678 | CAN_TAKE_LOCK; |
| 679 | |
| 680 | MODE_PREEMPTIVE; |
| 681 | } |
| 682 | CONTRACTL_END; |
| 683 | |
| 684 | HRESULT hr; |
| 685 | |
| 686 | // Find out if profiling is enabled |
| 687 | DWORD fProfEnabled = 0; |
| 688 | |
| 689 | fProfEnabled = CLRConfig::GetConfigValue(CLRConfig::EXTERNAL_CORECLR_ENABLE_PROFILING); |
| 690 | |
| 691 | // If profiling is not enabled, return. |
| 692 | if (fProfEnabled == 0) |
| 693 | { |
| 694 | LOG((LF_CORPROF, LL_INFO10, "**PROF: Profiling not enabled.\n" )); |
| 695 | return S_FALSE; |
| 696 | } |
| 697 | |
| 698 | LOG((LF_CORPROF, LL_INFO10, "**PROF: Initializing Profiling Services.\n" )); |
| 699 | |
| 700 | // Get the CLSID of the profiler to CoCreate |
| 701 | NewArrayHolder<WCHAR> wszClsid(NULL); |
| 702 | NewArrayHolder<WCHAR> wszProfilerDLL(NULL); |
| 703 | |
| 704 | IfFailRet(CLRConfig::GetConfigValue(CLRConfig::EXTERNAL_CORECLR_PROFILER, &wszClsid)); |
| 705 | |
| 706 | #if defined(_TARGET_X86_) |
| 707 | IfFailRet(CLRConfig::GetConfigValue(CLRConfig::EXTERNAL_CORECLR_PROFILER_PATH_32, &wszProfilerDLL)); |
| 708 | #elif defined(_TARGET_AMD64_) |
| 709 | IfFailRet(CLRConfig::GetConfigValue(CLRConfig::EXTERNAL_CORECLR_PROFILER_PATH_64, &wszProfilerDLL)); |
| 710 | #endif |
| 711 | if(wszProfilerDLL == NULL) |
| 712 | { |
| 713 | IfFailRet(CLRConfig::GetConfigValue(CLRConfig::EXTERNAL_CORECLR_PROFILER_PATH, &wszProfilerDLL)); |
| 714 | } |
| 715 | |
| 716 | // If the environment variable doesn't exist, profiling is not enabled. |
| 717 | if (wszClsid == NULL) |
| 718 | { |
| 719 | LOG((LF_CORPROF, LL_INFO10, "**PROF: Profiling flag set, but required " |
| 720 | "environment variable does not exist.\n" )); |
| 721 | |
| 722 | LogProfError(IDS_E_PROF_NO_CLSID); |
| 723 | |
| 724 | return S_FALSE; |
| 725 | } |
| 726 | |
| 727 | if ((wszProfilerDLL != NULL) && (wcslen(wszProfilerDLL) >= MAX_LONGPATH)) |
| 728 | { |
| 729 | LOG((LF_CORPROF, LL_INFO10, "**PROF: Profiling flag set, but COR_PROFILER_PATH was not set properly.\n" )); |
| 730 | |
| 731 | LogProfError(IDS_E_PROF_BAD_PATH); |
| 732 | |
| 733 | return S_FALSE; |
| 734 | } |
| 735 | |
| 736 | #ifdef FEATURE_PAL |
| 737 | // If the environment variable doesn't exist, profiling is not enabled. |
| 738 | if (wszProfilerDLL == NULL) |
| 739 | { |
| 740 | LOG((LF_CORPROF, LL_INFO10, "**PROF: Profiling flag set, but required " |
| 741 | "environment variable does not exist.\n" )); |
| 742 | |
| 743 | LogProfError(IDS_E_PROF_BAD_PATH); |
| 744 | |
| 745 | return S_FALSE; |
| 746 | } |
| 747 | #endif // FEATURE_PAL |
| 748 | |
| 749 | CLSID clsid; |
| 750 | hr = ProfilingAPIUtility::ProfilerCLSIDFromString(wszClsid, &clsid); |
| 751 | if (FAILED(hr)) |
| 752 | { |
| 753 | // ProfilerCLSIDFromString already logged an event if there was a failure |
| 754 | return hr; |
| 755 | } |
| 756 | |
| 757 | hr = LoadProfiler( |
| 758 | kStartupLoad, |
| 759 | &clsid, |
| 760 | wszClsid, |
| 761 | wszProfilerDLL, |
| 762 | NULL, // No client data for startup load |
| 763 | 0); // No client data for startup load |
| 764 | if (FAILED(hr)) |
| 765 | { |
| 766 | // A failure in either the CLR or the profiler prevented it from |
| 767 | // loading. Event has been logged. Propagate hr |
| 768 | return hr; |
| 769 | } |
| 770 | |
| 771 | return S_OK; |
| 772 | } |
| 773 | |
| 774 | |
| 775 | //--------------------------------------------------------------------------------------- |
| 776 | // |
| 777 | // Performs lazy initialization that need not occur on startup, but does need to occur |
| 778 | // before trying to load a profiler. |
| 779 | // |
| 780 | // Return Value: |
| 781 | // HRESULT indicating success or failure. |
| 782 | // |
| 783 | |
| 784 | HRESULT ProfilingAPIUtility::PerformDeferredInit() |
| 785 | { |
| 786 | CONTRACTL |
| 787 | { |
| 788 | THROWS; |
| 789 | GC_TRIGGERS; |
| 790 | CAN_TAKE_LOCK; |
| 791 | MODE_ANY; |
| 792 | } |
| 793 | CONTRACTL_END; |
| 794 | |
| 795 | #ifdef FEATURE_PROFAPI_ATTACH_DETACH |
| 796 | // Initialize internal resources for detaching |
| 797 | HRESULT hr = ProfilingAPIDetach::Initialize(); |
| 798 | if (FAILED(hr)) |
| 799 | { |
| 800 | LOG(( |
| 801 | LF_CORPROF, |
| 802 | LL_ERROR, |
| 803 | "**PROF: Unable to initialize resources for detaching. hr=0x%x.\n" , |
| 804 | hr)); |
| 805 | return hr; |
| 806 | } |
| 807 | #endif // FEATURE_PROFAPI_ATTACH_DETACH |
| 808 | |
| 809 | if (s_csStatus == NULL) |
| 810 | { |
| 811 | s_csStatus = ClrCreateCriticalSection( |
| 812 | CrstProfilingAPIStatus, |
| 813 | (CrstFlags) (CRST_REENTRANCY | CRST_TAKEN_DURING_SHUTDOWN)); |
| 814 | if (s_csStatus == NULL) |
| 815 | { |
| 816 | return E_OUTOFMEMORY; |
| 817 | } |
| 818 | } |
| 819 | |
| 820 | return S_OK; |
| 821 | } |
| 822 | |
| 823 | // ---------------------------------------------------------------------------- |
| 824 | // ProfilingAPIUtility::LoadProfiler |
| 825 | // |
| 826 | // Description: |
| 827 | // Outermost common code for loading the profiler DLL. Both startup and attach code |
| 828 | // paths use this. |
| 829 | // |
| 830 | // Arguments: |
| 831 | // * loadType - Startup load or attach load? |
| 832 | // * pClsid - Profiler's CLSID |
| 833 | // * wszClsid - Profiler's CLSID (or progid) in string form, for event log messages |
| 834 | // * wszProfilerDLL - Profiler's DLL path |
| 835 | // * pvClientData - For attach loads, this is the client data the trigger wants to |
| 836 | // pass to the profiler DLL |
| 837 | // * cbClientData - For attach loads, size of client data in bytes |
| 838 | // * dwConcurrentGCWaitTimeoutInMs - Time out for wait operation on concurrent GC. Attach scenario only |
| 839 | // |
| 840 | // Return Value: |
| 841 | // HRESULT indicating success or failure of the load |
| 842 | // |
| 843 | // Notes: |
| 844 | // * On failure, this function or a callee will have logged an event |
| 845 | // |
| 846 | |
| 847 | // static |
| 848 | HRESULT ProfilingAPIUtility::LoadProfiler( |
| 849 | LoadType loadType, |
| 850 | const CLSID * pClsid, |
| 851 | LPCWSTR wszClsid, |
| 852 | LPCWSTR wszProfilerDLL, |
| 853 | LPVOID pvClientData, |
| 854 | UINT cbClientData, |
| 855 | DWORD dwConcurrentGCWaitTimeoutInMs) |
| 856 | { |
| 857 | CONTRACTL |
| 858 | { |
| 859 | THROWS; |
| 860 | GC_TRIGGERS; |
| 861 | |
| 862 | // This causes events to be logged, which loads resource strings, |
| 863 | // which takes locks. |
| 864 | CAN_TAKE_LOCK; |
| 865 | |
| 866 | MODE_ANY; |
| 867 | } |
| 868 | CONTRACTL_END; |
| 869 | |
| 870 | if (g_fEEShutDown) |
| 871 | { |
| 872 | return CORPROF_E_RUNTIME_UNINITIALIZED; |
| 873 | } |
| 874 | |
| 875 | enum ProfilerCompatibilityFlag |
| 876 | { |
| 877 | // Default: disable V2 profiler |
| 878 | kDisableV2Profiler = 0x0, |
| 879 | |
| 880 | // Enable V2 profilers |
| 881 | kEnableV2Profiler = 0x1, |
| 882 | |
| 883 | // Disable Profiling |
| 884 | kPreventLoad = 0x2, |
| 885 | }; |
| 886 | |
| 887 | ProfilerCompatibilityFlag profilerCompatibilityFlag = kDisableV2Profiler; |
| 888 | NewArrayHolder<WCHAR> wszProfilerCompatibilitySetting(NULL); |
| 889 | |
| 890 | if (loadType == kStartupLoad) |
| 891 | { |
| 892 | CLRConfig::GetConfigValue(CLRConfig::EXTERNAL_ProfAPI_ProfilerCompatibilitySetting, &wszProfilerCompatibilitySetting); |
| 893 | if (wszProfilerCompatibilitySetting != NULL) |
| 894 | { |
| 895 | if (SString::_wcsicmp(wszProfilerCompatibilitySetting, W("EnableV2Profiler" )) == 0) |
| 896 | { |
| 897 | profilerCompatibilityFlag = kEnableV2Profiler; |
| 898 | } |
| 899 | else if (SString::_wcsicmp(wszProfilerCompatibilitySetting, W("PreventLoad" )) == 0) |
| 900 | { |
| 901 | profilerCompatibilityFlag = kPreventLoad; |
| 902 | } |
| 903 | } |
| 904 | |
| 905 | if (profilerCompatibilityFlag == kPreventLoad) |
| 906 | { |
| 907 | LOG((LF_CORPROF, LL_INFO10, "**PROF: COMPlus_ProfAPI_ProfilerCompatibilitySetting is set to PreventLoad. " |
| 908 | "Profiler will not be loaded.\n" )); |
| 909 | |
| 910 | LogProfInfo(IDS_PROF_PROFILER_DISABLED, |
| 911 | CLRConfig::EXTERNAL_ProfAPI_ProfilerCompatibilitySetting.name, |
| 912 | wszProfilerCompatibilitySetting.GetValue(), |
| 913 | wszClsid); |
| 914 | |
| 915 | return S_OK; |
| 916 | } |
| 917 | } |
| 918 | |
| 919 | HRESULT hr; |
| 920 | |
| 921 | hr = PerformDeferredInit(); |
| 922 | if (FAILED(hr)) |
| 923 | { |
| 924 | LOG(( |
| 925 | LF_CORPROF, |
| 926 | LL_ERROR, |
| 927 | "**PROF: ProfilingAPIUtility::PerformDeferredInit failed. hr=0x%x.\n" , |
| 928 | hr)); |
| 929 | LogProfError(IDS_E_PROF_INTERNAL_INIT, wszClsid, hr); |
| 930 | return hr; |
| 931 | } |
| 932 | |
| 933 | // Valid loadType? |
| 934 | _ASSERTE((loadType == kStartupLoad) || (loadType == kAttachLoad)); |
| 935 | |
| 936 | // If a nonzero client data size is reported, there'd better be client data! |
| 937 | _ASSERTE((cbClientData == 0) || (pvClientData != NULL)); |
| 938 | |
| 939 | // Client data is currently only specified on attach |
| 940 | _ASSERTE((pvClientData == NULL) || (loadType == kAttachLoad)); |
| 941 | |
| 942 | // Don't be telling me to load a profiler if there already is one. |
| 943 | _ASSERTE(g_profControlBlock.curProfStatus.Get() == kProfStatusNone); |
| 944 | |
| 945 | // Create the ProfToEE interface to provide to the profiling services |
| 946 | NewHolder<ProfToEEInterfaceImpl> pProfEE(new (nothrow) ProfToEEInterfaceImpl()); |
| 947 | if (pProfEE == NULL) |
| 948 | { |
| 949 | LOG((LF_CORPROF, LL_ERROR, "**PROF: Unable to allocate ProfToEEInterfaceImpl.\n" )); |
| 950 | LogProfError(IDS_E_PROF_INTERNAL_INIT, wszClsid, E_OUTOFMEMORY); |
| 951 | return E_OUTOFMEMORY; |
| 952 | } |
| 953 | |
| 954 | // Initialize the interface |
| 955 | hr = pProfEE->Init(); |
| 956 | if (FAILED(hr)) |
| 957 | { |
| 958 | LOG((LF_CORPROF, LL_ERROR, "**PROF: ProfToEEInterface::Init failed.\n" )); |
| 959 | LogProfError(IDS_E_PROF_INTERNAL_INIT, wszClsid, hr); |
| 960 | return hr; |
| 961 | } |
| 962 | |
| 963 | // Provide the newly created and inited interface |
| 964 | LOG((LF_CORPROF, LL_INFO10, "**PROF: Profiling code being provided with EE interface.\n" )); |
| 965 | |
| 966 | // Create a new EEToProf object |
| 967 | NewHolder<EEToProfInterfaceImpl> pEEProf(new (nothrow) EEToProfInterfaceImpl()); |
| 968 | if (pEEProf == NULL) |
| 969 | { |
| 970 | LOG((LF_CORPROF, LL_ERROR, "**PROF: Unable to allocate EEToProfInterfaceImpl.\n" )); |
| 971 | LogProfError(IDS_E_PROF_INTERNAL_INIT, wszClsid, E_OUTOFMEMORY); |
| 972 | return E_OUTOFMEMORY; |
| 973 | } |
| 974 | |
| 975 | #ifdef FEATURE_PROFAPI_ATTACH_DETACH |
| 976 | // We're about to load the profiler, so first make sure we successfully create the |
| 977 | // DetachThread and abort the load of the profiler if we can't. This ensures we don't |
| 978 | // load a profiler unless we're prepared to detach it later. |
| 979 | hr = ProfilingAPIDetach::CreateDetachThread(); |
| 980 | if (FAILED(hr)) |
| 981 | { |
| 982 | LOG(( |
| 983 | LF_CORPROF, |
| 984 | LL_ERROR, |
| 985 | "**PROF: Unable to create DetachThread. hr=0x%x.\n" , |
| 986 | hr)); |
| 987 | ProfilingAPIUtility::LogProfError(IDS_E_PROF_INTERNAL_INIT, wszClsid, hr); |
| 988 | return hr; |
| 989 | } |
| 990 | #endif // FEATURE_PROFAPI_ATTACH_DETACH |
| 991 | |
| 992 | // Initialize internal state of our EEToProfInterfaceImpl. This also loads the |
| 993 | // profiler itself, but does not yet call its Initalize() callback |
| 994 | hr = pEEProf->Init(pProfEE, pClsid, wszClsid, wszProfilerDLL, (loadType == kAttachLoad), dwConcurrentGCWaitTimeoutInMs); |
| 995 | if (FAILED(hr)) |
| 996 | { |
| 997 | LOG((LF_CORPROF, LL_ERROR, "**PROF: EEToProfInterfaceImpl::Init failed.\n" )); |
| 998 | // EEToProfInterfaceImpl::Init logs an event log error on failure |
| 999 | return hr; |
| 1000 | } |
| 1001 | |
| 1002 | // EEToProfInterfaceImpl::Init takes over the ownership of pProfEE when Init succeeds, and |
| 1003 | // EEToProfInterfaceImpl::~EEToProfInterfaceImpl is responsible for releasing the resource pointed |
| 1004 | // by pProfEE. Calling SuppressRelease here is necessary to avoid double release that |
| 1005 | // the resource pointed by pProfEE are released by both pProfEE and pEEProf's destructor. |
| 1006 | pProfEE.SuppressRelease(); |
| 1007 | pProfEE = NULL; |
| 1008 | |
| 1009 | if (loadType == kAttachLoad) // V4 profiler from attach |
| 1010 | { |
| 1011 | // Profiler must support ICorProfilerCallback3 to be attachable |
| 1012 | if (!pEEProf->IsCallback3Supported()) |
| 1013 | { |
| 1014 | LogProfError(IDS_E_PROF_NOT_ATTACHABLE, wszClsid); |
| 1015 | return CORPROF_E_PROFILER_NOT_ATTACHABLE; |
| 1016 | } |
| 1017 | } |
| 1018 | else if (!pEEProf->IsCallback3Supported()) // V2 profiler from startup |
| 1019 | { |
| 1020 | if (profilerCompatibilityFlag == kDisableV2Profiler) |
| 1021 | { |
| 1022 | LOG((LF_CORPROF, LL_INFO10, "**PROF: COMPlus_ProfAPI_ProfilerCompatibilitySetting is set to DisableV2Profiler (the default). " |
| 1023 | "V2 profilers are not allowed, so that the configured V2 profiler is going to be unloaded.\n" )); |
| 1024 | |
| 1025 | LogProfInfo(IDS_PROF_V2PROFILER_DISABLED, wszClsid); |
| 1026 | return S_OK; |
| 1027 | } |
| 1028 | |
| 1029 | _ASSERTE(profilerCompatibilityFlag == kEnableV2Profiler); |
| 1030 | |
| 1031 | LOG((LF_CORPROF, LL_INFO10, "**PROF: COMPlus_ProfAPI_ProfilerCompatibilitySetting is set to EnableV2Profiler. " |
| 1032 | "The configured V2 profiler is going to be initialized.\n" )); |
| 1033 | |
| 1034 | LogProfInfo(IDS_PROF_V2PROFILER_ENABLED, |
| 1035 | CLRConfig::EXTERNAL_ProfAPI_ProfilerCompatibilitySetting.name, |
| 1036 | wszProfilerCompatibilitySetting.GetValue(), |
| 1037 | wszClsid); |
| 1038 | } |
| 1039 | |
| 1040 | _ASSERTE(s_csStatus != NULL); |
| 1041 | { |
| 1042 | // All modification of the profiler's status and |
| 1043 | // g_profControlBlock.pProfInterface need to be serialized against each other, |
| 1044 | // in particular, this code should be serialized against detach and unloading |
| 1045 | // code. |
| 1046 | CRITSEC_Holder csh(s_csStatus); |
| 1047 | |
| 1048 | // We've successfully allocated and initialized the callback wrapper object and the |
| 1049 | // Info interface implementation objects. The profiler DLL is therefore also |
| 1050 | // successfully loaded (but not yet Initialized). Transfer ownership of the |
| 1051 | // callback wrapper object to globals (thus suppress a release when the local |
| 1052 | // vars go out of scope). |
| 1053 | // |
| 1054 | // Setting this state now enables us to call into the profiler's Initialize() |
| 1055 | // callback (which we do immediately below), and have it successfully call |
| 1056 | // back into us via the Info interface (ProfToEEInterfaceImpl) to perform its |
| 1057 | // initialization. |
| 1058 | g_profControlBlock.pProfInterface = pEEProf.GetValue(); |
| 1059 | pEEProf.SuppressRelease(); |
| 1060 | pEEProf = NULL; |
| 1061 | |
| 1062 | // Set global status to reflect the proper type of Init we're doing (attach vs |
| 1063 | // startup) |
| 1064 | g_profControlBlock.curProfStatus.Set( |
| 1065 | (loadType == kStartupLoad) ? |
| 1066 | kProfStatusInitializingForStartupLoad : |
| 1067 | kProfStatusInitializingForAttachLoad); |
| 1068 | } |
| 1069 | |
| 1070 | // Now that the profiler is officially loaded and in Init status, call into the |
| 1071 | // profiler's appropriate Initialize() callback. Note that if the profiler fails this |
| 1072 | // call, we should abort the rest of the profiler loading, and reset our state so we |
| 1073 | // appear as if we never attempted to load the profiler. |
| 1074 | |
| 1075 | if (loadType == kStartupLoad) |
| 1076 | { |
| 1077 | hr = g_profControlBlock.pProfInterface->Initialize(); |
| 1078 | } |
| 1079 | else |
| 1080 | { |
| 1081 | _ASSERTE(loadType == kAttachLoad); |
| 1082 | _ASSERTE(g_profControlBlock.pProfInterface->IsCallback3Supported()); |
| 1083 | hr = g_profControlBlock.pProfInterface->InitializeForAttach(pvClientData, cbClientData); |
| 1084 | } |
| 1085 | |
| 1086 | if (FAILED(hr)) |
| 1087 | { |
| 1088 | LOG(( |
| 1089 | LF_CORPROF, |
| 1090 | LL_INFO10, |
| 1091 | "**PROF: Profiler failed its Initialize callback. hr=0x%x.\n" , |
| 1092 | hr)); |
| 1093 | |
| 1094 | // If we timed out due to waiting on concurrent GC to finish, it is very likely this is |
| 1095 | // the reason InitializeForAttach callback failed even though we cannot be sure and we cannot |
| 1096 | // cannot assume hr is going to be CORPROF_E_TIMEOUT_WAITING_FOR_CONCURRENT_GC. |
| 1097 | // The best we can do in this case is to report this failure anyway. |
| 1098 | if (g_profControlBlock.pProfInterface->HasTimedOutWaitingForConcurrentGC()) |
| 1099 | { |
| 1100 | ProfilingAPIUtility::LogProfError(IDS_E_PROF_TIMEOUT_WAITING_FOR_CONCURRENT_GC, dwConcurrentGCWaitTimeoutInMs, wszClsid); |
| 1101 | } |
| 1102 | |
| 1103 | // Check for known failure types, to customize the event we log |
| 1104 | if ((loadType == kAttachLoad) && |
| 1105 | ((hr == CORPROF_E_PROFILER_NOT_ATTACHABLE) || (hr == E_NOTIMPL))) |
| 1106 | { |
| 1107 | _ASSERTE(g_profControlBlock.pProfInterface->IsCallback3Supported()); |
| 1108 | |
| 1109 | // Profiler supports ICorProfilerCallback3, but explicitly doesn't support |
| 1110 | // Attach loading. So log specialized event |
| 1111 | LogProfError(IDS_E_PROF_NOT_ATTACHABLE, wszClsid); |
| 1112 | |
| 1113 | // Normalize (CORPROF_E_PROFILER_NOT_ATTACHABLE || E_NOTIMPL) down to |
| 1114 | // CORPROF_E_PROFILER_NOT_ATTACHABLE |
| 1115 | hr = CORPROF_E_PROFILER_NOT_ATTACHABLE; |
| 1116 | } |
| 1117 | else if (hr == CORPROF_E_PROFILER_CANCEL_ACTIVATION) |
| 1118 | { |
| 1119 | // Profiler didn't encounter a bad error, but is voluntarily choosing not to |
| 1120 | // profile this runtime. Profilers that need to set system environment |
| 1121 | // variables to be able to profile services may use this HRESULT to avoid |
| 1122 | // profiling all the other managed apps on the box. |
| 1123 | LogProfInfo(IDS_PROF_CANCEL_ACTIVATION, wszClsid); |
| 1124 | } |
| 1125 | else |
| 1126 | { |
| 1127 | LogProfError(IDS_E_PROF_INIT_CALLBACK_FAILED, wszClsid, hr); |
| 1128 | } |
| 1129 | |
| 1130 | // Profiler failed; reset everything. This will automatically reset |
| 1131 | // g_profControlBlock and will unload the profiler's DLL. |
| 1132 | TerminateProfiling(); |
| 1133 | return hr; |
| 1134 | } |
| 1135 | |
| 1136 | #ifdef FEATURE_MULTICOREJIT |
| 1137 | |
| 1138 | // Disable multicore JIT when profiling is enabled |
| 1139 | if (g_profControlBlock.dwEventMask & COR_PRF_MONITOR_JIT_COMPILATION) |
| 1140 | { |
| 1141 | MulticoreJitManager::DisableMulticoreJit(); |
| 1142 | } |
| 1143 | |
| 1144 | #endif |
| 1145 | |
| 1146 | // Indicate that profiling is properly initialized. On an attach-load, this will |
| 1147 | // force a FlushStoreBuffers(), which is important for catch-up synchronization (see |
| 1148 | // code:#ProfCatchUpSynchronization) |
| 1149 | g_profControlBlock.curProfStatus.Set(kProfStatusActive); |
| 1150 | |
| 1151 | LOG(( |
| 1152 | LF_CORPROF, |
| 1153 | LL_INFO10, |
| 1154 | "**PROF: Profiler successfully loaded and initialized.\n" )); |
| 1155 | |
| 1156 | LogProfInfo(IDS_PROF_LOAD_COMPLETE, wszClsid); |
| 1157 | |
| 1158 | LOG((LF_CORPROF, LL_INFO10, "**PROF: Profiler created and enabled.\n" )); |
| 1159 | |
| 1160 | if (loadType == kStartupLoad) |
| 1161 | { |
| 1162 | // For startup profilers only: If the profiler is interested in tracking GC |
| 1163 | // events, then we must disable concurrent GC since concurrent GC can allocate |
| 1164 | // and kill objects without relocating and thus not doing a heap walk. |
| 1165 | if (CORProfilerTrackGC()) |
| 1166 | { |
| 1167 | LOG((LF_CORPROF, LL_INFO10, "**PROF: Turning off concurrent GC at startup.\n" )); |
| 1168 | // Previously we would use SetGCConcurrent(0) to indicate to the GC that it shouldn't even |
| 1169 | // attempt to use concurrent GC. The standalone GC feature create a cycle during startup, |
| 1170 | // where the profiler couldn't set startup flags for the GC. To overcome this, we call |
| 1171 | // TempraryDisableConcurrentGC and never enable it again. This has a perf cost, since the |
| 1172 | // GC will create concurrent GC data structures, but it is acceptable in the context of |
| 1173 | // this kind of profiling. |
| 1174 | GCHeapUtilities::GetGCHeap()->TemporaryDisableConcurrentGC(); |
| 1175 | LOG((LF_CORPROF, LL_INFO10, "**PROF: Concurrent GC has been turned off at startup.\n" )); |
| 1176 | } |
| 1177 | } |
| 1178 | |
| 1179 | if (loadType == kAttachLoad) |
| 1180 | { |
| 1181 | // #ProfCatchUpSynchronization |
| 1182 | // |
| 1183 | // Now that callbacks are enabled (and all threads are aware), tell an attaching |
| 1184 | // profiler that it's safe to request catchup information. |
| 1185 | // |
| 1186 | // There's a race we're preventing that's worthwhile to spell out. An attaching |
| 1187 | // profiler should be able to get a COMPLETE set of data through the use of |
| 1188 | // callbacks unioned with the use of catch-up enumeration Info functions. To |
| 1189 | // achieve this, we must ensure that there is no "hole"--any new data the |
| 1190 | // profiler seeks must be available from a callback or a catch-up info function |
| 1191 | // (or both, as dupes are ok). That means that: |
| 1192 | // |
| 1193 | // * callbacks must be enabled on other threads NO LATER THAN the profiler begins |
| 1194 | // requesting catch-up information on this thread |
| 1195 | // * Abbreviate: callbacks <= catch-up. |
| 1196 | // |
| 1197 | // Otherwise, if catch-up < callbacks, then it would be possible to have this: |
| 1198 | // |
| 1199 | // * catch-up < new data arrives < callbacks. |
| 1200 | // |
| 1201 | // In this nightmare scenario, the new data would not be accessible from the |
| 1202 | // catch-up calls made by the profiler (cuz the profiler made the calls too |
| 1203 | // early) or the callbacks made into the profiler (cuz the callbacks were enabled |
| 1204 | // too late). That's a hole, and that's bad. So we ensure callbacks <= catch-up |
| 1205 | // by the following order of operations: |
| 1206 | // |
| 1207 | // * This thread: |
| 1208 | // * a: Set (volatile) currentProfStatus = kProfStatusActive (done above) and |
| 1209 | // event mask bits (profiler did this in Initialize() callback above, |
| 1210 | // when it called SetEventMask) |
| 1211 | // * b: Flush CPU buffers (done automatically when we set status to |
| 1212 | // kProfStatusActive) |
| 1213 | // * c: CLR->Profiler call: ProfilerAttachComplete() (below). Inside this |
| 1214 | // call: |
| 1215 | // * Profiler->CLR calls: Catch-up Info functions |
| 1216 | // * Other threads: |
| 1217 | // * a: New data (thread, JIT info, etc.) is created |
| 1218 | // * b: This new data is now available to a catch-up Info call |
| 1219 | // * c: currentProfStatus & event mask bits are accurately visible to thread |
| 1220 | // in determining whether to make a callback |
| 1221 | // * d: Read currentProfStatus & event mask bits and make callback |
| 1222 | // (CLR->Profiler) if necessary |
| 1223 | // |
| 1224 | // So as long as OtherThreads.c <= ThisThread.c we're ok. This means other |
| 1225 | // threads must be able to get a clean read of the (volatile) currentProfStatus & |
| 1226 | // event mask bits BEFORE this thread calls ProfilerAttachComplete(). Use of the |
| 1227 | // "volatile" keyword ensures that compiler optimizations and (w/ VC2005+ |
| 1228 | // compilers) the CPU's instruction reordering optimizations at runtime are |
| 1229 | // disabled enough such that they do not hinder the order above. Use of |
| 1230 | // FlushStoreBuffers() ensures that multiple caches on multiple CPUs do not |
| 1231 | // hinder the order above (by causing other threads to get stale reads of the |
| 1232 | // volatiles). |
| 1233 | // |
| 1234 | // For more information about catch-up enumerations and exactly which entities, |
| 1235 | // and which stage of loading, are permitted to appear in the enumerations, see |
| 1236 | // code:ProfilerFunctionEnum::Init#ProfilerEnumGeneral |
| 1237 | |
| 1238 | { |
| 1239 | BEGIN_PIN_PROFILER(CORProfilerPresent()); |
| 1240 | g_profControlBlock.pProfInterface->ProfilerAttachComplete(); |
| 1241 | END_PIN_PROFILER(); |
| 1242 | } |
| 1243 | } |
| 1244 | return S_OK; |
| 1245 | } |
| 1246 | |
| 1247 | |
| 1248 | //--------------------------------------------------------------------------------------- |
| 1249 | // |
| 1250 | // This is the top-most level of profiling API teardown, and is called directly by |
| 1251 | // EEShutDownHelper() (in ceemain.cpp). This cleans up internal structures relating to |
| 1252 | // the Profiling API. If we're not in process teardown, then this also releases the |
| 1253 | // profiler COM object and frees the profiler DLL |
| 1254 | // |
| 1255 | |
| 1256 | // static |
| 1257 | void ProfilingAPIUtility::TerminateProfiling() |
| 1258 | { |
| 1259 | CONTRACTL |
| 1260 | { |
| 1261 | NOTHROW; |
| 1262 | GC_TRIGGERS; |
| 1263 | MODE_ANY; |
| 1264 | CAN_TAKE_LOCK; |
| 1265 | } |
| 1266 | CONTRACTL_END; |
| 1267 | |
| 1268 | if (IsAtProcessExit()) |
| 1269 | { |
| 1270 | // We're tearing down the process so don't bother trying to clean everything up. |
| 1271 | // There's no reliable way to verify other threads won't be trying to re-enter |
| 1272 | // the profiler anyway, so cleaning up here could cause AVs. |
| 1273 | return; |
| 1274 | } |
| 1275 | |
| 1276 | _ASSERTE(s_csStatus != NULL); |
| 1277 | { |
| 1278 | // We're modifying status and possibly unloading the profiler DLL below, so |
| 1279 | // serialize this code with any other loading / unloading / detaching code. |
| 1280 | CRITSEC_Holder csh(s_csStatus); |
| 1281 | |
| 1282 | |
| 1283 | #ifdef FEATURE_PROFAPI_ATTACH_DETACH |
| 1284 | if (ProfilingAPIDetach::GetEEToProfPtr() != NULL) |
| 1285 | { |
| 1286 | // The profiler is still being referenced by |
| 1287 | // ProfilingAPIDetach::s_profilerDetachInfo, so don't try to release and |
| 1288 | // unload it. This can happen if Shutdown and Detach race, and Shutdown wins. |
| 1289 | // For example, we could be called as part of Shutdown, but the profiler |
| 1290 | // called RequestProfilerDetach near shutdown time as well (or even earlier |
| 1291 | // but remains un-evacuated as shutdown begins). Whatever the cause, just |
| 1292 | // don't unload the profiler here (as part of shutdown), and let the Detach |
| 1293 | // Thread deal with it (if it gets the chance). |
| 1294 | // |
| 1295 | // Note: Since this check occurs inside s_csStatus, we don't have to worry |
| 1296 | // that ProfilingAPIDetach::GetEEToProfPtr() will suddenly change during the |
| 1297 | // code below. |
| 1298 | // |
| 1299 | // FUTURE: For reattach-with-neutered-profilers feature crew, change the |
| 1300 | // above to scan through list of detaching profilers to make sure none of |
| 1301 | // them give a GetEEToProfPtr() equal to g_profControlBlock.pProfInterface. |
| 1302 | return; |
| 1303 | } |
| 1304 | |
| 1305 | if (g_profControlBlock.curProfStatus.Get() == kProfStatusActive) |
| 1306 | { |
| 1307 | g_profControlBlock.curProfStatus.Set(kProfStatusDetaching); |
| 1308 | |
| 1309 | // Profiler was active when TerminateProfiling() was called, so we're unloading |
| 1310 | // it due to shutdown. But other threads may still be trying to enter profiler |
| 1311 | // callbacks (e.g., ClassUnloadStarted() can get called during shutdown). Now |
| 1312 | // that the status has been changed to kProfStatusDetaching, no new threads will |
| 1313 | // attempt to enter the profiler. But use the detach evacuation counters to see |
| 1314 | // if other threads already began to enter the profiler. |
| 1315 | if (!ProfilingAPIDetach::IsProfilerEvacuated()) |
| 1316 | { |
| 1317 | // Other threads might be entering the profiler, so just skip cleanup |
| 1318 | return; |
| 1319 | } |
| 1320 | } |
| 1321 | #endif // FEATURE_PROFAPI_ATTACH_DETACH |
| 1322 | |
| 1323 | // If we have a profiler callback wrapper and / or info implementation |
| 1324 | // active, then terminate them. |
| 1325 | |
| 1326 | if (g_profControlBlock.pProfInterface.Load() != NULL) |
| 1327 | { |
| 1328 | // This destructor takes care of releasing the profiler's ICorProfilerCallback* |
| 1329 | // interface, and unloading the DLL when we're not in process teardown. |
| 1330 | delete g_profControlBlock.pProfInterface; |
| 1331 | g_profControlBlock.pProfInterface.Store(NULL); |
| 1332 | } |
| 1333 | |
| 1334 | // NOTE: Intentionally not deleting s_pSidBuffer. Doing so can cause annoying races |
| 1335 | // with other threads that lazily create and initialize it when needed. (Example: |
| 1336 | // it's used to fill out the "User" field of profiler event log entries.) Keeping |
| 1337 | // s_pSidBuffer around after a profiler detaches and before a new one attaches |
| 1338 | // consumes a bit more memory unnecessarily, but it'll get paged out if another |
| 1339 | // profiler doesn't attach. |
| 1340 | |
| 1341 | // NOTE: Similarly, intentionally not destroying / NULLing s_csStatus. If |
| 1342 | // s_csStatus is already initialized, we can reuse it each time we do another |
| 1343 | // attach / detach, so no need to destroy it. |
| 1344 | |
| 1345 | // If we disabled concurrent GC and somehow failed later during the initialization |
| 1346 | if (g_profControlBlock.fConcurrentGCDisabledForAttach) |
| 1347 | { |
| 1348 | // We know for sure GC has been fully initialized as we've turned off concurrent GC before |
| 1349 | _ASSERTE(IsGarbageCollectorFullyInitialized()); |
| 1350 | GCHeapUtilities::GetGCHeap()->TemporaryEnableConcurrentGC(); |
| 1351 | g_profControlBlock.fConcurrentGCDisabledForAttach = FALSE; |
| 1352 | } |
| 1353 | |
| 1354 | // #ProfileResetSessionStatus Reset all the status variables that are for the current |
| 1355 | // profiling attach session. |
| 1356 | // When you are adding new status in g_profControlBlock, you need to think about whether |
| 1357 | // your new status is per-session, or consistent across sessions |
| 1358 | g_profControlBlock.ResetPerSessionStatus(); |
| 1359 | |
| 1360 | g_profControlBlock.curProfStatus.Set(kProfStatusNone); |
| 1361 | } |
| 1362 | } |
| 1363 | |
| 1364 | #ifndef FEATURE_PAL |
| 1365 | |
| 1366 | // ---------------------------------------------------------------------------- |
| 1367 | // ProfilingAPIUtility::GetCurrentProcessUserSid |
| 1368 | // |
| 1369 | // Description: |
| 1370 | // Generates a SID of the current user from the current process's token. SID is |
| 1371 | // returned in an [out] param, and is also cached for future use. The SID is used for |
| 1372 | // two purposes: event log entries (for filling out the User field) and the ACL used |
| 1373 | // on the globally named pipe object for attaching profilers. |
| 1374 | // |
| 1375 | // Arguments: |
| 1376 | // * ppsid - [out] Generated (or cached) SID |
| 1377 | // |
| 1378 | // Return Value: |
| 1379 | // HRESULT indicating success or failure. |
| 1380 | // |
| 1381 | |
| 1382 | // static |
| 1383 | HRESULT ProfilingAPIUtility::GetCurrentProcessUserSid(PSID * ppsid) |
| 1384 | { |
| 1385 | CONTRACTL |
| 1386 | { |
| 1387 | NOTHROW; |
| 1388 | GC_NOTRIGGER; |
| 1389 | MODE_ANY; |
| 1390 | } |
| 1391 | CONTRACTL_END; |
| 1392 | |
| 1393 | if (s_pSidBuffer == NULL) |
| 1394 | { |
| 1395 | HRESULT hr; |
| 1396 | NewHolder<SidBuffer> pSidBuffer(new (nothrow) SidBuffer); |
| 1397 | if (pSidBuffer == NULL) |
| 1398 | { |
| 1399 | return E_OUTOFMEMORY; |
| 1400 | } |
| 1401 | |
| 1402 | // This gets the SID of the user from the process token |
| 1403 | hr = pSidBuffer->InitFromProcessUserNoThrow(GetCurrentProcessId()); |
| 1404 | if (FAILED(hr)) |
| 1405 | { |
| 1406 | return hr; |
| 1407 | } |
| 1408 | |
| 1409 | if (FastInterlockCompareExchangePointer( |
| 1410 | &s_pSidBuffer, |
| 1411 | pSidBuffer.GetValue(), |
| 1412 | NULL) == NULL) |
| 1413 | { |
| 1414 | // Lifetime successfully transferred to s_pSidBuffer, so don't delete it here |
| 1415 | pSidBuffer.SuppressRelease(); |
| 1416 | } |
| 1417 | } |
| 1418 | |
| 1419 | _ASSERTE(s_pSidBuffer != NULL); |
| 1420 | _ASSERTE(s_pSidBuffer->GetSid().RawSid() != NULL); |
| 1421 | *ppsid = s_pSidBuffer->GetSid().RawSid(); |
| 1422 | return S_OK; |
| 1423 | } |
| 1424 | |
| 1425 | #endif // !FEATURE_PAL |
| 1426 | |
| 1427 | #endif // PROFILING_SUPPORTED |
| 1428 | |