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// ProfAttachServer.cpp
6//
7
8//
9// Implementation of ProfilingAPIAttachServer, which is instantiated on the stack of the
10// AttachThread running in the target profilee (server end of the pipe) to receive and
11// carry out requests that are sent by the trigger (client end of the pipe).
12//
13// Most of the contracts in this file follow the lead of default contracts throughout the
14// CLR (triggers, throws, etc.) and many are marked as CAN_TAKE_LOCK, as event logging
15// happens all over the place, and that loads resource strings, which takes locks. Some
16// notes:
17// * MODE_PREEMPTIVE also allows for GetThread() == NULL, which will be the case for
18// most of these functions most of the time (as most are called on the
19// AttachThread).
20// * NOTHROW is used at the root of the AttachThread (to protect AttachThread from
21// unhandled exceptions which would tear down the entire process), and at the
22// root of the AttachProfiler() API (to protect trigger processes from unhandled
23// exceptions).
24//
25
26// ======================================================================================
27
28#include "common.h"
29
30#ifdef FEATURE_PROFAPI_ATTACH_DETACH
31
32#include "profilinghelper.h"
33#include "profilinghelper.inl"
34#include "profattach.h"
35#include "profattach.inl"
36#include "profattachserver.h"
37#include "profattachserver.inl"
38
39
40// ----------------------------------------------------------------------------
41// Implementation of RequestMessageVerifier; a helper to verify incoming messages to the
42// target profilee.
43//
44
45// ----------------------------------------------------------------------------
46// RequestMessageVerifier::Verify
47//
48// Description:
49// Verifies self-consistency of a request message expressed as a byte array from
50// the pipe. This also calls the appropriate helper to check consistency of the
51// derived request message type, based on the kind of request this is.
52//
53// Return Value:
54// S_OK or CORPROF_E_UNRECOGNIZED_PIPE_MSG_FORMAT
55//
56
57HRESULT RequestMessageVerifier::Verify()
58{
59 CONTRACTL
60 {
61 NOTHROW;
62 GC_NOTRIGGER;
63 MODE_ANY;
64 CANNOT_TAKE_LOCK;
65 }
66 CONTRACTL_END;
67
68 // In the beginning, the message is not yet verified
69 _ASSERTE(!m_fVerified);
70
71 HRESULT hr = CORPROF_E_UNRECOGNIZED_PIPE_MSG_FORMAT;
72
73 // First, do we have something big enough to fit in a BaseRequestMessage?
74 if (m_cbRequestMessage < sizeof(BaseRequestMessage))
75 {
76 return CORPROF_E_UNRECOGNIZED_PIPE_MSG_FORMAT;
77 }
78
79 // Yes, but do the fields lie?
80 const BaseRequestMessage * pUnverifiedBaseRequestMessage
81 = (const BaseRequestMessage *) m_pbRequestMessage;
82
83 // Does the struct claim a size different than the entire message?
84 if (pUnverifiedBaseRequestMessage->m_cbMessage != m_cbRequestMessage)
85 {
86 return CORPROF_E_UNRECOGNIZED_PIPE_MSG_FORMAT;
87 }
88
89 // Check for an unknown type, or a known type but with invalid subclass fields
90 switch(pUnverifiedBaseRequestMessage->m_requestMessageType)
91 {
92 default:
93 // Unknown message type
94 hr = CORPROF_E_UNRECOGNIZED_PIPE_MSG_FORMAT;
95 break;
96
97 case kMsgGetVersion:
98 hr = VerifyGetVersionRequestMessage();
99 break;
100
101 case kMsgAttach:
102 hr = VerifyAttachRequestMessage();
103 break;
104 }
105
106 // For debug builds, remember whether we successfully verified the message
107 INDEBUG(m_fVerified = SUCCEEDED(hr));
108 return hr;
109}
110
111// ----------------------------------------------------------------------------
112// RequestMessageVerifier::VerifyGetVersionRequestMessage
113//
114// Description:
115// Once a BaseRequestMessage has been verified as self-consistent, and is of type
116// kMsgGetVersion, this helper is called to verify consistency as a Get Version
117// message
118//
119// Return Value:
120// S_OK or CORPROF_E_UNRECOGNIZED_PIPE_MSG_FORMAT
121//
122// Assumptions:
123// * Verify() calls this, but only after it has verified base type
124//
125
126HRESULT RequestMessageVerifier::VerifyGetVersionRequestMessage()
127{
128 LIMITED_METHOD_CONTRACT;
129
130 const BaseRequestMessage * pBaseRequestMessage =
131 (const BaseRequestMessage *) m_pbRequestMessage;
132
133 // Not much to verify here, since the get version request message is simply a
134 // BaseRequestMessage (no subtype)
135
136 // Not allowed to call this unless you checked the m_requestMessageType first!
137 _ASSERTE(pBaseRequestMessage->m_requestMessageType == kMsgGetVersion);
138
139 if (pBaseRequestMessage->m_cbMessage != sizeof(BaseRequestMessage))
140 {
141 return CORPROF_E_UNRECOGNIZED_PIPE_MSG_FORMAT;
142 }
143
144 return S_OK;
145}
146
147// ----------------------------------------------------------------------------
148// RequestMessageVerifier::VerifyAttachRequestMessage
149//
150// Description:
151// Once a BaseRequestMessage has been verified as self-consistent, and is of type
152// kMsgAttach, this helper is called to verify consistency of derived type
153// AttachRequestMessage
154//
155// Return Value:
156// S_OK or CORPROF_E_UNRECOGNIZED_PIPE_MSG_FORMAT
157//
158// Assumptions:
159// * Verify() calls this, but only after it has verified base type
160//
161
162HRESULT RequestMessageVerifier::VerifyAttachRequestMessage()
163{
164 LIMITED_METHOD_CONTRACT;
165
166 const BaseRequestMessage * pBaseRequestMessage =
167 (const BaseRequestMessage *) m_pbRequestMessage;
168
169 // Not allowed to call this unless you checked the m_requestMessageType first!
170 _ASSERTE(pBaseRequestMessage->m_requestMessageType == kMsgAttach);
171
172 // Enough memory to cast to AttachRequestMessage?
173 if (pBaseRequestMessage->m_cbMessage < sizeof(AttachRequestMessage))
174 {
175 return CORPROF_E_UNRECOGNIZED_PIPE_MSG_FORMAT;
176 }
177
178 AttachRequestMessage * pUnverifiedAttachRequestMessage =
179 (AttachRequestMessage *) pBaseRequestMessage;
180
181 // Is client data properly contained inside message? Use 64-bit arithmetic to
182 // detect overflow
183 UINT64 ui64TotalMsgLength = (UINT64) pUnverifiedAttachRequestMessage->m_cbMessage;
184 UINT64 ui64ClientDataStartOffset = (UINT64) pUnverifiedAttachRequestMessage->m_dwClientDataStartOffset;
185 UINT64 ui64ClientDataLength = (UINT64) pUnverifiedAttachRequestMessage->m_cbClientDataLength;
186
187 // Client data must occur AFTER struct
188 if (ui64ClientDataStartOffset < sizeof(AttachRequestMessage))
189 {
190 return CORPROF_E_UNRECOGNIZED_PIPE_MSG_FORMAT;
191 }
192
193 // Client data should be wholly contained inside the message
194 if (ui64ClientDataStartOffset + ui64ClientDataLength > ui64TotalMsgLength)
195 {
196 return CORPROF_E_UNRECOGNIZED_PIPE_MSG_FORMAT;
197 }
198
199 // m_wszProfilerPath must be a NULL-terminated string.
200 if (wmemchr(pUnverifiedAttachRequestMessage->m_wszProfilerPath,
201 W('\0'),
202 _countof(pUnverifiedAttachRequestMessage->m_wszProfilerPath)) == NULL)
203 {
204 return CORPROF_E_UNRECOGNIZED_PIPE_MSG_FORMAT;
205 }
206
207 return S_OK;
208}
209
210
211// ----------------------------------------------------------------------------
212// RequestMessageVerifier::GetBaseRequestMessage
213//
214// Description:
215// After you've called code:RequestMessageVerifier::Verify, this function will hand
216// you a pointer to the verified request message. (If you call this before verifying
217// the message, it'll assert.)
218//
219// Return Value:
220// Pointer to the verified message
221//
222// Assumptions:
223// * Call code:RequestMessageVerifier::Verify first!
224//
225
226const BaseRequestMessage * RequestMessageVerifier::GetBaseRequestMessage()
227{
228 LIMITED_METHOD_CONTRACT;
229
230 // Not allowed to ask for the message unless it's been successfully verified!
231 _ASSERTE(m_fVerified);
232
233 return (const BaseRequestMessage *) m_pbRequestMessage;
234}
235
236
237//---------------------------------------------------------------------------------------
238// #ConnectedPipeHolder
239//
240// Simple holder that ensures a connected pipe disconnects its client when the scope is
241// over. User of the class is responsible for creating the pipe and connecting the pipe,
242// before using this holder. The user of the class is responsible for closing the pipe
243// after this holder goes away.
244//
245
246// ----------------------------------------------------------------------------
247// AcquireConnectedPipe
248//
249// Description:
250// Used for ConnectedPipeHolder when acquiring a pipe HANDLE. Does nothing but
251// assert that the handle is valid.
252//
253// Arguments:
254// * hConnectedPipe - HANDLE being acquired
255//
256
257void AcquireConnectedPipe(HANDLE hConnectedPipe)
258{
259 LIMITED_METHOD_CONTRACT;
260 _ASSERTE(IsValidHandle(hConnectedPipe));
261}
262
263// ----------------------------------------------------------------------------
264// ReleaseConnectedPipe
265//
266// Description:
267// Used for ConnectedPipeHolder when releasing a pipe HANDLE. Disconnects the pipe
268// from its client, but leaves the pipe open and ready for the next client connection.
269//
270// Arguments:
271// * hConnectedPipe - HANDLE to pipe being disconnected
272//
273
274void ReleaseConnectedPipe(HANDLE hConnectedPipe)
275{
276 LIMITED_METHOD_CONTRACT;
277
278 _ASSERTE(IsValidHandle(hConnectedPipe));
279
280 LOG((
281 LF_CORPROF,
282 LL_ERROR,
283 "**PROF: Disconnecting pipe from current client.\n"));
284
285 if (!DisconnectNamedPipe(hConnectedPipe))
286 {
287 LOG((
288 LF_CORPROF,
289 LL_ERROR,
290 "**PROF: DisconnectNamedPipe failed with %d.\n",
291 GetLastError()));
292 }
293}
294
295// See code:#ConnectedPipeHolder
296typedef Wrapper<HANDLE, AcquireConnectedPipe, ReleaseConnectedPipe,
297 (UINT_PTR) INVALID_HANDLE_VALUE> ConnectedPipeHolder;
298
299
300// ----------------------------------------------------------------------------
301// Implementation of ProfilingAPIAttachServer: the primary class that handles the server
302// end of the pipe by receiving trigger requests, carrying them out, and then sending
303// responses back to the trigger (client end of pipe).
304//
305// This is the meat. Savor its juices.
306
307
308// ----------------------------------------------------------------------------
309// ProfilingAPIAttachServer::ExecutePipeRequests
310//
311// Description:
312// The AttachThread is responsible for performing attach and detach operations. This
313// function comprises the main loop for the "attach" operations. Creates the pipe
314// server, and repeatedly connects to clients (i.e., trigger processes calling
315// AttachProfiler() API), services them, and disconnects them. Once client connections
316// stop arriving for a while (default is 5 minutes), the loop ends, the pipe server is
317// destroyed, and this function returns. (Note: the exception is when running in
318// code:ProfilingAPIAttachDetach::kAlwaysOn mode. In that case, this function loops
319// forever over all clients, without timing out and returning if it takes a long time
320// for the next connection request to come in.)
321//
322// Return Value:
323// Any success code implies one client successfully attached a profiler, else, error
324// HRESULT indicating the last error encountered with a client.
325//
326
327HRESULT ProfilingAPIAttachServer::ExecutePipeRequests()
328{
329 CONTRACTL
330 {
331 THROWS;
332 GC_TRIGGERS;
333 MODE_PREEMPTIVE;
334 CAN_TAKE_LOCK;
335 }
336 CONTRACTL_END;
337
338 HRESULT hr;
339 AttachStatus attachStatusOverall = kNoAttachRequested;
340
341 // First create the pipe server. If this fails, all is lost
342 hr = CreateAttachPipe();
343 if (FAILED(hr))
344 {
345 LOG((
346 LF_CORPROF,
347 LL_ERROR,
348 "**PROF: Failed trying to create attach pipe server. hr=0x%x.\n",
349 hr));
350 ProfilingAPIUtility::LogProfError(IDS_E_PROF_ATTACHTHREAD_INIT, hr);
351 return hr;
352 }
353
354 // Thank you, CreateAttachPipe()!
355 _ASSERTE(IsValidHandle(m_hPipeServer));
356
357 // Now loop until there are no more clients to service. Remember if any of the
358 // clients got a profiler to attach, so we can return the appropriate HRESULT.
359 //
360 // Note that we intentionally keep on looping even after a profiler is attached, just
361 // in case there are any extra client requests coming in (e.g., user launched a
362 // couple triggers simultanously, or a single trigger retried AttachProfiler API a
363 // couple times). Once client connections stop coming in for a while,
364 // ServiceOneClient will fail with a timeout, and we'll break out of the loop.
365 //
366 // Also note that, in kAlwaysOn mode, we loop forever until the thread naturally dies
367 // during app shutdown
368
369 while (SUCCEEDED(hr) ||
370 (ProfilingAPIAttachDetach::GetAttachThreadingMode() ==
371 ProfilingAPIAttachDetach::kAlwaysOn))
372 {
373 AttachStatus attachStatusForThisClient = kNoAttachRequested;
374
375 hr = ServiceOneClient(&attachStatusForThisClient);
376
377 // #AttachStatusOrder
378 // Here's where the order of the AttachStatus enum is important. Any given client
379 // must have an attach status "better" than the current overall attach status,
380 // for us to want the overall attach status to change (to match the client's
381 // status). See code:ProfilingAPIAttachServer::AttachStatus
382 if ((int) attachStatusForThisClient > (int) attachStatusOverall)
383 {
384 attachStatusOverall = attachStatusForThisClient;
385 }
386 }
387
388 // We reach this point only when we're in kOnDemand mode, and a failure is causing
389 // us to destroy the pipe (usually the failure is simply a timeout waiting for the
390 // next client to come along).
391 _ASSERTE(FAILED(hr) &&
392 (ProfilingAPIAttachDetach::GetAttachThreadingMode() ==
393 ProfilingAPIAttachDetach::kOnDemand));
394
395 // This switch statement can forgive, but it will never forget. We went through all
396 // the trouble of making an AttachThread and a pipe, and now we're destroying them.
397 // If no one even asked to attach a profiler in the meantime, this switch notes that
398 // in the event log. Conversely, if at least some client successfully attached a
399 // profiler, return S_OK.
400
401 switch(attachStatusOverall)
402 {
403 default:
404 _ASSERTE(!"Unknown AttachStatus value!");
405 return E_UNEXPECTED;
406
407 case kNoAttachRequested:
408 // All this time, and no one even asked for an attach? Wack. Log and return the
409 // last error we got
410 _ASSERTE(FAILED(hr));
411 ProfilingAPIUtility::LogProfError(IDS_E_PROF_NO_ATTACH_REQ, hr);
412 return hr;
413
414 case kAttachFailed:
415 // Someone tried to attach and failed. Event was already logged at that time
416 _ASSERTE(FAILED(hr));
417 return hr;
418
419 case kAttachSucceeded:
420 // At least one of the clients managed to get a profiler successfully attached
421 // (info event was logged at that time), so all is well
422 return S_OK;
423 }
424}
425
426
427// ----------------------------------------------------------------------------
428// ProfilingAPIAttachServer::CreateAttachPipe
429//
430// Description:
431// Creates a new pipe server, that is not yet connected to a client
432//
433// Return Value:
434// HRESULT indicating success or failure
435//
436
437HRESULT ProfilingAPIAttachServer::CreateAttachPipe()
438{
439 CONTRACTL
440 {
441 THROWS;
442 GC_TRIGGERS;
443 MODE_PREEMPTIVE;
444 CAN_TAKE_LOCK;
445 }
446 CONTRACTL_END;
447
448 HRESULT hr;
449 SECURITY_ATTRIBUTES *psa = NULL;
450 SECURITY_ATTRIBUTES sa;
451
452 // Only assign security attributes for non-app container scenario
453 // We are assuming the default for app container scenario is good enough
454 if (!ProfilingAPIAttachDetach::IsAppContainerProcess(GetCurrentProcess()))
455 {
456 hr = ProfilingAPIAttachDetach::InitSecurityAttributes(&sa, sizeof(sa));
457 if (FAILED(hr))
458 {
459 return hr;
460 }
461
462 psa = &sa;
463 }
464
465 StackSString attachPipeName;
466 hr = ProfilingAPIAttachDetach::GetAttachPipeName(::GetCurrentProcess(), &attachPipeName);
467 if (FAILED(hr))
468 {
469 return hr;
470 }
471
472 m_hPipeServer = CreateNamedPipeW(
473 attachPipeName.GetUnicode(),
474 PIPE_ACCESS_DUPLEX | // server and client read/write to pipe
475 FILE_FLAG_OVERLAPPED, // server may read asynchronously & use a timeout
476 PIPE_TYPE_MESSAGE | // pipe data written as stream of messages
477 PIPE_READMODE_MESSAGE, // pipe data read as stream of messages
478 1, // Only one instance of the pipe is allowed
479 sizeof(GetVersionResponseMessage), // Hint of typical response size (GetVersion is the biggest)
480 sizeof(AttachRequestMessage) +
481 0x100, // Hint of typical request size (attach requests are the
482 // biggest, plus figure 0x100 for client data)
483 1000, // nDefaultTimeOut: unused. Clients will always
484 // specify their own timeout when waiting
485 // for the pipe to appear
486 psa // lpSecurityAttributes
487 );
488 if (m_hPipeServer == INVALID_HANDLE_VALUE)
489 {
490 return HRESULT_FROM_GetLastError();
491 }
492
493 _ASSERTE(IsValidHandle(m_hPipeServer));
494
495 LOG((
496 LF_CORPROF,
497 LL_INFO10,
498 "**PROF: Successfully created attach pipe server. Name: '%S'.\n",
499 attachPipeName.GetUnicode()));
500
501 return S_OK;
502}
503
504// ----------------------------------------------------------------------------
505// ProfilingAPIAttachServer::ServiceOneClient
506//
507// Description:
508// Awaits a connection from a client, receives the client's requests, executes and
509// responds to those requests, and then disconnects the client on error or once a
510// profiler has been attached as a result. If any blocking operation takes too long,
511// this will disconnect the client as well.
512//
513// Arguments:
514// * pAttachStatusForClient - [out] enum indicating whether an attach request was
515// received and processed successfully. NOTE: This out param is always set
516// properly, even if this function returns an error.
517//
518// Return Value:
519// * error HRESULT: something bad happened with the pipe itself (e.g., couldn't
520// connect to a new client due to timeout or something worse). When in kOnDemand
521// mode, an error return from this function indicates the entire AttachThread
522// should go away.
523// * S_OK: Pipe is fine and connected to at least one client. That connection may or
524// may not have resulted in successful communication or a profiler attach. But in
525// any case, the pipe is still intact, and the caller should connect with the next
526// client.
527//
528// Notes:
529// * A failure event will be logged for any kind of user-actionable failure that
530// occurs in this function or callees.
531// * A failure event is NOT logged for a NON-actionable failure such as failure in
532// communicating a response message back to the trigger (client). See comment at
533// top of code:ProfilingAPIAttachServer::WriteResponseToPipe
534
535HRESULT ProfilingAPIAttachServer::ServiceOneClient(
536 AttachStatus * pAttachStatusForClient)
537{
538 CONTRACTL
539 {
540 THROWS;
541 GC_TRIGGERS;
542 MODE_PREEMPTIVE;
543 CAN_TAKE_LOCK;
544 }
545 CONTRACTL_END;
546
547 _ASSERTE(IsValidHandle(m_hPipeServer));
548 _ASSERTE(pAttachStatusForClient != NULL);
549
550 HRESULT hr = E_UNEXPECTED;
551 *pAttachStatusForClient = kNoAttachRequested;
552
553 // What is the max timeout for each blocking wait for the trigger? Examples of
554 // blocking waits: wait for a pipe client to show up, or for the client to send a
555 // request, or for the pipe to transfer our response to the client.
556 //
557 // If any blocking operation takes longer than this, the current function will
558 // timeout.
559 // * While in kOnDemand mode, a timeout waiting for a client to connect will
560 // cause the AttachThread to give up, go away, and the app reverts to
561 // non-attach performance characteristics. The Global Attach Event will need
562 // to be signaled again by a trigger process (via AttachProfiler API) before
563 // a new AttachThread gets created and tries again.
564 // * Once a client is connected, timeouts from this function simply cause that
565 // client to be disconnected, and this function will be called again to wait
566 // (with timeout!) for the next client to connect.
567 m_dwMillisecondsMaxPerWait = CLRConfig::GetConfigValue(CLRConfig::EXTERNAL_ProfAPIMaxWaitForTriggerMs);
568
569 hr = ConnectToClient();
570 if (FAILED(hr))
571 {
572 if (hr != HRESULT_FROM_WIN32(ERROR_TIMEOUT))
573 {
574 // Any error other than timeout is unexpected and should be logged. Timeouts,
575 // however, are expected, as eventually clients will stop trying to connect
576 // to the pipe, so no need to log that.
577 ProfilingAPIUtility::LogProfError(IDS_E_PROF_CONNECT_TO_TRIGGER, hr);
578 }
579 return hr;
580 }
581
582 LOG((
583 LF_CORPROF,
584 LL_INFO10,
585 "**PROF: Pipe server is now connected to a new client.\n"));
586
587 // This forces a call to DisconnectNamedPipe before we return. That kicks the current
588 // client off of the pipe, and leaves the pipe available for the next client
589 // connection.
590 ConnectedPipeHolder connectedPipeHolder(m_hPipeServer);
591
592 // Keep executing requests from this client until it asks for (and we attempt) an
593 // attach. Whether the attach succeeds or fails, that's the end of this client, and
594 // we'll fall out of the loop and return.
595 while (*pAttachStatusForClient == kNoAttachRequested)
596 {
597 hr = ServiceOneRequest(pAttachStatusForClient);
598 if (FAILED(hr))
599 {
600 // Low-level error on the pipe itself indicating that we should disconnect
601 // from this client, and try connecting to a new one. Typical errors you
602 // might see here:
603 // * HRESULT_FROM_WIN32(ERROR_BROKEN_PIPE)
604 // * Someone killed the trigger process (or it timed out) before an
605 // attach could be requested.
606 // * HRESULT_FROM_WIN32(ERROR_TIMEOUT)
607 // * HRESULT_FROM_WIN32(ERROR_SEM_TIMEOUT)
608 // * Client's taking too long to send a request
609 //
610 // Since a failure here indicates a problem with this particular client, and
611 // not a global problem with the pipe, just convert to S_OK and return so we
612 // disconnect this client, and the caller knows to try connecting to another
613 // client. Note that ServiceOneRequest() has already reported any actionable
614 // problem into the event log.
615 return S_OK;
616 }
617 }
618
619 // A trigger finally managed to request an attach (success of the attach may be
620 // found in pAttachStatusForClient). So we can return to disconnect this client and
621 // poll for the next client.
622 return S_OK;
623}
624
625
626// ----------------------------------------------------------------------------
627// ProfilingAPIAttachServer::ConnectToClient
628//
629// Description:
630// Waits until a client connects to the pipe server, or until timeout.
631//
632// Return Value:
633// HRESULT indicating success or failure.
634//
635
636HRESULT ProfilingAPIAttachServer::ConnectToClient()
637{
638 CONTRACTL
639 {
640 THROWS;
641 GC_TRIGGERS;
642 MODE_PREEMPTIVE;
643 CAN_TAKE_LOCK;
644 }
645 CONTRACTL_END;
646
647 _ASSERTE(IsValidHandle(m_hPipeServer));
648
649 HRESULT hr;
650 BOOL fRet;
651 DWORD dwErr;
652 DWORD cbReceived;
653 ProfilingAPIAttachDetach::OverlappedResultHolder overlapped;
654 hr = overlapped.Initialize();
655 if (FAILED(hr))
656 {
657 return hr;
658 }
659
660 // Start an overlapped connection for this pipe instance.
661 fRet = ConnectNamedPipe(m_hPipeServer, overlapped);
662 if (fRet)
663 {
664 // No need to wait, pipe connected already
665 return S_OK;
666 }
667
668 dwErr = GetLastError();
669 if (dwErr == ERROR_PIPE_CONNECTED)
670 {
671 // In true Windows style, a "failure" with ERROR_PIPE_CONNECTED is
672 // actually a success case: a client tried to connect before we (the
673 // server) called ConnectNamedPipe, so that we're now connected
674 // just fine
675 return S_OK;
676 }
677
678 if (dwErr != ERROR_IO_PENDING)
679 {
680 // An error we cannot recover from
681 LOG((
682 LF_CORPROF,
683 LL_ERROR,
684 "**PROF: ConnectNamedPipe failed. hr=0x%x.\n",
685 HRESULT_FROM_WIN32(dwErr)));
686 return HRESULT_FROM_WIN32(dwErr);
687 }
688
689 // Typical case: ERROR_IO_PENDING. ConnectNamedPipe is waiting (in overlapped mode)
690 // for a client to connect. Block until this happens (or we timeout)
691
692 hr = overlapped.Wait(
693
694 // How long we wait for the next client to show up depends on our threading mode
695 (ProfilingAPIAttachDetach::GetAttachThreadingMode() ==
696 ProfilingAPIAttachDetach::kAlwaysOn) ?
697
698 // In always-on mode, we're willing to wait forever until the next client
699 // shows up.
700 INFINITE :
701
702 // In on-demand mode, we want the AttachThread to exit if there aren't
703 // any new clients in a reasonable amount of time.
704 m_dwMillisecondsMaxPerWait,
705
706 m_hPipeServer,
707 &cbReceived);
708 if (FAILED(hr))
709 {
710 LOG((
711 LF_CORPROF,
712 LL_ERROR,
713 "**PROF: Waiting for overlapped result for ConnectNamedPipe failed. hr=0x%x.\n",
714 hr));
715 return hr;
716 }
717
718 return S_OK;
719}
720
721
722// ----------------------------------------------------------------------------
723// ProfilingAPIAttachServer::ServiceOneRequest
724//
725// Description:
726// Receives, executes, and responds to a single request from a single client.
727//
728// Arguments:
729// * pAttachStatus - [out] enum indicating whether an attach request was received and
730// processed successfully. NOTE: This out param is always set properly, even if
731// this function returns an error.
732//
733// Return Value:
734// * S_OK: Request was received. It may or may not have been processed successfully.
735// Any processing failure would be due to a high level problem, like an unknown
736// request format, or a CLR problem in handling the request ("can't attach
737// profiler cuz profiler already loaded"). In any case, the caller may leave the
738// pipe connection to this client open, as the connection is valid.
739// * error: Low-level error (e.g., OS pipe failure or timeout) trying to receive the
740// request or send a response. Such an error is generally unexpected and will
741// cause the caller to close the connection to the client (though the pipe will
742// remain up for the next client to try connecting).
743//
744// Notes:
745// * A failure event will be logged for any kind of user-actionable failure that
746// occurs in this function or callees.
747// * A failure event is NOT logged for a NON-actionable failure such as failure in
748// communicating a response message back to the trigger (client). See comment at
749// top of code:ProfilingAPIAttachServer::WriteResponseToPipe
750//
751
752HRESULT ProfilingAPIAttachServer::ServiceOneRequest(AttachStatus * pAttachStatus)
753{
754 CONTRACTL
755 {
756 THROWS;
757 GC_TRIGGERS;
758 MODE_PREEMPTIVE;
759 CAN_TAKE_LOCK;
760 }
761 CONTRACTL_END;
762
763 _ASSERTE(IsValidHandle(m_hPipeServer));
764 _ASSERTE(pAttachStatus != NULL);
765
766 HRESULT hr;
767 DWORD cbRequestMessageRead;
768 *pAttachStatus = kNoAttachRequested;
769
770 // Reading from the pipe is a 3-step process.
771 //
772 // * 1. Read into a 0-sized buffer. This causes us to block (with timeout) until the
773 // message is in the pipe and ready to be analyzed. Since the buffer is 0-sized,
774 // the message is not actually read out of the pipe yet.
775 // * 2. Now that we know the message is available, peek into the pipe to extract the
776 // size of the message
777 // * 3. Now that we know the size, allocate a sufficient buffer, and repeat step 1,
778 // but with the appropriately sized buffer. This time the data is emptied out of
779 // the pipe.
780
781 // Step 1: Read request once w/ 0-sized buffer so we know when the message is ready;
782 // at that point we can ask how long the message is
783 hr = ReadRequestFromPipe(
784 NULL, // Request buffer
785 0, // Size of request buffer
786 &cbRequestMessageRead);
787 if (FAILED(hr) && (hr != HRESULT_FROM_WIN32(ERROR_MORE_DATA)))
788 {
789 ProfilingAPIUtility::LogProfError(IDS_E_PROF_PIPE_RCV, hr);
790 return hr;
791 }
792
793 // Step 2: Message is ready. How big is it?
794 DWORD cbRequestMessage;
795 if (!PeekNamedPipe(
796 m_hPipeServer,
797 NULL, // Request buffer (0-size for now)
798 0, // Size of request buffer
799 NULL, // lpBytesRead (NULL cuz message shan't be read)
800 NULL, // lpTotalBytesAvail (NULL cuz don't care)
801 &cbRequestMessage))
802 {
803 ProfilingAPIUtility::LogProfError(IDS_E_PROF_PIPE_RCV, hr);
804 return hr;
805 }
806
807 // 0-sized requests are invalid. Something wrong with the pipe?
808 if (cbRequestMessage == 0)
809 {
810 hr = E_UNEXPECTED;
811 ProfilingAPIUtility::LogProfError(IDS_E_PROF_PIPE_RCV, hr);
812 return hr;
813 }
814
815 // Step 3: message is ready and we know the size. Make the buffer, and read it in.
816
817 NewHolder<BYTE> pbRequestMessage(new (nothrow) BYTE[cbRequestMessage]);
818 if (pbRequestMessage == NULL)
819 {
820 hr = E_OUTOFMEMORY;
821 ProfilingAPIUtility::LogProfError(IDS_E_PROF_PIPE_RCV, hr);
822 return hr;
823 }
824
825 hr = ReadRequestFromPipe(
826 pbRequestMessage,
827 cbRequestMessage,
828 &cbRequestMessageRead);
829 if (FAILED(hr))
830 {
831 ProfilingAPIUtility::LogProfError(IDS_E_PROF_PIPE_RCV, hr);
832 return hr;
833 }
834
835 if (cbRequestMessage != cbRequestMessageRead)
836 {
837 // Somehow we read a different number of bytes than we were told was in the pipe
838 // buffer. Pipe having problems?
839 hr = E_UNEXPECTED;
840 ProfilingAPIUtility::LogProfError(IDS_E_PROF_PIPE_RCV, hr);
841 return hr;
842 }
843
844 // Request successfully read! Now figure out what the request is, carry it out, and
845 // send a response. This function will report to the event log any user-actionable
846 // error.
847 return InterpretAndExecuteRequestMessage(pbRequestMessage, cbRequestMessage, pAttachStatus);
848}
849
850// ----------------------------------------------------------------------------
851// ProfilingAPIAttachServer::ReadRequestFromPipe
852//
853// Description:
854// Performs a ReadFile with timeout on the pipe server to read the client's request
855// message.
856//
857// Arguments:
858// * pvRequestBuffer - [out] Buffer into which the request will be placed
859// * cbRequestBuffer - [in] Size, in bytes, of the request buffer
860// * pcbActualRequest - [out] Actual number of bytes placed into the request buffer.
861//
862// Return Value:
863// HRESULT indicating success or failure
864//
865// Assumptions:
866// * m_hPipeServer must be connected to a client.
867//
868// Notes:
869// * The [out] parameters may be written to even if this function fails. But their
870// contents should be ignored by the caller in this case.
871//
872
873HRESULT ProfilingAPIAttachServer::ReadRequestFromPipe(
874 LPVOID pvRequestBuffer,
875 DWORD cbRequestBuffer,
876 DWORD * pcbActualRequest)
877{
878 CONTRACTL
879 {
880 THROWS;
881 GC_TRIGGERS;
882 MODE_PREEMPTIVE;
883 CAN_TAKE_LOCK;
884 }
885 CONTRACTL_END;
886
887 _ASSERTE(IsValidHandle(m_hPipeServer));
888
889 // NULL buffer implies zero size!
890 _ASSERTE((pvRequestBuffer != NULL) || (cbRequestBuffer == 0));
891
892 _ASSERTE(pcbActualRequest != NULL);
893
894 HRESULT hr;
895 DWORD dwErr;
896 ProfilingAPIAttachDetach::OverlappedResultHolder overlapped;
897 hr = overlapped.Initialize();
898 if (FAILED(hr))
899 {
900 return hr;
901 }
902
903 if (ReadFile(
904 m_hPipeServer,
905 pvRequestBuffer,
906 cbRequestBuffer,
907 pcbActualRequest,
908 overlapped))
909 {
910 // Quick read, no waiting
911 return S_OK;
912 }
913
914 dwErr = GetLastError();
915 if (dwErr != ERROR_IO_PENDING)
916 {
917 LOG((
918 LF_CORPROF,
919 LL_ERROR,
920 "**PROF: ReadFile on the pipe failed. hr=0x%x.\n",
921 HRESULT_FROM_WIN32(dwErr)));
922 return HRESULT_FROM_WIN32(dwErr);
923 }
924
925 // Typical case=ERROR_IO_PENDING: gotta wait until request comes in (or we timeout)
926
927 hr = overlapped.Wait(
928 m_dwMillisecondsMaxPerWait,
929 m_hPipeServer,
930 pcbActualRequest);
931 if (FAILED(hr))
932 {
933 LOG((
934 LF_CORPROF,
935 LL_ERROR,
936 "**PROF: Waiting for overlapped result for ReadFile on the pipe failed. hr=0x%x.\n",
937 hr));
938 return hr;
939 }
940
941 return S_OK;
942}
943
944
945// ----------------------------------------------------------------------------
946// ProfilingAPIAttachServer::InterpretAndExecuteRequestMessage
947//
948// Description:
949// Takes an unverified stream of bytes read from the pipe, and then verifies the bytes
950// as a self-consistent message and executes the request (either get version or
951// attach). Once the request has been executed, a response is sent back across the
952// pipe.
953//
954// Arguments:
955// * pbRequestMessage - [in] Bytes read from pipe
956// * cbRequestMessage - [in] Count of bytes read from pipe
957// * pAttachStatus - [out] (see comment header for
958// code:ProfilingAPIAttachServer::ServiceOneRequest)
959//
960// Return Value:
961// HRESULT indicating success or failure with low-level reading / writing operations
962// on the pipe that indicate whether the caller should abandon this client connection.
963// Higher-level failures (e.g., bogus request messages, or failure performing the
964// actual attach) do not cause an error to be returned from this function. Caller may
965// use pAttachStatus to determine whether this request resulted in a successful
966// profiler attach.
967//
968// Notes:
969// * This (or callee) will log an event on actionable failures. (Failure to send a
970// response back to the trigger is not considered actionable. See comment at top
971// of code:ProfilingAPIAttachServer::WriteResponseToPipe.)
972//
973
974HRESULT ProfilingAPIAttachServer::InterpretAndExecuteRequestMessage(
975 LPCBYTE pbRequestMessage,
976 DWORD cbRequestMessage,
977 AttachStatus * pAttachStatus)
978{
979 CONTRACTL
980 {
981 THROWS;
982 GC_TRIGGERS;
983
984 // This causes events to be logged, which loads resource strings,
985 // which takes locks.
986 CAN_TAKE_LOCK;
987
988 MODE_PREEMPTIVE;
989 }
990 CONTRACTL_END;
991
992 _ASSERTE(pbRequestMessage != NULL);
993 _ASSERTE(pAttachStatus != NULL);
994
995 HRESULT hr;
996
997 *pAttachStatus = kNoAttachRequested;
998
999 // Message bytes have not been verified, so none of the contents (such as sizes or
1000 // offsets) may be trusted until they're all verified.
1001 RequestMessageVerifier messageVerifier(pbRequestMessage, cbRequestMessage);
1002 hr = messageVerifier.Verify();
1003 if (FAILED(hr))
1004 {
1005 // Bogus request message. Log to event log
1006 ProfilingAPIUtility::LogProfError(IDS_E_PROF_INVALID_MSG);
1007
1008 // And send complaint back to trigger
1009 BaseResponseMessage responseMsg(hr);
1010 return WriteResponseToPipe(&responseMsg, sizeof(responseMsg));
1011 }
1012
1013 // Yay! Message is valid
1014 const BaseRequestMessage * pBaseRequestMessage = messageVerifier.GetBaseRequestMessage();
1015
1016 // Execute request based on its type
1017 switch(pBaseRequestMessage->m_requestMessageType)
1018 {
1019 default:
1020 // RequestMessageVerifier should have verified no unexpected request message
1021 // types slipped through.
1022 _ASSERTE(!"Unexpected m_requestMessageType");
1023 return E_UNEXPECTED;
1024
1025 case kMsgGetVersion:
1026 return ExecuteGetVersionRequestMessage();
1027
1028 case kMsgAttach:
1029 return ExecuteAttachRequestMessage(
1030 (const AttachRequestMessage *) pBaseRequestMessage,
1031 pAttachStatus);
1032 }
1033}
1034
1035
1036// ----------------------------------------------------------------------------
1037// ProfilingAPIAttachServer::ExecuteAttachRequestMessage
1038//
1039// Description:
1040// Once an attach request message has been verified as self-consistent (see
1041// code:RequestMessageVerifier), call this function to actually attach the profiler
1042// using data from the message
1043//
1044// Arguments:
1045// * pAttachRequestMessage - [in] An already-verified attach request message that was
1046// received from trigger.
1047// * pAttachStatus - [out] (see comment header for
1048// code:ProfilingAPIAttachServer::ServiceOneRequest)
1049//
1050// Return Value:
1051// HRESULT indicating success or failure in sending response over the pipe back to the
1052// trigger. Note that a failure to perform the attach does not necessarily cause a
1053// failure HRESULT to be returned by this function (only low-level pipe problems will
1054// cause this function to fail). A failure performing the attach is noted in
1055// pAttachStatus.
1056//
1057// Notes:
1058// * This (or a callee) will log an event on failure or success of performing the
1059// attach. However, once the attach is complete (failed or succeeded), no event
1060// will be logged if there is a communication error sending the response back to
1061// the trigger. (See comment at top of
1062// code:ProfilingAPIAttachServer::WriteResponseToPipe)
1063//
1064
1065HRESULT ProfilingAPIAttachServer::ExecuteAttachRequestMessage(
1066 const AttachRequestMessage * pAttachRequestMessage,
1067 AttachStatus * pAttachStatus)
1068{
1069 CONTRACTL
1070 {
1071 THROWS;
1072 GC_TRIGGERS;
1073
1074 // This causes events to be logged, which loads resource strings,
1075 // which takes locks.
1076 CAN_TAKE_LOCK;
1077
1078 MODE_PREEMPTIVE;
1079 }
1080 CONTRACTL_END;
1081
1082 _ASSERTE(pAttachRequestMessage != NULL);
1083 _ASSERTE(pAttachStatus != NULL);
1084
1085 // Start off pessimistic
1086 *pAttachStatus = kAttachFailed;
1087
1088 if (g_profControlBlock.curProfStatus.Get() != kProfStatusNone)
1089 {
1090 // Sorry, profiler's already here.
1091 //
1092 // Note: It might appear that there's a race here (i.e.,
1093 // g_profControlBlock.curProfStatus.Get() == kProfStatusNone so we try to load the
1094 // profiler, but another profiler is already getting loaded somehow, and
1095 // g_profControlBlock.curProfStatus.Get() just hasn't been updated yet. So we end
1096 // up loading two profilers at once.) But there is actually no race here for a
1097 // couple reasons:
1098 // * 1. Startup load of profiler occurs before the pipe is even constructed. So
1099 // we won't get an attach request while a startup load is in progress
1100 // * 2. Pipe requests are serialized. OS handles this for us because:
1101 // * a. Only one instance of the attach pipe is allowed at a time, because
1102 // our call to CreateNamedPipeW specifies only 1 instance is allowed, and
1103 // * b. Within that single pipe instance, messages are processed serially,
1104 // from the single AttachThread that successfully created the pipe in the
1105 // first place.
1106 ProfilingAPIUtility::LogProfError(IDS_E_PROF_PROFILER_ALREADY_ACTIVE);
1107
1108 _ASSERTE(*pAttachStatus == kAttachFailed);
1109
1110 // Inform trigger that attach cannot happen now
1111 AttachResponseMessage responseMsg(CORPROF_E_PROFILER_ALREADY_ACTIVE);
1112 return WriteResponseToPipe(&responseMsg, sizeof(responseMsg));
1113 }
1114
1115 // If the client sends us a V2 message, retrieve the time out value
1116 // In theory both client and server should be both on v4.5+ but I'm assigning a default value
1117 // just in case
1118 DWORD dwConcurrentGCWaitTimeoutInMs = INFINITE;
1119 if (AttachRequestMessageV2::CanCastTo(pAttachRequestMessage))
1120 dwConcurrentGCWaitTimeoutInMs =
1121 static_cast<const AttachRequestMessageV2 *>(pAttachRequestMessage)->m_dwConcurrentGCWaitTimeoutInMs;
1122
1123 // LoadProfilerForAttach & callees ensure an event is logged on error.
1124 HRESULT hrAttach = ProfilingAPIUtility::LoadProfilerForAttach(
1125
1126 // Profiler's CLSID
1127 &(pAttachRequestMessage->m_clsidProfiler),
1128
1129 // wszProfilerDLL
1130 pAttachRequestMessage->m_wszProfilerPath,
1131
1132 // Client data ptr
1133 (pAttachRequestMessage->m_cbClientDataLength == 0) ?
1134 // No client data: use NULL
1135 NULL :
1136 // Else, follow offset to find client data
1137 (LPVOID) (((LPBYTE) pAttachRequestMessage) +
1138 pAttachRequestMessage->m_dwClientDataStartOffset),
1139
1140 // Client data size
1141 pAttachRequestMessage->m_cbClientDataLength,
1142
1143 // Time out for wait operation on current gc that is in progress
1144 dwConcurrentGCWaitTimeoutInMs);
1145
1146 // Inform caller if attach succeeded
1147 if (SUCCEEDED(hrAttach))
1148 {
1149 *pAttachStatus = kAttachSucceeded;
1150 }
1151 else
1152 {
1153 _ASSERTE(*pAttachStatus == kAttachFailed);
1154 }
1155
1156 // Inform trigger about how the attach went
1157 AttachResponseMessage responseMsg(hrAttach);
1158 return WriteResponseToPipe(&responseMsg, sizeof(responseMsg));
1159}
1160
1161
1162// ----------------------------------------------------------------------------
1163// ProfilingAPIAttachServer::ExecuteGetVersionRequestMessage
1164//
1165// Description:
1166// Composes a response message to the "GetVersion" request message. Response contains
1167// the version of the profilee (server), and the minimum allowable version of a
1168// trigger (client) we're willing to talk to.
1169//
1170// Return Value:
1171// HRESULT Indicating success or failure.
1172//
1173// Notes:
1174// * Composing the response cannot fail, and we are not logging communcation failures
1175// in sending response messages (see comment at top of
1176// code:ProfilingAPIAttachServer::WriteResponseToPipe), so no event will be logged
1177// by this function or callees.
1178//
1179
1180HRESULT ProfilingAPIAttachServer::ExecuteGetVersionRequestMessage()
1181{
1182 CONTRACTL
1183 {
1184 THROWS;
1185 GC_TRIGGERS;
1186 MODE_PREEMPTIVE;
1187 CAN_TAKE_LOCK;
1188 }
1189 CONTRACTL_END;
1190
1191 GetVersionResponseMessage responseMsg(
1192 // S_OK means we successfully carried out the "GetVersion" request
1193 S_OK,
1194
1195 // This is the version of the target profilee app
1196 ProfilingAPIAttachDetach::kCurrentProcessVersion,
1197
1198 // This is the oldest trigger that we allow communicating with
1199 ProfilingAPIAttachDetach::kMinimumAllowableTriggerVersion);
1200
1201 return WriteResponseToPipe(&responseMsg, sizeof(responseMsg));
1202}
1203
1204// ----------------------------------------------------------------------------
1205// ProfilingAPIAttachServer::WriteResponseToPipeNoBufferSizeCheck
1206//
1207// Description:
1208// Performs a WriteFile with timeout on the pipe server to write the specified
1209// response back to the client. This is an internal helper used by
1210// code:ProfilingAPIAttachServer::WriteResponseToPipe
1211//
1212// Arguments:
1213// * pvResponse - [in] Buffer containing the response to be sent to the client
1214// * cbResponse - [in] Size, in bytes, of the response to send.
1215// * pcbWritten - [out] Actual number of bytes sent to client
1216//
1217// Return Value:
1218// HRESULT indicating success or failure
1219//
1220// Assumptions:
1221// * m_hPipeServer must be connected to a client.
1222//
1223// Notes:
1224// * The [out] parameter may be written to even if this function fails. But its
1225// contents should be ignored by the caller in this case.
1226//
1227
1228HRESULT ProfilingAPIAttachServer::WriteResponseToPipeNoBufferSizeCheck(
1229 LPVOID pvResponse,
1230 DWORD cbResponse,
1231 DWORD * pcbWritten)
1232{
1233 CONTRACTL
1234 {
1235 THROWS;
1236 GC_TRIGGERS;
1237 MODE_PREEMPTIVE;
1238 CAN_TAKE_LOCK;
1239 }
1240 CONTRACTL_END;
1241
1242 _ASSERTE(IsValidHandle(m_hPipeServer));
1243 _ASSERTE(pvResponse != NULL);
1244 _ASSERTE(pcbWritten != NULL);
1245
1246 HRESULT hr;
1247 DWORD dwErr;
1248 ProfilingAPIAttachDetach::OverlappedResultHolder overlapped;
1249 hr = overlapped.Initialize();
1250 if (FAILED(hr))
1251 {
1252 return hr;
1253 }
1254
1255 if (WriteFile(
1256 m_hPipeServer,
1257 pvResponse,
1258 cbResponse,
1259 pcbWritten,
1260 overlapped))
1261 {
1262 // Quick write, no waiting
1263 return S_OK;
1264 }
1265
1266 dwErr = GetLastError();
1267 if (dwErr != ERROR_IO_PENDING)
1268 {
1269 LOG((
1270 LF_CORPROF,
1271 LL_ERROR,
1272 "**PROF: WriteFile on the pipe failed. hr=0x%x.\n",
1273 HRESULT_FROM_WIN32(dwErr)));
1274 return HRESULT_FROM_WIN32(dwErr);
1275 }
1276
1277 // Typical case=ERROR_IO_PENDING: gotta wait until response is sent (or we timeout)
1278
1279 hr = overlapped.Wait(
1280 m_dwMillisecondsMaxPerWait,
1281 m_hPipeServer,
1282 pcbWritten);
1283 if (FAILED(hr))
1284 {
1285 LOG((
1286 LF_CORPROF,
1287 LL_ERROR,
1288 "**PROF: Waiting for overlapped result for WriteFile on the pipe failed. hr=0x%x.\n",
1289 hr));
1290 return hr;
1291 }
1292
1293 return S_OK;
1294}
1295
1296#endif //FEATURE_PROFAPI_ATTACH_DETACH
1297