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.
9 // Implementation of helper classes used for miscellaneous purposes within the profiling
12 // ======================================================================================
15 // #LoadUnloadCallbackSynchronization
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).
25 // The synchronization protects the following data:
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.
34 // SYNCHRONIZATION COMPONENTS
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
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
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
56 // BEGIN_PIN_PROFILER(CORProfilerTrackAppDomainLoads());
57 // g_profControlBlock.pProfInterface->AppDomainCreationStarted(MyAppDomainID);
58 // END_PIN_PROFILER();
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
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
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.
74 // READER / WRITER COORDINATION
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()).
82 // At the same time, a profiler DLL is never unloaded (nor
83 // g_profControlBlock.pProfInterface deleted and NULLed out) UNLESS the writer performs
85 // * (a) Set the profiler's status to a non-active state like kProfStatusDetaching or
87 // * (b) Call FlushProcessWriteBuffers()
88 // * (c) Grab thread store lock, iterate through all threads, and verify each per-thread
89 // evacuation counter is zero.
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:ProfilingAPIUtility::IsProfilerEvacuated.
99 // WHEN ARE BEGIN/END_PIN_PROFILER REQUIRED?
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
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.
129 #ifdef PROFILING_SUPPORTED
131 #include "eeprofinterfaces.h"
132 #include "eetoprofinterfaceimpl.h"
133 #include "eetoprofinterfaceimpl.inl"
135 #include "proftoeeinterfaceimpl.h"
136 #include "proftoeeinterfaceimpl.inl"
137 #include "profilinghelper.h"
138 #include "profilinghelper.inl"
139 #include "eemessagebox.h"
142 #ifdef FEATURE_PROFAPI_ATTACH_DETACH
143 #include "profdetach.h"
144 #endif // FEATURE_PROFAPI_ATTACH_DETACH
146 #include "utilcode.h"
149 #include "securitywrapper.h"
150 #endif // !FEATURE_PAL
152 //---------------------------------------------------------------------------------------
153 // Normally, this would go in profilepriv.inl, but it's not easily inlineable because of
154 // the use of BEGIN/END_PIN_PROFILER
157 // TRUE iff security transparency checks in full trust assemblies should be disabled
158 // due to the profiler.
160 BOOL CORProfilerBypassSecurityChecks()
171 BEGIN_PIN_PROFILER(CORProfilerPresent());
173 // V2 profiler binaries, for compatibility purposes, should bypass transparency
174 // checks in full trust assemblies.
175 if (!(&g_profControlBlock)->pProfInterface->IsCallback3Supported())
178 // V4 profiler binaries must opt in to bypasssing transparency checks in full trust
180 if (((&g_profControlBlock)->dwEventMask & COR_PRF_DISABLE_TRANSPARENCY_CHECKS_UNDER_FULL_TRUST) != 0)
186 // All other cases, including no profiler loaded at all: Don't bypass
190 // ----------------------------------------------------------------------------
191 // CurrentProfilerStatus methods
194 //---------------------------------------------------------------------------------------
196 // Updates the value indicating the profiler's current status
199 // profStatus - New value (from enum ProfilerStatus) to set.
202 // Sets the status under a lock, and performs a debug-only check to verify that the
203 // status transition is a legal one. Also performs a FlushStoreBuffers() after
204 // changing the status when necessary.
207 void CurrentProfilerStatus::Set(ProfilerStatus newProfStatus)
218 _ASSERTE(ProfilingAPIUtility::GetStatusCrst() != NULL);
221 // Need to serialize attempts to transition the profiler status. For example, a
222 // profiler in one thread could request a detach, while the CLR in another
223 // thread is transitioning the profiler from kProfStatusInitializing* to
225 CRITSEC_Holder csh(ProfilingAPIUtility::GetStatusCrst());
227 // Based on what the old status is, verify the new status is a legal transition.
231 _ASSERTE(!"Unknown ProfilerStatus");
234 case kProfStatusNone:
235 _ASSERTE((newProfStatus == kProfStatusInitializingForStartupLoad) ||
236 (newProfStatus == kProfStatusInitializingForAttachLoad));
239 case kProfStatusDetaching:
240 _ASSERTE(newProfStatus == kProfStatusNone);
243 case kProfStatusInitializingForStartupLoad:
244 case kProfStatusInitializingForAttachLoad:
245 _ASSERTE((newProfStatus == kProfStatusActive) ||
246 (newProfStatus == kProfStatusNone));
249 case kProfStatusActive:
250 _ASSERTE((newProfStatus == kProfStatusNone) ||
251 (newProfStatus == kProfStatusDetaching));
255 m_profStatus = newProfStatus;
258 #if !defined(DACCESS_COMPILE)
259 if (((newProfStatus == kProfStatusNone) ||
260 (newProfStatus == kProfStatusDetaching) ||
261 (newProfStatus == kProfStatusActive)))
263 // Flush the store buffers on all CPUs, to ensure other threads see that
264 // g_profControlBlock.curProfStatus has changed. The important status changes to
266 // * to kProfStatusNone or kProfStatusDetaching so other threads know to stop
267 // making calls into the profiler
268 // * to kProfStatusActive, to ensure callbacks can be issued by the time an
269 // attaching profiler receives ProfilerAttachComplete(), so the profiler
270 // can safely perform catchup at that time (see
271 // code:#ProfCatchUpSynchronization).
273 ::FlushProcessWriteBuffers();
275 #endif // !defined(DACCESS_COMPILE)
279 //---------------------------------------------------------------------------------------
280 // ProfilingAPIUtility members
283 // See code:#LoadUnloadCallbackSynchronization.
284 CRITSEC_COOKIE ProfilingAPIUtility::s_csStatus = NULL;
287 SidBuffer * ProfilingAPIUtility::s_pSidBuffer = NULL;
289 // ----------------------------------------------------------------------------
290 // ProfilingAPIUtility::AppendSupplementaryInformation
293 // Helper to the event logging functions to append the process ID and string
294 // resource ID to the end of the message.
297 // * iStringResource - [in] String resource ID to append to message.
298 // * pString - [in/out] On input, the string to log so far. On output, the original
299 // string with the process ID info appended.
303 void ProfilingAPIUtility::AppendSupplementaryInformation(int iStringResource, SString * pString)
311 // This loads resource strings, which takes locks.
316 StackSString supplementaryInformation;
318 if (!supplementaryInformation.LoadResource(
320 IDS_PROF_SUPPLEMENTARY_INFO
323 // Resource not found; should never happen.
327 pString->Append(W(" "));
328 pString->AppendPrintf(
329 supplementaryInformation,
330 GetCurrentProcessId(),
334 //---------------------------------------------------------------------------------------
336 // Helper function to log publicly-viewable errors about profiler loading and
341 // * iStringResourceID - resource ID of string containing message to log
342 // * wEventType - same constant used in win32 to specify the type of event:
343 // usually EVENTLOG_ERROR_TYPE, EVENTLOG_WARNING_TYPE, or
344 // EVENTLOG_INFORMATION_TYPE
345 // * insertionArgs - 0 or more values to be inserted into the string to be logged
346 // (>0 only if iStringResourceID contains format arguments (%)).
350 void ProfilingAPIUtility::LogProfEventVA(
351 int iStringResourceID,
353 va_list insertionArgs)
361 // This loads resource strings, which takes locks.
366 StackSString messageFromResource;
367 StackSString messageToLog;
369 if (!messageFromResource.LoadResource(
374 // Resource not found; should never happen.
378 messageToLog.VPrintf(messageFromResource, insertionArgs);
380 AppendSupplementaryInformation(iStringResourceID, &messageToLog);
382 // Ouput debug strings for diagnostic messages.
383 WszOutputDebugString(messageToLog);
386 // See code:ProfilingAPIUtility.LogProfEventVA for description of arguments.
388 void ProfilingAPIUtility::LogProfError(int iStringResourceID, ...)
396 // This loads resource strings, which takes locks.
401 va_list insertionArgs;
402 va_start(insertionArgs, iStringResourceID);
407 va_end(insertionArgs);
410 // See code:ProfilingAPIUtility.LogProfEventVA for description of arguments.
412 void ProfilingAPIUtility::LogProfInfo(int iStringResourceID, ...)
420 // This loads resource strings, which takes locks.
425 va_list insertionArgs;
426 va_start(insertionArgs, iStringResourceID);
429 EVENTLOG_INFORMATION_TYPE,
431 va_end(insertionArgs);
434 #ifdef PROF_TEST_ONLY_FORCE_ELT
435 // Special forward-declarations of the profiling API's slow-path enter/leave/tailcall
436 // hooks. These need to be forward-declared here so that they may be referenced in
437 // InitializeProfiling() below solely for the debug-only, test-only code to allow
438 // enter/leave/tailcall to be turned on at startup without a profiler. See
439 // code:ProfControlBlock#TestOnlyELT
440 EXTERN_C void STDMETHODCALLTYPE ProfileEnterNaked(UINT_PTR clientData);
441 EXTERN_C void STDMETHODCALLTYPE ProfileLeaveNaked(UINT_PTR clientData);
442 EXTERN_C void STDMETHODCALLTYPE ProfileTailcallNaked(UINT_PTR clientData);
443 #endif //PROF_TEST_ONLY_FORCE_ELT
445 // ----------------------------------------------------------------------------
446 // ProfilingAPIUtility::InitializeProfiling
448 // This is the top-most level of profiling API initialization, and is called directly by
449 // EEStartupHelper() (in ceemain.cpp). This initializes internal structures relating to the
450 // Profiling API. This also orchestrates loading the profiler and initializing it (if
451 // its GUID is specified in the environment).
454 // HRESULT indicating success or failure. This is generally very lenient about internal
455 // failures, as we don't want them to prevent the startup of the app:
456 // S_OK = Environment didn't request a profiler, or
457 // Environment did request a profiler, and it was loaded successfully
458 // S_FALSE = There was a problem loading the profiler, but that shouldn't prevent the app
460 // else (failure) = There was a serious problem that should be dealt with by the caller
463 // This function (or one of its callees) will log an error to the event log
464 // if there is a failure
467 // InitializeProfiling is called during startup, AFTER the host has initialized its
468 // settings and the config variables have been read, but BEFORE the finalizer thread
469 // has entered its first wait state. ASSERTs are placed in
470 // code:ProfilingAPIAttachDetach::Initialize (which is called by this function, and
471 // which depends on these assumptions) to verify.
474 HRESULT ProfilingAPIUtility::InitializeProfiling()
481 // This causes events to be logged, which loads resource strings,
482 // which takes locks.
491 // NULL out / initialize members of the global profapi structure
492 g_profControlBlock.Init();
494 if (IsCompilationProcess())
496 LOG((LF_CORPROF, LL_INFO10, "**PROF: Profiling disabled for ngen process.\n"));
500 AttemptLoadProfilerForStartup();
501 // For now, the return value from AttemptLoadProfilerForStartup is of no use to us.
502 // Any event has been logged already by AttemptLoadProfilerForStartup, and
503 // regardless of whether a profiler got loaded, we still need to continue.
506 #ifdef PROF_TEST_ONLY_FORCE_ELT
507 // Test-only, debug-only code to enable ELT on startup regardless of whether a
508 // startup profiler is loaded. See code:ProfControlBlock#TestOnlyELT.
509 DWORD dwEnableSlowELTHooks = CLRConfig::GetConfigValue(CLRConfig::INTERNAL_TestOnlyEnableSlowELTHooks);
510 if (dwEnableSlowELTHooks != 0)
512 (&g_profControlBlock)->fTestOnlyForceEnterLeave = TRUE;
513 SetJitHelperFunction(CORINFO_HELP_PROF_FCN_ENTER, (void *) ProfileEnterNaked);
514 SetJitHelperFunction(CORINFO_HELP_PROF_FCN_LEAVE, (void *) ProfileLeaveNaked);
515 SetJitHelperFunction(CORINFO_HELP_PROF_FCN_TAILCALL, (void *) ProfileTailcallNaked);
516 LOG((LF_CORPROF, LL_INFO10, "**PROF: Enabled test-only slow ELT hooks.\n"));
518 #endif //PROF_TEST_ONLY_FORCE_ELT
520 #ifdef PROF_TEST_ONLY_FORCE_OBJECT_ALLOCATED
521 // Test-only, debug-only code to enable ObjectAllocated callbacks on startup regardless of whether a
522 // startup profiler is loaded. See code:ProfControlBlock#TestOnlyObjectAllocated.
523 DWORD dwEnableObjectAllocated = CLRConfig::GetConfigValue(CLRConfig::INTERNAL_TestOnlyEnableObjectAllocatedHook);
524 if (dwEnableObjectAllocated != 0)
526 (&g_profControlBlock)->fTestOnlyForceObjectAllocated = TRUE;
527 LOG((LF_CORPROF, LL_INFO10, "**PROF: Enabled test-only object ObjectAllocated hooks.\n"));
529 #endif //PROF_TEST_ONLY_FORCE_ELT
533 // Test-only, debug-only code to allow attaching profilers to call ICorProfilerInfo inteface,
534 // which would otherwise be disallowed for attaching profilers
535 DWORD dwTestOnlyEnableICorProfilerInfo = CLRConfig::GetConfigValue(CLRConfig::INTERNAL_TestOnlyEnableICorProfilerInfo);
536 if (dwTestOnlyEnableICorProfilerInfo != 0)
538 (&g_profControlBlock)->fTestOnlyEnableICorProfilerInfo = TRUE;
546 // ----------------------------------------------------------------------------
547 // ProfilingAPIUtility::ProfilerCLSIDFromString
550 // Takes a string form of a CLSID (or progid, believe it or not), and returns the
551 // corresponding CLSID structure.
554 // * wszClsid - [in / out] CLSID string to convert. This may also be a progid. This
555 // ensures our behavior is backward-compatible with previous CLR versions. I don't
556 // know why previous versions allowed the user to set a progid in the environment,
557 // but well whatever. On [out], this string is normalized in-place (e.g.,
558 // double-quotes around progid are removed).
559 // * pClsid - [out] CLSID structure corresponding to wszClsid
562 // HRESULT indicating success or failure.
565 // * An event is logged if there is a failure.
569 HRESULT ProfilingAPIUtility::ProfilerCLSIDFromString(
570 __inout_z LPWSTR wszClsid,
578 // This causes events to be logged, which loads resource strings,
579 // which takes locks.
586 _ASSERTE(wszClsid != NULL);
587 _ASSERTE(pClsid != NULL);
591 // Translate the string into a CLSID
592 if (*wszClsid == W('{'))
594 hr = IIDFromString(wszClsid, pClsid);
599 WCHAR *szFrom, *szTo;
602 #pragma warning(push)
603 #pragma warning(disable:26000) // "espX thinks there is an overflow here, but there isn't any"
605 for (szFrom=szTo=wszClsid; *szFrom; )
607 if (*szFrom == W('"'))
615 hr = CLSIDFromProgID(wszClsid, pClsid);
620 #else // !FEATURE_PAL
621 // ProgID not supported on FEATURE_PAL
623 #endif // !FEATURE_PAL
631 "**PROF: Invalid CLSID or ProgID (%S). hr=0x%x.\n",
634 ProfilingAPIUtility::LogProfError(IDS_E_PROF_BAD_CLSID, wszClsid, hr);
641 // ----------------------------------------------------------------------------
642 // ProfilingAPIUtility::AttemptLoadProfilerForStartup
645 // Checks environment or registry to see if the app is configured to run with a
646 // profiler loaded on startup. If so, this calls LoadProfiler() to load it up.
651 // * S_OK: Startup-profiler has been loaded
652 // * S_FALSE: No profiler is configured for startup load
653 // * else, HRESULT indicating failure that occurred
656 // * This should be called on startup, after g_profControlBlock is initialized, but
657 // before any attach infrastructure is initialized. This ensures we don't receive
658 // an attach request while startup-loading a profiler.
661 // * This or its callees will ensure an event is logged on failure (though will be
662 // silent if no profiler is configured for startup load (which causes S_FALSE to
667 HRESULT ProfilingAPIUtility::AttemptLoadProfilerForStartup()
674 // This causes events to be logged, which loads resource strings,
675 // which takes locks.
684 // Find out if profiling is enabled
685 DWORD fProfEnabled = 0;
687 fProfEnabled = CLRConfig::GetConfigValue(CLRConfig::EXTERNAL_CORECLR_ENABLE_PROFILING);
689 // If profiling is not enabled, return.
690 if (fProfEnabled == 0)
692 LOG((LF_CORPROF, LL_INFO10, "**PROF: Profiling not enabled.\n"));
696 LOG((LF_CORPROF, LL_INFO10, "**PROF: Initializing Profiling Services.\n"));
698 // Get the CLSID of the profiler to CoCreate
699 NewArrayHolder<WCHAR> wszClsid(NULL);
700 NewArrayHolder<WCHAR> wszProfilerDLL(NULL);
702 IfFailRet(CLRConfig::GetConfigValue(CLRConfig::EXTERNAL_CORECLR_PROFILER, &wszClsid));
704 #if defined(_TARGET_X86_)
705 IfFailRet(CLRConfig::GetConfigValue(CLRConfig::EXTERNAL_CORECLR_PROFILER_PATH_32, &wszProfilerDLL));
706 #elif defined(_TARGET_AMD64_)
707 IfFailRet(CLRConfig::GetConfigValue(CLRConfig::EXTERNAL_CORECLR_PROFILER_PATH_64, &wszProfilerDLL));
709 if(wszProfilerDLL == NULL)
711 IfFailRet(CLRConfig::GetConfigValue(CLRConfig::EXTERNAL_CORECLR_PROFILER_PATH, &wszProfilerDLL));
714 // If the environment variable doesn't exist, profiling is not enabled.
715 if (wszClsid == NULL)
717 LOG((LF_CORPROF, LL_INFO10, "**PROF: Profiling flag set, but required "
718 "environment variable does not exist.\n"));
720 LogProfError(IDS_E_PROF_NO_CLSID);
725 if ((wszProfilerDLL != NULL) && (wcslen(wszProfilerDLL) >= MAX_LONGPATH))
727 LOG((LF_CORPROF, LL_INFO10, "**PROF: Profiling flag set, but COR_PROFILER_PATH was not set properly.\n"));
729 LogProfError(IDS_E_PROF_BAD_PATH);
735 // If the environment variable doesn't exist, profiling is not enabled.
736 if (wszProfilerDLL == NULL)
738 LOG((LF_CORPROF, LL_INFO10, "**PROF: Profiling flag set, but required "
739 "environment variable does not exist.\n"));
741 LogProfError(IDS_E_PROF_BAD_PATH);
745 #endif // FEATURE_PAL
748 hr = ProfilingAPIUtility::ProfilerCLSIDFromString(wszClsid, &clsid);
751 // ProfilerCLSIDFromString already logged an event if there was a failure
760 NULL, // No client data for startup load
761 0); // No client data for startup load
764 // A failure in either the CLR or the profiler prevented it from
765 // loading. Event has been logged. Propagate hr
773 //---------------------------------------------------------------------------------------
775 // Performs lazy initialization that need not occur on startup, but does need to occur
776 // before trying to load a profiler.
779 // HRESULT indicating success or failure.
782 HRESULT ProfilingAPIUtility::PerformDeferredInit()
793 #ifdef FEATURE_PROFAPI_ATTACH_DETACH
794 // Initialize internal resources for detaching
795 HRESULT hr = ProfilingAPIDetach::Initialize();
801 "**PROF: Unable to initialize resources for detaching. hr=0x%x.\n",
805 #endif // FEATURE_PROFAPI_ATTACH_DETACH
807 if (s_csStatus == NULL)
809 s_csStatus = ClrCreateCriticalSection(
810 CrstProfilingAPIStatus,
811 (CrstFlags) (CRST_REENTRANCY | CRST_TAKEN_DURING_SHUTDOWN));
812 if (s_csStatus == NULL)
814 return E_OUTOFMEMORY;
821 // ----------------------------------------------------------------------------
822 // ProfilingAPIUtility::LoadProfiler
825 // Outermost common code for loading the profiler DLL. Both startup and attach code
829 // * loadType - Startup load or attach load?
830 // * pClsid - Profiler's CLSID
831 // * wszClsid - Profiler's CLSID (or progid) in string form, for event log messages
832 // * wszProfilerDLL - Profiler's DLL path
833 // * pvClientData - For attach loads, this is the client data the trigger wants to
834 // pass to the profiler DLL
835 // * cbClientData - For attach loads, size of client data in bytes
836 // * dwConcurrentGCWaitTimeoutInMs - Time out for wait operation on concurrent GC. Attach scenario only
839 // HRESULT indicating success or failure of the load
842 // * On failure, this function or a callee will have logged an event
846 HRESULT ProfilingAPIUtility::LoadProfiler(
848 const CLSID * pClsid,
850 LPCWSTR wszProfilerDLL,
853 DWORD dwConcurrentGCWaitTimeoutInMs)
860 // This causes events to be logged, which loads resource strings,
861 // which takes locks.
870 return CORPROF_E_RUNTIME_UNINITIALIZED;
873 enum ProfilerCompatibilityFlag
875 // Default: disable V2 profiler
876 kDisableV2Profiler = 0x0,
878 // Enable V2 profilers
879 kEnableV2Profiler = 0x1,
885 ProfilerCompatibilityFlag profilerCompatibilityFlag = kDisableV2Profiler;
886 NewArrayHolder<WCHAR> wszProfilerCompatibilitySetting(NULL);
888 if (loadType == kStartupLoad)
890 CLRConfig::GetConfigValue(CLRConfig::EXTERNAL_ProfAPI_ProfilerCompatibilitySetting, &wszProfilerCompatibilitySetting);
891 if (wszProfilerCompatibilitySetting != NULL)
893 if (SString::_wcsicmp(wszProfilerCompatibilitySetting, W("EnableV2Profiler")) == 0)
895 profilerCompatibilityFlag = kEnableV2Profiler;
897 else if (SString::_wcsicmp(wszProfilerCompatibilitySetting, W("PreventLoad")) == 0)
899 profilerCompatibilityFlag = kPreventLoad;
903 if (profilerCompatibilityFlag == kPreventLoad)
905 LOG((LF_CORPROF, LL_INFO10, "**PROF: COMPlus_ProfAPI_ProfilerCompatibilitySetting is set to PreventLoad. "
906 "Profiler will not be loaded.\n"));
908 LogProfInfo(IDS_PROF_PROFILER_DISABLED,
909 CLRConfig::EXTERNAL_ProfAPI_ProfilerCompatibilitySetting.name,
910 wszProfilerCompatibilitySetting.GetValue(),
919 hr = PerformDeferredInit();
925 "**PROF: ProfilingAPIUtility::PerformDeferredInit failed. hr=0x%x.\n",
927 LogProfError(IDS_E_PROF_INTERNAL_INIT, wszClsid, hr);
932 _ASSERTE((loadType == kStartupLoad) || (loadType == kAttachLoad));
934 // If a nonzero client data size is reported, there'd better be client data!
935 _ASSERTE((cbClientData == 0) || (pvClientData != NULL));
937 // Client data is currently only specified on attach
938 _ASSERTE((pvClientData == NULL) || (loadType == kAttachLoad));
940 // Don't be telling me to load a profiler if there already is one.
941 _ASSERTE(g_profControlBlock.curProfStatus.Get() == kProfStatusNone);
943 // Create the ProfToEE interface to provide to the profiling services
944 NewHolder<ProfToEEInterfaceImpl> pProfEE(new (nothrow) ProfToEEInterfaceImpl());
947 LOG((LF_CORPROF, LL_ERROR, "**PROF: Unable to allocate ProfToEEInterfaceImpl.\n"));
948 LogProfError(IDS_E_PROF_INTERNAL_INIT, wszClsid, E_OUTOFMEMORY);
949 return E_OUTOFMEMORY;
952 // Initialize the interface
953 hr = pProfEE->Init();
956 LOG((LF_CORPROF, LL_ERROR, "**PROF: ProfToEEInterface::Init failed.\n"));
957 LogProfError(IDS_E_PROF_INTERNAL_INIT, wszClsid, hr);
961 // Provide the newly created and inited interface
962 LOG((LF_CORPROF, LL_INFO10, "**PROF: Profiling code being provided with EE interface.\n"));
964 // Create a new EEToProf object
965 NewHolder<EEToProfInterfaceImpl> pEEProf(new (nothrow) EEToProfInterfaceImpl());
968 LOG((LF_CORPROF, LL_ERROR, "**PROF: Unable to allocate EEToProfInterfaceImpl.\n"));
969 LogProfError(IDS_E_PROF_INTERNAL_INIT, wszClsid, E_OUTOFMEMORY);
970 return E_OUTOFMEMORY;
973 #ifdef FEATURE_PROFAPI_ATTACH_DETACH
974 // We're about to load the profiler, so first make sure we successfully create the
975 // DetachThread and abort the load of the profiler if we can't. This ensures we don't
976 // load a profiler unless we're prepared to detach it later.
977 hr = ProfilingAPIDetach::CreateDetachThread();
983 "**PROF: Unable to create DetachThread. hr=0x%x.\n",
985 ProfilingAPIUtility::LogProfError(IDS_E_PROF_INTERNAL_INIT, wszClsid, hr);
988 #endif // FEATURE_PROFAPI_ATTACH_DETACH
990 // Initialize internal state of our EEToProfInterfaceImpl. This also loads the
991 // profiler itself, but does not yet call its Initalize() callback
992 hr = pEEProf->Init(pProfEE, pClsid, wszClsid, wszProfilerDLL, (loadType == kAttachLoad), dwConcurrentGCWaitTimeoutInMs);
995 LOG((LF_CORPROF, LL_ERROR, "**PROF: EEToProfInterfaceImpl::Init failed.\n"));
996 // EEToProfInterfaceImpl::Init logs an event log error on failure
1000 // EEToProfInterfaceImpl::Init takes over the ownership of pProfEE when Init succeeds, and
1001 // EEToProfInterfaceImpl::~EEToProfInterfaceImpl is responsible for releasing the resource pointed
1002 // by pProfEE. Calling SuppressRelease here is necessary to avoid double release that
1003 // the resource pointed by pProfEE are released by both pProfEE and pEEProf's destructor.
1004 pProfEE.SuppressRelease();
1007 if (loadType == kAttachLoad) // V4 profiler from attach
1009 // Profiler must support ICorProfilerCallback3 to be attachable
1010 if (!pEEProf->IsCallback3Supported())
1012 LogProfError(IDS_E_PROF_NOT_ATTACHABLE, wszClsid);
1013 return CORPROF_E_PROFILER_NOT_ATTACHABLE;
1016 else if (!pEEProf->IsCallback3Supported()) // V2 profiler from startup
1018 if (profilerCompatibilityFlag == kDisableV2Profiler)
1020 LOG((LF_CORPROF, LL_INFO10, "**PROF: COMPlus_ProfAPI_ProfilerCompatibilitySetting is set to DisableV2Profiler (the default). "
1021 "V2 profilers are not allowed, so that the configured V2 profiler is going to be unloaded.\n"));
1023 LogProfInfo(IDS_PROF_V2PROFILER_DISABLED, wszClsid);
1027 _ASSERTE(profilerCompatibilityFlag == kEnableV2Profiler);
1029 LOG((LF_CORPROF, LL_INFO10, "**PROF: COMPlus_ProfAPI_ProfilerCompatibilitySetting is set to EnableV2Profiler. "
1030 "The configured V2 profiler is going to be initialized.\n"));
1032 LogProfInfo(IDS_PROF_V2PROFILER_ENABLED,
1033 CLRConfig::EXTERNAL_ProfAPI_ProfilerCompatibilitySetting.name,
1034 wszProfilerCompatibilitySetting.GetValue(),
1038 _ASSERTE(s_csStatus != NULL);
1040 // All modification of the profiler's status and
1041 // g_profControlBlock.pProfInterface need to be serialized against each other,
1042 // in particular, this code should be serialized against detach and unloading
1044 CRITSEC_Holder csh(s_csStatus);
1046 // We've successfully allocated and initialized the callback wrapper object and the
1047 // Info interface implementation objects. The profiler DLL is therefore also
1048 // successfully loaded (but not yet Initialized). Transfer ownership of the
1049 // callback wrapper object to globals (thus suppress a release when the local
1050 // vars go out of scope).
1052 // Setting this state now enables us to call into the profiler's Initialize()
1053 // callback (which we do immediately below), and have it successfully call
1054 // back into us via the Info interface (ProfToEEInterfaceImpl) to perform its
1056 g_profControlBlock.pProfInterface = pEEProf.GetValue();
1057 pEEProf.SuppressRelease();
1060 // Set global status to reflect the proper type of Init we're doing (attach vs
1062 g_profControlBlock.curProfStatus.Set(
1063 (loadType == kStartupLoad) ?
1064 kProfStatusInitializingForStartupLoad :
1065 kProfStatusInitializingForAttachLoad);
1068 // Now that the profiler is officially loaded and in Init status, call into the
1069 // profiler's appropriate Initialize() callback. Note that if the profiler fails this
1070 // call, we should abort the rest of the profiler loading, and reset our state so we
1071 // appear as if we never attempted to load the profiler.
1073 if (loadType == kStartupLoad)
1075 hr = g_profControlBlock.pProfInterface->Initialize();
1079 _ASSERTE(loadType == kAttachLoad);
1080 _ASSERTE(g_profControlBlock.pProfInterface->IsCallback3Supported());
1081 hr = g_profControlBlock.pProfInterface->InitializeForAttach(pvClientData, cbClientData);
1089 "**PROF: Profiler failed its Initialize callback. hr=0x%x.\n",
1092 // If we timed out due to waiting on concurrent GC to finish, it is very likely this is
1093 // the reason InitializeForAttach callback failed even though we cannot be sure and we cannot
1094 // cannot assume hr is going to be CORPROF_E_TIMEOUT_WAITING_FOR_CONCURRENT_GC.
1095 // The best we can do in this case is to report this failure anyway.
1096 if (g_profControlBlock.pProfInterface->HasTimedOutWaitingForConcurrentGC())
1098 ProfilingAPIUtility::LogProfError(IDS_E_PROF_TIMEOUT_WAITING_FOR_CONCURRENT_GC, dwConcurrentGCWaitTimeoutInMs, wszClsid);
1101 // Check for known failure types, to customize the event we log
1102 if ((loadType == kAttachLoad) &&
1103 ((hr == CORPROF_E_PROFILER_NOT_ATTACHABLE) || (hr == E_NOTIMPL)))
1105 _ASSERTE(g_profControlBlock.pProfInterface->IsCallback3Supported());
1107 // Profiler supports ICorProfilerCallback3, but explicitly doesn't support
1108 // Attach loading. So log specialized event
1109 LogProfError(IDS_E_PROF_NOT_ATTACHABLE, wszClsid);
1111 // Normalize (CORPROF_E_PROFILER_NOT_ATTACHABLE || E_NOTIMPL) down to
1112 // CORPROF_E_PROFILER_NOT_ATTACHABLE
1113 hr = CORPROF_E_PROFILER_NOT_ATTACHABLE;
1115 else if (hr == CORPROF_E_PROFILER_CANCEL_ACTIVATION)
1117 // Profiler didn't encounter a bad error, but is voluntarily choosing not to
1118 // profile this runtime. Profilers that need to set system environment
1119 // variables to be able to profile services may use this HRESULT to avoid
1120 // profiling all the other managed apps on the box.
1121 LogProfInfo(IDS_PROF_CANCEL_ACTIVATION, wszClsid);
1125 LogProfError(IDS_E_PROF_INIT_CALLBACK_FAILED, wszClsid, hr);
1128 // Profiler failed; reset everything. This will automatically reset
1129 // g_profControlBlock and will unload the profiler's DLL.
1130 TerminateProfiling();
1134 #ifdef FEATURE_MULTICOREJIT
1136 // Disable multicore JIT when profiling is enabled
1137 if (g_profControlBlock.dwEventMask & COR_PRF_MONITOR_JIT_COMPILATION)
1139 MulticoreJitManager::DisableMulticoreJit();
1144 // Indicate that profiling is properly initialized. On an attach-load, this will
1145 // force a FlushStoreBuffers(), which is important for catch-up synchronization (see
1146 // code:#ProfCatchUpSynchronization)
1147 g_profControlBlock.curProfStatus.Set(kProfStatusActive);
1152 "**PROF: Profiler successfully loaded and initialized.\n"));
1154 LogProfInfo(IDS_PROF_LOAD_COMPLETE, wszClsid);
1156 LOG((LF_CORPROF, LL_INFO10, "**PROF: Profiler created and enabled.\n"));
1158 if (loadType == kStartupLoad)
1160 // For startup profilers only: If the profiler is interested in tracking GC
1161 // events, then we must disable concurrent GC since concurrent GC can allocate
1162 // and kill objects without relocating and thus not doing a heap walk.
1163 if (CORProfilerTrackGC())
1165 LOG((LF_CORPROF, LL_INFO10, "**PROF: Turning off concurrent GC at startup.\n"));
1166 // Previously we would use SetGCConcurrent(0) to indicate to the GC that it shouldn't even
1167 // attempt to use concurrent GC. The standalone GC feature create a cycle during startup,
1168 // where the profiler couldn't set startup flags for the GC. To overcome this, we call
1169 // TempraryDisableConcurrentGC and never enable it again. This has a perf cost, since the
1170 // GC will create concurrent GC data structures, but it is acceptable in the context of
1171 // this kind of profiling.
1172 GCHeapUtilities::GetGCHeap()->TemporaryDisableConcurrentGC();
1173 LOG((LF_CORPROF, LL_INFO10, "**PROF: Concurrent GC has been turned off at startup.\n"));
1177 if (loadType == kAttachLoad)
1179 // #ProfCatchUpSynchronization
1181 // Now that callbacks are enabled (and all threads are aware), tell an attaching
1182 // profiler that it's safe to request catchup information.
1184 // There's a race we're preventing that's worthwhile to spell out. An attaching
1185 // profiler should be able to get a COMPLETE set of data through the use of
1186 // callbacks unioned with the use of catch-up enumeration Info functions. To
1187 // achieve this, we must ensure that there is no "hole"--any new data the
1188 // profiler seeks must be available from a callback or a catch-up info function
1189 // (or both, as dupes are ok). That means that:
1191 // * callbacks must be enabled on other threads NO LATER THAN the profiler begins
1192 // requesting catch-up information on this thread
1193 // * Abbreviate: callbacks <= catch-up.
1195 // Otherwise, if catch-up < callbacks, then it would be possible to have this:
1197 // * catch-up < new data arrives < callbacks.
1199 // In this nightmare scenario, the new data would not be accessible from the
1200 // catch-up calls made by the profiler (cuz the profiler made the calls too
1201 // early) or the callbacks made into the profiler (cuz the callbacks were enabled
1202 // too late). That's a hole, and that's bad. So we ensure callbacks <= catch-up
1203 // by the following order of operations:
1206 // * a: Set (volatile) currentProfStatus = kProfStatusActive (done above) and
1207 // event mask bits (profiler did this in Initialize() callback above,
1208 // when it called SetEventMask)
1209 // * b: Flush CPU buffers (done automatically when we set status to
1210 // kProfStatusActive)
1211 // * c: CLR->Profiler call: ProfilerAttachComplete() (below). Inside this
1213 // * Profiler->CLR calls: Catch-up Info functions
1215 // * a: New data (thread, JIT info, etc.) is created
1216 // * b: This new data is now available to a catch-up Info call
1217 // * c: currentProfStatus & event mask bits are accurately visible to thread
1218 // in determining whether to make a callback
1219 // * d: Read currentProfStatus & event mask bits and make callback
1220 // (CLR->Profiler) if necessary
1222 // So as long as OtherThreads.c <= ThisThread.c we're ok. This means other
1223 // threads must be able to get a clean read of the (volatile) currentProfStatus &
1224 // event mask bits BEFORE this thread calls ProfilerAttachComplete(). Use of the
1225 // "volatile" keyword ensures that compiler optimizations and (w/ VC2005+
1226 // compilers) the CPU's instruction reordering optimizations at runtime are
1227 // disabled enough such that they do not hinder the order above. Use of
1228 // FlushStoreBuffers() ensures that multiple caches on multiple CPUs do not
1229 // hinder the order above (by causing other threads to get stale reads of the
1232 // For more information about catch-up enumerations and exactly which entities,
1233 // and which stage of loading, are permitted to appear in the enumerations, see
1234 // code:ProfilerFunctionEnum::Init#ProfilerEnumGeneral
1237 BEGIN_PIN_PROFILER(CORProfilerPresent());
1238 g_profControlBlock.pProfInterface->ProfilerAttachComplete();
1246 //---------------------------------------------------------------------------------------
1248 // Performs the evacuation checks by grabbing the thread store lock, iterating through
1249 // all EE Threads, and querying each one's evacuation counter. If they're all 0, the
1250 // profiler is ready to be unloaded.
1253 // Nonzero iff the profiler is fully evacuated and ready to be unloaded.
1257 BOOL ProfilingAPIUtility::IsProfilerEvacuated()
1268 _ASSERTE(g_profControlBlock.curProfStatus.Get() == kProfStatusDetaching);
1270 // Check evacuation counters on all the threads (see
1271 // code:ProfilingAPIUtility::InitializeProfiling#LoadUnloadCallbackSynchronization
1272 // for details). Doing this under the thread store lock not only ensures we can
1273 // iterate through the Thread objects safely, but also forces us to serialize with
1274 // the GC. The latter is important, as server GC enters the profiler on non-EE
1275 // Threads, and so no evacuation counters might be incremented during server GC even
1276 // though control could be entering the profiler.
1278 ThreadStoreLockHolder TSLockHolder;
1280 Thread * pThread = ThreadStore::GetAllThreadList(
1281 NULL, // cursor thread; always NULL to begin with
1282 0, // mask to AND with Thread::m_State to filter returned threads
1283 0); // bits to match the result of the above AND. (m_State & 0 == 0,
1284 // so we won't filter out any threads)
1286 // Note that, by not filtering out any of the threads, we're intentionally including
1287 // stuff like TS_Dead or TS_Unstarted. But that keeps us on the safe
1288 // side. If an EE Thread object exists, we want to check its counters to be
1289 // absolutely certain it isn't executing in a profiler.
1291 while (pThread != NULL)
1293 // Note that pThread is still in motion as we check its evacuation counter.
1294 // This is ok, because we've already changed the profiler status to
1295 // kProfStatusDetaching and flushed CPU buffers. So at this point the counter
1296 // will typically only go down to 0 (and not increment anymore), with one
1297 // small exception (below). So if we get a read of 0 below, the counter will
1298 // typically stay there. Specifically:
1299 // * pThread is most likely not about to increment its evacuation counter
1300 // from 0 to 1 because pThread sees that the status is
1301 // kProfStatusDetaching.
1302 // * Note that there is a small race where pThread might actually
1303 // increment its evac counter from 0 to 1 (if it dirty-read the
1304 // profiler status a tad too early), but that implies that when
1305 // pThread rechecks the profiler status (clean read) then pThread
1306 // will immediately decrement the evac counter back to 0 and avoid
1307 // calling into the EEToProfInterfaceImpl pointer.
1310 // code:ProfilingAPIUtility::InitializeProfiling#LoadUnloadCallbackSynchronization
1312 DWORD dwEvacCounter = pThread->GetProfilerEvacuationCounter();
1313 if (dwEvacCounter != 0)
1318 "**PROF: Profiler not yet evacuated because OS Thread ID 0x%x has evac counter of %d (decimal).\n",
1319 pThread->GetOSThreadId(),
1324 pThread = ThreadStore::GetAllThreadList(pThread, 0, 0);
1328 // FUTURE: When rejit feature crew complete, add code to verify all rejitted
1329 // functions are fully reverted and off of all stacks. If this is very easy to
1330 // verify (e.g., checking a single value), consider putting it above the loop
1331 // above so we can early-out quicker if rejitted code is still around.
1333 // We got this far without returning, so the profiler is fully evacuated
1337 //---------------------------------------------------------------------------------------
1339 // This is the top-most level of profiling API teardown, and is called directly by
1340 // EEShutDownHelper() (in ceemain.cpp). This cleans up internal structures relating to
1341 // the Profiling API. If we're not in process teardown, then this also releases the
1342 // profiler COM object and frees the profiler DLL
1346 void ProfilingAPIUtility::TerminateProfiling()
1357 if (IsAtProcessExit())
1359 // We're tearing down the process so don't bother trying to clean everything up.
1360 // There's no reliable way to verify other threads won't be trying to re-enter
1361 // the profiler anyway, so cleaning up here could cause AVs.
1365 _ASSERTE(s_csStatus != NULL);
1367 // We're modifying status and possibly unloading the profiler DLL below, so
1368 // serialize this code with any other loading / unloading / detaching code.
1369 CRITSEC_Holder csh(s_csStatus);
1372 #ifdef FEATURE_PROFAPI_ATTACH_DETACH
1373 if (ProfilingAPIDetach::GetEEToProfPtr() != NULL)
1375 // The profiler is still being referenced by
1376 // ProfilingAPIDetach::s_profilerDetachInfo, so don't try to release and
1377 // unload it. This can happen if Shutdown and Detach race, and Shutdown wins.
1378 // For example, we could be called as part of Shutdown, but the profiler
1379 // called RequestProfilerDetach near shutdown time as well (or even earlier
1380 // but remains un-evacuated as shutdown begins). Whatever the cause, just
1381 // don't unload the profiler here (as part of shutdown), and let the Detach
1382 // Thread deal with it (if it gets the chance).
1384 // Note: Since this check occurs inside s_csStatus, we don't have to worry
1385 // that ProfilingAPIDetach::GetEEToProfPtr() will suddenly change during the
1388 // FUTURE: For reattach-with-neutered-profilers feature crew, change the
1389 // above to scan through list of detaching profilers to make sure none of
1390 // them give a GetEEToProfPtr() equal to g_profControlBlock.pProfInterface.
1393 #endif // FEATURE_PROFAPI_ATTACH_DETACH
1395 if (g_profControlBlock.curProfStatus.Get() == kProfStatusActive)
1397 g_profControlBlock.curProfStatus.Set(kProfStatusDetaching);
1399 // Profiler was active when TerminateProfiling() was called, so we're unloading
1400 // it due to shutdown. But other threads may still be trying to enter profiler
1401 // callbacks (e.g., ClassUnloadStarted() can get called during shutdown). Now
1402 // that the status has been changed to kProfStatusDetaching, no new threads will
1403 // attempt to enter the profiler. But use the detach evacuation counters to see
1404 // if other threads already began to enter the profiler.
1405 if (!ProfilingAPIUtility::IsProfilerEvacuated())
1407 // Other threads might be entering the profiler, so just skip cleanup
1413 // If we have a profiler callback wrapper and / or info implementation
1414 // active, then terminate them.
1416 if (g_profControlBlock.pProfInterface.Load() != NULL)
1418 // This destructor takes care of releasing the profiler's ICorProfilerCallback*
1419 // interface, and unloading the DLL when we're not in process teardown.
1420 delete g_profControlBlock.pProfInterface;
1421 g_profControlBlock.pProfInterface.Store(NULL);
1424 // NOTE: Intentionally not deleting s_pSidBuffer. Doing so can cause annoying races
1425 // with other threads that lazily create and initialize it when needed. (Example:
1426 // it's used to fill out the "User" field of profiler event log entries.) Keeping
1427 // s_pSidBuffer around after a profiler detaches and before a new one attaches
1428 // consumes a bit more memory unnecessarily, but it'll get paged out if another
1429 // profiler doesn't attach.
1431 // NOTE: Similarly, intentionally not destroying / NULLing s_csStatus. If
1432 // s_csStatus is already initialized, we can reuse it each time we do another
1433 // attach / detach, so no need to destroy it.
1435 // If we disabled concurrent GC and somehow failed later during the initialization
1436 if (g_profControlBlock.fConcurrentGCDisabledForAttach)
1438 // We know for sure GC has been fully initialized as we've turned off concurrent GC before
1439 _ASSERTE(IsGarbageCollectorFullyInitialized());
1440 GCHeapUtilities::GetGCHeap()->TemporaryEnableConcurrentGC();
1441 g_profControlBlock.fConcurrentGCDisabledForAttach = FALSE;
1444 // #ProfileResetSessionStatus Reset all the status variables that are for the current
1445 // profiling attach session.
1446 // When you are adding new status in g_profControlBlock, you need to think about whether
1447 // your new status is per-session, or consistent across sessions
1448 g_profControlBlock.ResetPerSessionStatus();
1450 g_profControlBlock.curProfStatus.Set(kProfStatusNone);
1456 // ----------------------------------------------------------------------------
1457 // ProfilingAPIUtility::GetCurrentProcessUserSid
1460 // Generates a SID of the current user from the current process's token. SID is
1461 // returned in an [out] param, and is also cached for future use. The SID is used for
1462 // two purposes: event log entries (for filling out the User field) and the ACL used
1463 // on the globally named pipe object for attaching profilers.
1466 // * ppsid - [out] Generated (or cached) SID
1469 // HRESULT indicating success or failure.
1473 HRESULT ProfilingAPIUtility::GetCurrentProcessUserSid(PSID * ppsid)
1483 if (s_pSidBuffer == NULL)
1486 NewHolder<SidBuffer> pSidBuffer(new (nothrow) SidBuffer);
1487 if (pSidBuffer == NULL)
1489 return E_OUTOFMEMORY;
1492 // This gets the SID of the user from the process token
1493 hr = pSidBuffer->InitFromProcessUserNoThrow(GetCurrentProcessId());
1499 if (FastInterlockCompareExchangePointer(
1501 pSidBuffer.GetValue(),
1504 // Lifetime successfully transferred to s_pSidBuffer, so don't delete it here
1505 pSidBuffer.SuppressRelease();
1509 _ASSERTE(s_pSidBuffer != NULL);
1510 _ASSERTE(s_pSidBuffer->GetSid().RawSid() != NULL);
1511 *ppsid = s_pSidBuffer->GetSid().RawSid();
1515 #endif // !FEATURE_PAL
1517 #endif // PROFILING_SUPPORTED