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 | |
57 | HRESULT 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 | |
126 | HRESULT 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 | |
162 | HRESULT 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 | |
226 | const 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 | |
257 | void 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 | |
274 | void 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 |
296 | typedef 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 | |
327 | HRESULT 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 | |
437 | HRESULT 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 | |
535 | HRESULT 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 | |
636 | HRESULT 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 | |
752 | HRESULT 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 | |
873 | HRESULT 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 | |
974 | HRESULT 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 | |
1065 | HRESULT 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 | |
1180 | HRESULT 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 | |
1228 | HRESULT 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 | |