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// ProfAttach.cpp
6//
7
8//
9// Definitions of functions that help with attaching and detaching profilers
10//
11
12// ======================================================================================
13
14
15#include "common.h"
16
17#ifdef FEATURE_PROFAPI_ATTACH_DETACH
18
19#include <sddl.h> // Windows security descriptor language
20#include <SecurityUtil.h>
21#include "eeprofinterfaces.h"
22#include "eetoprofinterfaceimpl.h"
23#include "corprof.h"
24#include "proftoeeinterfaceimpl.h"
25#include "proftoeeinterfaceimpl.inl"
26#include "profilinghelper.h"
27#include "profilinghelper.inl"
28#include "profattach.h"
29#include "profattach.inl"
30#include "securitywrapper.h"
31#include "profattachserver.h"
32#include "profattachserver.inl"
33#include "profattachclient.h"
34#include "profdetach.h"
35
36PSECURITY_DESCRIPTOR ProfilingAPIAttachDetach::s_pSecurityDescriptor = NULL;
37HANDLE ProfilingAPIAttachDetach::s_hAttachEvent = NULL;
38ProfilingAPIAttachDetach::AttachThreadingMode ProfilingAPIAttachDetach::s_attachThreadingMode =
39 ProfilingAPIAttachDetach::kUninitialized;
40BOOL ProfilingAPIAttachDetach::s_fInitializeCalled = FALSE;
41
42
43// ----------------------------------------------------------------------------
44// ProfilingAPIAttachDetach::OverlappedResultHolder implementation. See
45// code:ProfilingAPIAttachDetach::OverlappedResultHolder for more information
46//
47
48// ----------------------------------------------------------------------------
49// ProfilingAPIAttachDetach::OverlappedResultHolder::Initialize
50//
51// Description:
52// Call this first! This initializes the contained OVERLAPPED structure
53//
54// Return Value:
55// Returns E_OUTOFMEMORY if OVERLAPPED structure could not be allocated.
56// Else S_OK.
57//
58
59HRESULT ProfilingAPIAttachDetach::OverlappedResultHolder::Initialize()
60{
61 CONTRACTL
62 {
63 NOTHROW;
64 GC_NOTRIGGER;
65 SO_INTOLERANT;
66 MODE_ANY;
67 }
68 CONTRACTL_END;
69
70 Assign(new (nothrow) OVERLAPPED);
71 if (m_value == NULL)
72 {
73 return E_OUTOFMEMORY;
74 }
75
76 memset(m_value, 0, sizeof(OVERLAPPED));
77 return S_OK;
78}
79
80// ----------------------------------------------------------------------------
81// ProfilingAPIAttachDetach::OverlappedResultHolder::Wait
82//
83// Description:
84// Uses the contained OVERLAPPED structure (pointed to by m_value) to call
85// WaitForSingleObject to wait for an overlapped read or write on the pipe to complete
86// (or timeout).
87//
88// Arguments:
89// * dwMillisecondsMax - [in] Timeout for the wait
90// * hPipe - [in] Handle to the pipe object carrying out the request (may be either a
91// server or client pipe handle).
92// * pcbReceived - [out] Number of bytes received from the overlapped request
93//
94// Return Value:
95// HRESULT indicating success or failure
96//
97// Assumptions:
98// * Must call code:ProfilingAPIAttachDetach::OverlappedResultHolder::Initialize first
99
100HRESULT ProfilingAPIAttachDetach::OverlappedResultHolder::Wait(
101 DWORD dwMillisecondsMax,
102 HANDLE hPipe,
103 DWORD * pcbReceived)
104{
105 CONTRACTL
106 {
107 THROWS;
108 GC_TRIGGERS;
109 MODE_PREEMPTIVE;
110 CAN_TAKE_LOCK;
111 }
112 CONTRACTL_END;
113
114 _ASSERTE(IsValidHandle(hPipe));
115 _ASSERTE(m_value != NULL);
116 _ASSERTE(pcbReceived != NULL);
117
118 HRESULT hr = E_UNEXPECTED;
119
120 // Since the OVERLAPPED structure referenced by m_value contains a NULL event, the OS
121 // will signal hPipe itself when the operation is complete
122 switch (WaitForSingleObject(hPipe, dwMillisecondsMax))
123 {
124 default:
125 _ASSERTE(!"Unexpected return from WaitForSingleObject()");
126 hr = E_UNEXPECTED;
127 break;
128
129 case WAIT_FAILED:
130 hr = HRESULT_FROM_GetLastError();
131 break;
132
133 case WAIT_TIMEOUT:
134 hr = HRESULT_FROM_WIN32(ERROR_TIMEOUT);
135 break;
136
137 case WAIT_OBJECT_0:
138 // Operation finished in time. Get the results
139 if (!GetOverlappedResult(
140 hPipe,
141 m_value,
142 pcbReceived,
143 TRUE)) // bWait: operation is done, so this returns immediately anyway
144 {
145 hr = HRESULT_FROM_GetLastError();
146 }
147 else
148 {
149 hr = S_OK;
150 }
151 break;
152 }
153
154 // The gymnastics below are to ensure that Windows is done with the overlapped
155 // structure, so we know it's safe to allow the base class (NewHolder) to free it
156 // when the destructor is called.
157
158 if (SUCCEEDED(hr))
159 {
160 // Operation successful, so we're done with the OVERLAPPED structure pointed to
161 // by m_value and may return
162 return hr;
163 }
164
165 _ASSERTE(FAILED(hr));
166
167 // There was a failure waiting for or retrieving the result. Cancel the operation and
168 // wait again for verification that the operation is completed or canceled.
169
170 // Note that we're ignoring whether CancelIo succeeds or fails, as our action is the
171 // same either way: Wait on the pipe again to verify that no active operation remains.
172 CancelIo(hPipe);
173
174 if (WaitForSingleObject(hPipe, dwMillisecondsMax) == WAIT_OBJECT_0)
175 {
176 // Typical case: The wait returns successfully and quickly, so we have
177 // verification that the OVERLAPPED structured pointed to by m_value is done
178 // being used.
179 return hr;
180 }
181
182 // Atypical case: For all our trying, we're unable to force this request to end
183 // before returning. Therefore, we're intentionally leaking the OVERLAPPED structured
184 // pointed to by m_value, as Windows may write to it at a later time.
185 SuppressRelease();
186 return hr;
187}
188
189
190// ----------------------------------------------------------------------------
191// ProfilingAPIAttachDetach::ProfilingAPIAttachThreadStart
192//
193// Description:
194// Thread proc for AttachThread. Serves as simple try/catch wrapper around
195// ProfilingAPIAttachThreadMain
196//
197// Arguments:
198// * LPVOID thread proc param is ignored
199//
200// Return Value:
201// Just returns 0 always.
202//
203
204// static
205DWORD WINAPI ProfilingAPIAttachDetach::ProfilingAPIAttachThreadStart(LPVOID)
206{
207 CONTRACTL
208 {
209 NOTHROW;
210 GC_TRIGGERS;
211 MODE_PREEMPTIVE;
212 CAN_TAKE_LOCK;
213 }
214 CONTRACTL_END;
215
216 // At start of this thread, set its type so SOS !threads and anyone else knows who we
217 // are.
218 ClrFlsSetThreadType(ThreadType_ProfAPI_Attach);
219
220 LOG((
221 LF_CORPROF,
222 LL_INFO10,
223 "**PROF: AttachThread created and executing.\n"));
224
225 // This try block is a last-ditch stop-gap to prevent an unhandled exception on the
226 // AttachThread from bringing down the process. Note that if the unhandled
227 // exception is a terminal one, then hey, sure, let's tear everything down. Also
228 // note that any naughtiness in the profiler (e.g., throwing an exception from its
229 // Initialize callback) should already be handled before we pop back to here, so this
230 // is just being super paranoid.
231 EX_TRY
232 {
233 // Don't care about return value, thread proc will just return 0 regardless
234 ProfilingAPIAttachThreadMain();
235 }
236 EX_CATCH
237 {
238 _ASSERTE(!"Unhandled exception on profiling API attach / detach thread");
239 }
240 EX_END_CATCH(RethrowTerminalExceptions);
241
242 LOG((
243 LF_CORPROF,
244 LL_INFO10,
245 "**PROF: AttachThread exiting.\n"));
246
247 return 0;
248}
249
250// ----------------------------------------------------------------------------
251// ProfilingAPIAttachDetach::ProfilingAPIAttachThreadMain
252//
253// Description:
254// Main code for AttachThread. Includes all attach functionality.
255//
256// Return Value:
257// S_OK if a profiler ever attached, error HRESULT otherwise
258//
259
260// static
261HRESULT ProfilingAPIAttachDetach::ProfilingAPIAttachThreadMain()
262{
263 CONTRACTL
264 {
265 THROWS;
266 GC_TRIGGERS;
267 MODE_PREEMPTIVE;
268 CAN_TAKE_LOCK;
269 }
270 CONTRACTL_END;
271
272 HRESULT hr;
273
274 ProfilingAPIAttachServer attachServer;
275 hr = attachServer.ExecutePipeRequests();
276 if (FAILED(hr))
277 {
278 // No profiler got attached, so we're done
279 return hr;
280 }
281
282 // If we made it here, a profiler was successfully attached. It would be nice to be
283 // able to assert g_profControlBlock.curProfStatus.Get() == kProfStatusActive, but
284 // that's prone to a theoretical race: the profiler might have attached and detached
285 // by the time we get here.
286
287 return S_OK;
288}
289
290// ----------------------------------------------------------------------------
291// ProfilingAPIAttachDetach::InitSecurityAttributes
292//
293// Description:
294// Initializes a SECURITY_ATTRIBUTES struct using the result of
295// code:ProfilingAPIAttachDetach::GetSecurityDescriptor
296//
297// Arguments:
298// * pSecAttrs - [in/out] SECURITY_ATTRIBUTES struct to initialize
299// * cbSecAttrs - Size in bytes of *pSecAttrs
300//
301// Return Value:
302// HRESULT indicating success or failure
303//
304
305// static
306HRESULT ProfilingAPIAttachDetach::InitSecurityAttributes(
307 SECURITY_ATTRIBUTES * pSecAttrs,
308 DWORD cbSecAttrs)
309{
310 CONTRACTL
311 {
312 THROWS;
313 GC_TRIGGERS;
314 MODE_PREEMPTIVE;
315 CAN_TAKE_LOCK;
316 }
317 CONTRACTL_END;
318
319 PSECURITY_DESCRIPTOR psd = NULL;
320 HRESULT hr = GetSecurityDescriptor(&psd);
321 if (FAILED(hr))
322 {
323 return hr;
324 }
325
326 _ASSERTE(psd != NULL);
327 memset(pSecAttrs, 0, cbSecAttrs);
328 pSecAttrs->nLength = cbSecAttrs;
329 pSecAttrs->lpSecurityDescriptor = psd;
330 pSecAttrs->bInheritHandle = FALSE;
331
332 return S_OK;
333}
334
335//---------------------------------------------------------------------------------------
336//
337// Helper function that gets the string (SDDL) form of the mandatory SID for this
338// process. This encodes the integrity level of the process for use in security
339// descriptors. The integrity level is capped at "high". See code:#HighGoodEnough.
340//
341// Arguments:
342// * pwszIntegritySidString - [out] On return will point to a buffer allocated by
343// Windows that contains the string representation of the SID. If
344// GetIntegritySidString succeeds, the caller is responsible for freeing
345// *pwszIntegritySidString via LocalFree().
346//
347// Return Value:
348// HRESULT indicating success or failure.
349//
350//
351
352static HRESULT GetIntegritySidString(__out LPWSTR * pwszIntegritySidString)
353{
354 CONTRACTL
355 {
356 NOTHROW;
357 GC_NOTRIGGER;
358 MODE_ANY;
359 CANNOT_TAKE_LOCK;
360 }
361 CONTRACTL_END;
362
363 HRESULT hr;
364 _ASSERTE(pwszIntegritySidString != NULL);
365
366 NewArrayHolder<BYTE> pbLabel;
367
368 // This grabs the mandatory label SID of the current process. We will write this
369 // SID into the security descriptor, to ensure that triggers of lower integrity
370 // levels may NOT access the object... with one exception. See code:#HighGoodEnough
371 hr = SecurityUtil::GetMandatoryLabelFromProcess(GetCurrentProcess(), &pbLabel);
372 if (FAILED(hr))
373 {
374 return hr;
375 }
376
377 TOKEN_MANDATORY_LABEL * ptml = (TOKEN_MANDATORY_LABEL *) pbLabel.GetValue();
378
379 // #HighGoodEnough:
380 // The mandatory label SID we write into the security descriptor is the same as that
381 // of the current process, with one exception. If the current process's integrity
382 // level > high (e.g., ASP.NET running at "system" integrity level), then write
383 // "high" into the security descriptor instead of the current process's actual
384 // integrity level. This allows a high integrity trigger to access the object. This
385 // implements the policy that a high integrity level is "good enough" to profile any
386 // process, even if the target process is at an even higher integrity level than
387 // "high". Why have this policy:
388 // * A high integrity process represents an elevated admin, which morally equates
389 // to a principal that should have complete control over the machine. This
390 // includes debugging or profiling any process.
391 // * According to a security expert dev on Windows, integrity level is not a
392 // "security feature". It's mainly useful as defense-in-depth or to protect
393 // IE users and admins from themselves in most cases.
394 // * It's impossible to spawn a system integrity trigger process outside of
395 // session 0 services. So profiling ASP.NET would be crazy hard without this
396 // policy.
397 DWORD * pdwIntegrityLevel = SecurityUtil::GetIntegrityLevelFromMandatorySID(ptml->Label.Sid);
398 if (*pdwIntegrityLevel > SECURITY_MANDATORY_HIGH_RID)
399 {
400 *pdwIntegrityLevel = SECURITY_MANDATORY_HIGH_RID;
401 }
402
403 if (!ConvertSidToStringSid(ptml->Label.Sid, pwszIntegritySidString))
404 {
405 return HRESULT_FROM_GetLastError();
406 }
407
408 return S_OK;
409}
410
411
412// ----------------------------------------------------------------------------
413// ProfilingAPIAttachDetach::GetSecurityDescriptor
414//
415// Description:
416// Generates a security descriptor based on an ACL containing (1) an ACE that allows
417// the current user read / write and (2) an ACE that allows admins read / write.
418// Resulting security descriptor is returned in an [out] param, and is also cached for
419// future use.
420//
421// Arguments:
422// * ppsd - [out] Generated (or cached) security descriptor
423//
424// Return Value:
425// HRESULT indicating success or failure.
426//
427
428// static
429HRESULT ProfilingAPIAttachDetach::GetSecurityDescriptor(PSECURITY_DESCRIPTOR * ppsd)
430{
431 CONTRACTL
432 {
433 THROWS;
434 GC_TRIGGERS;
435 MODE_PREEMPTIVE;
436 CAN_TAKE_LOCK;
437 }
438 CONTRACTL_END;
439
440 _ASSERTE(ppsd != NULL);
441
442 if (s_pSecurityDescriptor != NULL)
443 {
444 *ppsd = s_pSecurityDescriptor;
445 return S_OK;
446 }
447
448 // Get the user SID for the DACL
449
450 PSID psidUser = NULL;
451 HRESULT hr = ProfilingAPIUtility::GetCurrentProcessUserSid(&psidUser);
452 if (FAILED(hr))
453 {
454 return hr;
455 }
456
457 WinAllocatedBlockHolder pvCurrentUserSidString;
458
459 if (!ConvertSidToStringSid(psidUser, (LPWSTR *)(LPVOID *) &pvCurrentUserSidString))
460 {
461 return HRESULT_FROM_GetLastError();
462 }
463
464 // Get the integrity / mandatory SID for the SACL, if Vista+
465
466 LPCWSTR pwszIntegritySid = NULL;
467 WinAllocatedBlockHolder pvIntegritySidString;
468
469 hr = GetIntegritySidString((LPWSTR *) (LPVOID *) &pvIntegritySidString);
470 if (FAILED(hr))
471 {
472 return hr;
473 }
474 pwszIntegritySid = (LPCWSTR) pvIntegritySidString.GetValue();
475
476 ULONG cbsd;
477 StackSString sddlSecurityDescriptor;
478 WinAllocatedBlockHolder pvSecurityDescriptor;
479
480 // The following API (ConvertStringSecurityDescriptorToSecurityDescriptorW) takes a
481 // string representation of a security descriptor (using the SDDL language), and
482 // returns back the security descriptor object to be used when defining the globally
483 // named event or pipe object. For a description of this language, go to the help on
484 // the API, and click on "string-format security descriptor":
485 // http://msdn.microsoft.com/library/default.asp?url=/library/en-us/secauthz/security/security_descriptor_string_format.asp
486 // or look through sddl.h.
487
488 // Cheat sheet for the subset of the format that we're using:
489 //
490 // Security Descriptor string:
491 // D:dacl_flags(string_ace1)(string_ace2)... (string_acen)
492 // Security SACL string:
493 // S:sacl_flags(string_ace1)(string_ace2)... (string_acen)
494 // Each string_ace:
495 // ace_type;ace_flags;rights;object_guid;inherit_object_guid;account_sid
496 //
497 // The following portions of the security descriptor string are NOT used:
498 // O:owner_sid (b/c we want current user to be the owner)
499 // G:group_sid (b/c not setting the primary group of the object)
500
501 // This reusable chunk defines the "(string_ace)" portion of the DACL. Given
502 // a SID, this makes an ACE for the SID with GENERIC_READ | GENERIC_WRITE access
503 #define ACE_STRING(AccountSidString) \
504 \
505 SDDL_ACE_BEGIN \
506 \
507 /* ace_type: "A;" An "allow" DACL (not "deny") */ \
508 SDDL_ACCESS_ALLOWED SDDL_SEPERATOR \
509 \
510 /* (skipping ace_flags, so that no child auto-inherits from this object) */ \
511 SDDL_SEPERATOR \
512 \
513 /* rights: "GRGW": GENERIC_READ | GENERIC_WRITE access allowed */ \
514 SDDL_GENERIC_READ SDDL_GENERIC_WRITE SDDL_SEPERATOR \
515 \
516 /* (skipping object_guid) */ \
517 SDDL_SEPERATOR \
518 \
519 /* (skipping inherit_object_guid) */ \
520 SDDL_SEPERATOR \
521 \
522 /* account_sid (filled in by macro user) */ \
523 AccountSidString \
524 \
525 SDDL_ACE_END
526
527
528 // First, construct the DACL
529
530 sddlSecurityDescriptor.Printf(
531 // "D:" This is a DACL
532 SDDL_DACL SDDL_DELIMINATOR
533
534 // dacl_flags:
535
536 // "P" This is protected (i.e., don't allow security descriptor to be modified
537 // by inheritable ACEs)
538 SDDL_PROTECTED
539
540 // (string_ace1)
541 // account_sid: "BA" built-in local administrators group
542 ACE_STRING(SDDL_BUILTIN_ADMINISTRATORS)
543
544 // (string_ace2)
545 // account_sid: to be filled in with the current process token's primary SID
546 ACE_STRING(W("%s")),
547
548 // current process token's primary SID
549 (LPCWSTR) (LPVOID) pvCurrentUserSidString);
550
551 // Next, add the SACL (Vista+ only)
552
553 if (pwszIntegritySid != NULL)
554 {
555 sddlSecurityDescriptor.AppendPrintf(
556 // "S:" This is a SACL -- for the integrity level of the current process
557 SDDL_SACL SDDL_DELIMINATOR
558
559 // The SACL ACE begins here
560 SDDL_ACE_BEGIN
561
562 // ace_type: "ML;" A Mandatory Label ACE (i.e., integrity level)
563 SDDL_MANDATORY_LABEL SDDL_SEPERATOR
564
565 // (skipping ace_flags, so that no child auto-inherits from this object)
566 SDDL_SEPERATOR
567
568 // rights: "NWNR;" If the trigger's integrity level is lower than the
569 // integrity level we're writing into this security descriptor, then that
570 // trigger may not read or write to this object.
571 SDDL_NO_WRITE_UP SDDL_NO_READ_UP SDDL_SEPERATOR
572
573 // (skipping object_guid)
574 SDDL_SEPERATOR
575
576 // (skipping inherit_object_guid)
577 SDDL_SEPERATOR
578
579 // To be filled in with the current process's mandatory label SID (which
580 // describes the current process's integrity level, capped at "high integrity")
581 W("%s")
582
583 SDDL_ACE_END,
584
585 // current process's mandatory label SID
586 pwszIntegritySid);
587 }
588
589 if (!ConvertStringSecurityDescriptorToSecurityDescriptorW(
590 sddlSecurityDescriptor.GetUnicode(),
591 SDDL_REVISION_1,
592 (PSECURITY_DESCRIPTOR *) (LPVOID *) &pvSecurityDescriptor,
593 &cbsd))
594 {
595 return HRESULT_FROM_GetLastError();
596 }
597
598 if (FastInterlockCompareExchangePointer(
599 &s_pSecurityDescriptor,
600 (PSECURITY_DESCRIPTOR) pvSecurityDescriptor,
601 NULL) == NULL)
602 {
603 // Ownership transferred to s_pSecurityDescriptor, so don't free it here
604 pvSecurityDescriptor.SuppressRelease();
605 }
606
607 _ASSERTE(s_pSecurityDescriptor != NULL);
608 *ppsd = s_pSecurityDescriptor;
609 return S_OK;
610}
611
612
613// ----------------------------------------------------------------------------
614// ProfilingAPIAttachDetach::Initialize
615//
616// Description:
617// Perform startup (one-time-only) initialization for attach / detach infrastructure.
618// This includes the Global Attach Event, but does NOT include the Global Attach Pipe
619// (which is created only on demand). This is lazily called the first time the
620// finalizer asks for the attach event.
621//
622// Return Value:
623// S_OK: Attach / detach infrastructure initialized ok
624// S_FALSE: Attach / detach infrastructure not initialized, but for an acceptable reason
625// (e.g., executing memory- or sync- hosted)
626// else: error HRESULT indicating an unacceptable failure that prevented attach /
627// detach infrastructure from initializing (e.g., security problem, OOM, etc.)
628//
629// Assumptions:
630// * By the time this is called:
631// * Configuration must have been read from the registry
632// * If there is a host, it has already initialized its state, including its
633// intent to memory-host or sync-host.
634// * Finalizer thread is initializing and is first asking for the attach event.
635//
636
637// static
638HRESULT ProfilingAPIAttachDetach::Initialize()
639{
640 CONTRACTL
641 {
642 THROWS;
643 GC_TRIGGERS;
644 MODE_PREEMPTIVE;
645 CAN_TAKE_LOCK;
646 }
647 CONTRACTL_END;
648
649 // This one assert verifies two things:
650 // * 1. Configuration has been read from the registry, AND
651 // * 2. If there is a host, it has already initialized its state.
652 // #2 is implied by this assert, because the host initializes its state before
653 // EEStartup is even called: Host directly calls CorHost2::SetHostControl to
654 // initialize itself, announce whether the CLR will be memory hosted, sync hosted,
655 // etc., and then host calls CorHost2::Start, which calls EEStartup, which
656 // initializes configuration information. So if configuration information is
657 // available, the host must have already initialized itself.
658 //
659 // The reason we care is that, for profiling API attach to be enabled during this
660 // run, we need to have the finalizer thread wait on multiple sync objects. And
661 // waiting on multiple objects is disallowed if we're memory / sync-hosted. So we
662 // need to know now whether waiting on multiple objects is allowed, so we know
663 // whether we can initialize the Attach support objects.
664 _ASSERTE(g_pConfig != NULL);
665
666 // Even if we fail to create the event, this BOOL indicates we at least
667 // tried to.
668 _ASSERTE(!s_fInitializeCalled);
669 s_fInitializeCalled = TRUE;
670
671 INDEBUG(VerifyMessageStructureLayout());
672
673 InitializeAttachThreadingMode();
674
675 if (s_attachThreadingMode == kOnDemand)
676 {
677 return InitializeForOnDemandMode();
678 }
679
680 _ASSERTE(s_attachThreadingMode == kAlwaysOn);
681 return InitializeForAlwaysOnMode();
682}
683
684#ifdef _DEBUG
685
686// ----------------------------------------------------------------------------
687// ProfilingAPIAttachDetach::VerifyMessageStructureLayout
688//
689// Description:
690// Debug-only function that asserts if there appear to be changes to structures that
691// are not allowed to change (for backward-compatibility reasons). In particular:
692// * BaseRequestMessage must not change
693//
694
695// static
696void ProfilingAPIAttachDetach::VerifyMessageStructureLayout()
697{
698 LIMITED_METHOD_CONTRACT;
699
700 // If any of these asserts fire, then GetVersionRequestMessage is changing its binary
701 // layout in an incompatible way. Bad!
702 _ASSERTE(sizeof(GetVersionRequestMessage) == 8);
703 _ASSERTE(offsetof(GetVersionRequestMessage, m_cbMessage) == 0);
704 _ASSERTE(offsetof(GetVersionRequestMessage, m_requestMessageType) == 4);
705
706 // If any of these asserts fire, then GetVersionResponseMessage is changing its binary
707 // layout in an incompatible way. Bad!
708 _ASSERTE(sizeof(GetVersionResponseMessage) == 12);
709 _ASSERTE(offsetof(GetVersionResponseMessage, m_hr) == 0);
710 _ASSERTE(offsetof(GetVersionResponseMessage, m_profileeVersion) == 4);
711 _ASSERTE(offsetof(GetVersionResponseMessage, m_minimumAllowableTriggerVersion) == 8);
712}
713
714#endif //_DEBUG
715
716// ----------------------------------------------------------------------------
717// ProfilingAPIAttachDetach::InitializeAttachThreadingMode
718//
719// Description:
720// Looks at environment and GC mode to determine whether the AttachThread should
721// always be around, or created only on demand. See
722// code:ProfilingAPIAttachDetach::AttachThreadingMode.
723//
724
725// static
726void ProfilingAPIAttachDetach::InitializeAttachThreadingMode()
727{
728 CONTRACTL
729 {
730 THROWS;
731 GC_TRIGGERS;
732 MODE_PREEMPTIVE;
733 CAN_TAKE_LOCK;
734 }
735 CONTRACTL_END;
736
737 _ASSERTE(s_attachThreadingMode == kUninitialized);
738
739 // Environment variable trumps all, so check it first
740 DWORD dwAlwaysOn = g_pConfig->GetConfigDWORD_DontUse_(
741 CLRConfig::EXTERNAL_AttachThreadAlwaysOn,
742 GCHeapUtilities::IsServerHeap() ? 1 : 0); // Default depends on GC server mode
743
744 if (dwAlwaysOn == 0)
745 {
746 s_attachThreadingMode = kOnDemand;
747 }
748 else
749 {
750 s_attachThreadingMode = kAlwaysOn;
751 }
752}
753
754
755// ----------------------------------------------------------------------------
756// ProfilingAPIAttachDetach::InitializeForAlwaysOnMode
757//
758// Description:
759// Performs initialization specific to running in Always On mode. Specifically, this
760// means creating the AttachThread. The attach event is not created in this case.
761//
762// Return Value:
763// HRESULT indicating success or failure.
764//
765
766// static
767HRESULT ProfilingAPIAttachDetach::InitializeForAlwaysOnMode()
768{
769 CONTRACTL
770 {
771 THROWS;
772 GC_TRIGGERS;
773 MODE_PREEMPTIVE;
774 CAN_TAKE_LOCK;
775 }
776 CONTRACTL_END;
777
778 _ASSERTE(s_attachThreadingMode == kAlwaysOn);
779
780 LOG((LF_CORPROF, LL_INFO10, "**PROF: Attach AlwaysOn mode invoked; creating new AttachThread.\n"));
781
782 CreateAttachThread();
783
784 return S_OK;
785}
786
787// ----------------------------------------------------------------------------
788// ProfilingAPIAttachDetach::InitializeForOnDemandMode
789//
790// Description:
791// Performs initialization specific to running in On Demand mode. Specifically, this
792// means creating the attach event. (The AttachThread will only be created when this
793// event is signaled by a trigger process.)
794//
795// Return Value:
796// HRESULT indicating success or failure.
797//
798
799// static
800HRESULT ProfilingAPIAttachDetach::InitializeForOnDemandMode()
801{
802 CONTRACTL
803 {
804 THROWS;
805 GC_TRIGGERS;
806 MODE_PREEMPTIVE;
807 CAN_TAKE_LOCK;
808 }
809 CONTRACTL_END;
810
811 _ASSERTE(s_attachThreadingMode == kOnDemand);
812
813 LOG((LF_CORPROF, LL_INFO10, "**PROF: Attach OnDemand mode invoked; creating attach event.\n"));
814
815 // The only part of attach that gets initialized before a profiler has
816 // actually requested to attach is the single global event that gets
817 // signaled from out-of-process.
818
819 StackSString attachEventName;
820 HRESULT hr;
821 hr = GetAttachEventName(::GetCurrentProcess(), &attachEventName);
822 if (FAILED(hr))
823 {
824 return hr;
825 }
826
827 // Deliberately NOT using CLREvent, as it does not have support for a global name.
828 // It's ok not to use CLREvent, as we're assured above that we're not sync-hosted,
829 // which means CLREvent would just use raw Windows events anyway.
830
831 SECURITY_ATTRIBUTES *psa = NULL;
832
833 SECURITY_ATTRIBUTES sa;
834
835 // Only assign security attributes for non-app container scenario
836 // We are assuming the default (blocking everything for app container scenario is good enough
837 if (!IsAppContainerProcess(::GetCurrentProcess()))
838 {
839 hr = InitSecurityAttributes(&sa, sizeof(sa));
840 if (FAILED(hr))
841 {
842 return hr;
843 }
844
845 psa = &sa;
846 }
847
848 _ASSERTE(s_hAttachEvent == NULL);
849 s_hAttachEvent = WszCreateEvent(
850 psa, // security attributes
851 FALSE, // bManualReset = FALSE: autoreset after waiting thread is unblocked
852 FALSE, // initial state = FALSE, i.e., unsignaled
853 attachEventName.GetUnicode() // Global name seen out-of-proc
854 );
855 if (s_hAttachEvent == NULL)
856 {
857 return HRESULT_FROM_GetLastError();
858 }
859
860 return S_OK;
861}
862
863// ----------------------------------------------------------------------------
864// ProfilingAPIAttachDetach::GetAttachEvent
865//
866// Description:
867// Used by finalizer thread to get the profiling API attach event. First time this is
868// called, the event and other supporting objects will be created.
869//
870// Return Value:
871// The attach event or NULL if attach event creation failed during startup. In either
872// case, do NOT call CloseHandle on the returned event handle.
873//
874// Assumptions:
875// * ProfilingAPIUtility::InitializeProfiling should already have been called before
876// this is called. That ensures that, if a profiler was configured to load on
877// startup, then that load has already occurred by now.
878// * The event's HANDLE refcount is managed solely by ProfilingAPIAttachDetach. So do
879// not call CloseHandle() on the HANDLE returned.
880//
881// Notes:
882// * If the attach event was not created on startup, then this will return NULL.
883// Possible reasons why this can occur:
884// * The current process is the NGEN service, OR
885// * The process is sync- or memory- hosted, OR
886// * Attach is running in "always on" mode, meaning we always have an AttachThread
887// with a pipe, so there's no need for an event.
888//
889
890// static
891HANDLE ProfilingAPIAttachDetach::GetAttachEvent()
892{
893 CONTRACTL
894 {
895 THROWS;
896 GC_TRIGGERS;
897 MODE_PREEMPTIVE;
898 CAN_TAKE_LOCK;
899 }
900 CONTRACTL_END;
901
902 if (IsCompilationProcess())
903 {
904 // No profiler attach on NGEN!
905 return NULL;
906 }
907
908 if (!s_fInitializeCalled)
909 {
910 // If a profiler was supposed to load on startup, it's already happened
911 // now. So it's safe to set up the attach support objects, and allow
912 // an attaching profiler to make an attempt (which can now gracefully fail
913 // if a startup profiler has loaded).
914
915 HRESULT hr = Initialize();
916 if (FAILED(hr))
917 {
918 LOG((
919 LF_CORPROF,
920 LL_ERROR,
921 "**PROF: ProfilingAPIAttachDetach::Initialize failed, so this process will not "
922 "be able to attach a profiler. hr=0x%x.\n",
923 hr));
924 ProfilingAPIUtility::LogProfError(IDS_E_PROF_ATTACH_INIT, hr);
925
926 return NULL;
927 }
928 }
929
930 if (s_attachThreadingMode == kAlwaysOn)
931 {
932 // In always-on mode, we always have an AttachThread listening on the pipe, so
933 // there's no need for an event.
934 _ASSERTE(s_hAttachEvent == NULL);
935 }
936
937 return s_hAttachEvent;
938}
939
940
941// ----------------------------------------------------------------------------
942// ProfilingAPIAttachDetach::ProcessSignaledAttachEvent
943//
944// Description:
945// Called by finalizer thread when the finalizer thread detects that the globally
946// named Profiler Attach Event is signaled. This simply spins up the AttachThread
947// (starting in ProfilingAPIAttachThreadStart) and returns.
948//
949
950// static
951void ProfilingAPIAttachDetach::ProcessSignaledAttachEvent()
952{
953 // This function is practically a leaf (though not quite), and is called from the
954 // finalizer thread at various points, so keeping the contract strict to allow for
955 // maximum flexibility on when this may called.
956 CONTRACTL
957 {
958 NOTHROW;
959 GC_NOTRIGGER;
960 MODE_ANY;
961 CANNOT_TAKE_LOCK;
962 }
963 CONTRACTL_END;
964
965 LOG((LF_CORPROF, LL_INFO10, "**PROF: Attach event signaled; creating new AttachThread.\n"));
966
967 CreateAttachThread();
968}
969
970typedef BOOL
971(WINAPI *PFN_GetAppContainerNamedObjectPath)(
972 HANDLE Token,
973 PSID AppContainerSid,
974 ULONG ObjectPathLength,
975 WCHAR * ObjectPath,
976 PULONG ReturnLength
977 );
978
979static Volatile<PFN_GetAppContainerNamedObjectPath> g_pfnGetAppContainerNamedObjectPath = NULL;
980
981// ----------------------------------------------------------------------------
982// GetAppContainerNamedObjectPath
983//
984// Description:
985// Retrieve named object path for the specified app container process
986// The name looks something like the following:
987// LowBoxNamedObjects\<AppContainer_SID>
988// AppContainer_SID is the SID for the app container, for example: S-1-15-2-3-4-5-6-7-8
989//
990// Arguments:
991// * hProcess - handle of the app container proces
992// * wszObjectPath - [out] Buffer to fill in
993// * dwObjectPathSizeInChar - Size of buffer
994//
995HRESULT ProfilingAPIAttachDetach::GetAppContainerNamedObjectPath(HANDLE hProcess, __out_ecount(dwObjectPathSizeInChar) WCHAR * wszObjectPath, DWORD dwObjectPathSizeInChar)
996{
997 LIMITED_METHOD_CONTRACT;
998
999 _ASSERTE(wszObjectPath != NULL);
1000
1001 HandleHolder hToken;
1002
1003 if (!OpenProcessToken(hProcess, TOKEN_QUERY, &hToken))
1004 {
1005 return HRESULT_FROM_GetLastError();
1006 }
1007
1008 if (g_pfnGetAppContainerNamedObjectPath.Load() == NULL)
1009 {
1010 HMODULE hMod = WszGetModuleHandle(W("kernel32.dll"));
1011 if (hMod == NULL)
1012 {
1013 // This should never happen but I'm checking it anyway
1014 return HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
1015 }
1016
1017 PFN_GetAppContainerNamedObjectPath pfnGetAppContainerNamedObjectPath = (PFN_GetAppContainerNamedObjectPath)
1018 ::GetProcAddress(
1019 hMod,
1020 "GetAppContainerNamedObjectPath");
1021
1022 if (!pfnGetAppContainerNamedObjectPath)
1023 {
1024
1025 return HRESULT_FROM_GetLastError();
1026 }
1027
1028 // We should always get the same address back from GetProcAddress so there is no concern for race condition
1029 g_pfnGetAppContainerNamedObjectPath = pfnGetAppContainerNamedObjectPath;
1030 }
1031
1032 DWORD dwBufferLength;
1033 if (!g_pfnGetAppContainerNamedObjectPath(
1034 hToken, // Process token
1035 NULL, // AppContainer package SID optional.
1036 dwObjectPathSizeInChar, // Object path length
1037 wszObjectPath, // Object path
1038 &dwBufferLength // return length
1039 ))
1040 {
1041 return HRESULT_FROM_GetLastError();
1042 }
1043
1044 return S_OK;
1045}
1046
1047
1048// @TODO: Update this once Windows header file is updated to Win8
1049#ifndef TokenIsAppContainer
1050 #define TokenIsAppContainer ((TOKEN_INFORMATION_CLASS) 29)
1051#endif
1052
1053// ----------------------------------------------------------------------------
1054// ProfilingAPIAttachDetach::IsAppContainerProcess
1055//
1056// Description:
1057// Return whether the specified process is a app container process
1058//
1059
1060// static
1061BOOL ProfilingAPIAttachDetach::IsAppContainerProcess(HANDLE hProcess)
1062{
1063 CONTRACTL
1064 {
1065 NOTHROW;
1066 GC_NOTRIGGER;
1067 MODE_ANY;
1068 CANNOT_TAKE_LOCK;
1069 }
1070 CONTRACTL_END;
1071
1072 HandleHolder hToken;
1073
1074 if(!::OpenProcessToken(hProcess, TOKEN_QUERY, &hToken))
1075 {
1076 return FALSE;
1077 }
1078
1079 BOOL fIsAppContainerProcess;
1080 DWORD dwReturnLength;
1081 if (!::GetTokenInformation(
1082 hToken,
1083 TokenIsAppContainer,
1084 &fIsAppContainerProcess,
1085 sizeof(BOOL),
1086 &dwReturnLength) ||
1087 dwReturnLength != sizeof(BOOL))
1088 {
1089 return FALSE;
1090 }
1091 else
1092 {
1093 return fIsAppContainerProcess;
1094 }
1095}
1096
1097//---------------------------------------------------------------------------------------
1098//
1099// Called by other points in the runtime (e.g., finalizer thread) to create a new thread
1100// to fill the role of the AttachThread.
1101//
1102
1103// static
1104void ProfilingAPIAttachDetach::CreateAttachThread()
1105{
1106 // This function is practically a leaf (though not quite), and is called from the
1107 // finalizer thread at various points, so keeping the contract strict to allow for
1108 // maximum flexibility on when this may called.
1109 CONTRACTL
1110 {
1111 NOTHROW;
1112 GC_NOTRIGGER;
1113 MODE_ANY;
1114 CANNOT_TAKE_LOCK;
1115 }
1116 CONTRACTL_END;
1117
1118 HandleHolder hAttachThread;
1119
1120 // The AttachThread is intentionally not an EE Thread-object thread
1121 hAttachThread = ::CreateThread(
1122 NULL, // lpThreadAttributes; don't want child processes inheriting this handle
1123 0, // dwStackSize (0 = use default)
1124 ProfilingAPIAttachThreadStart,
1125 NULL, // lpParameter (none to pass)
1126 0, // dwCreationFlags (0 = use default flags, start thread immediately)
1127 NULL // lpThreadId (don't need therad ID)
1128 );
1129 if (hAttachThread == NULL)
1130 {
1131 LOG((
1132 LF_CORPROF,
1133 LL_ERROR,
1134 "**PROF: Failed to create AttachThread. GetLastError=%d.\n",
1135 GetLastError()));
1136
1137 // No other error-specific code really makes much sense here. An error here is
1138 // probably due to serious OOM issues which would also probably prevent logging
1139 // an event. A trigger process will report that it waited for the pipe to be
1140 // created, and timed out during the wait. That should be enough for the user.
1141 }
1142}
1143
1144// ----------------------------------------------------------------------------
1145// CLRProfilingImpl::AttachProfiler
1146//
1147// Description:
1148// A wrapper COM function to invoke AttachProfiler with parameters from
1149// profiling trigger along with a runtime version string
1150//
1151HRESULT CLRProfilingImpl::AttachProfiler(DWORD dwProfileeProcessID,
1152 DWORD dwMillisecondsMax,
1153 const CLSID *pClsidProfiler,
1154 LPCWSTR wszProfilerPath,
1155 void *pvClientData,
1156 UINT cbClientData)
1157{
1158 CONTRACTL
1159 {
1160 NOTHROW;
1161 GC_TRIGGERS;
1162 MODE_PREEMPTIVE;
1163 CAN_TAKE_LOCK;
1164 SO_NOT_MAINLINE;
1165 }
1166 CONTRACTL_END;
1167
1168 WCHAR wszRuntimeVersion[MAX_PATH_FNAME];
1169 DWORD dwSize = _countof(wszRuntimeVersion);
1170 HRESULT hr = GetCORVersionInternal(wszRuntimeVersion, dwSize, &dwSize);
1171 if (FAILED(hr))
1172 return hr;
1173
1174 return ::AttachProfiler(dwProfileeProcessID,
1175 dwMillisecondsMax,
1176 pClsidProfiler,
1177 wszProfilerPath,
1178 pvClientData,
1179 cbClientData,
1180 wszRuntimeVersion);
1181}
1182
1183
1184// Contract for public APIs. These must be NOTHROW.
1185EXTERN_C const IID IID_ICLRProfiling;
1186
1187HRESULT
1188CreateCLRProfiling(
1189 __out void ** ppCLRProfilingInstance)
1190{
1191 CONTRACTL
1192 {
1193 NOTHROW;
1194 }
1195 CONTRACTL_END;
1196
1197 HRESULT hrIgnore = S_OK; // ignored HResult
1198 HRESULT hr = S_OK;
1199 HMODULE hMod = NULL;
1200 IUnknown * pCordb = NULL;
1201
1202 LOG((LF_CORDB, LL_EVERYTHING, "Calling CreateCLRProfiling"));
1203
1204 NewHolder<CLRProfilingImpl> pProfilingImpl = new (nothrow) CLRProfilingImpl();
1205 if (pProfilingImpl == NULL)
1206 return E_OUTOFMEMORY;
1207
1208 hr = pProfilingImpl->QueryInterface(IID_ICLRProfiling, ppCLRProfilingInstance);
1209 if (SUCCEEDED(hr))
1210 {
1211 pProfilingImpl.SuppressRelease();
1212 return S_OK;
1213 }
1214 return E_FAIL;
1215}
1216
1217#endif // FEATURE_PROFAPI_ATTACH_DETACH
1218