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//
161BOOL 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
209void 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.
286CRITSEC_COOKIE ProfilingAPIUtility::s_csStatus = NULL;
287
288
289SidBuffer * 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
305void 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
352void 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
390void 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
414void 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
442EXTERN_C void STDMETHODCALLTYPE ProfileEnterNaked(UINT_PTR clientData);
443EXTERN_C void STDMETHODCALLTYPE ProfileLeaveNaked(UINT_PTR clientData);
444EXTERN_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
476HRESULT 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
571HRESULT 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
669HRESULT 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
784HRESULT 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
848HRESULT 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
1257void 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
1383HRESULT 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