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.
6 #include "dbgtransportsession.h"
8 #if (!defined(RIGHT_SIDE_COMPILE) && defined(FEATURE_DBGIPC_TRANSPORT_VM)) || (defined(RIGHT_SIDE_COMPILE) && defined(FEATURE_DBGIPC_TRANSPORT_DI))
10 // This is the entry type for the IPC event queue owned by the transport.
11 // Each entry contains the multiplexing type of the IPC event plus the
13 struct DbgEventBufferEntry
17 BYTE m_event[CorDBIPC_BUFFER_SIZE]; // buffer for the IPC event
21 // Provides a robust and secure transport session between a debugger and a debuggee that are potentially on
22 // different machines.
24 // See DbgTransportSession.h for further detailed comments.
27 #ifndef RIGHT_SIDE_COMPILE
28 // The one and only transport instance for the left side. Allocated and initialized during EE startup (from
29 // Debugger::Startup() in debugger.cpp).
30 DbgTransportSession *g_pDbgTransport = NULL;
32 #include "ddmarshalutil.h"
33 #endif // !RIGHT_SIDE_COMPILE
35 // No real work done in the constructor. Use Init() instead.
36 DbgTransportSession::DbgTransportSession()
42 DbgTransportSession::~DbgTransportSession()
44 DbgTransportLog(LC_Proxy, "DbgTransportSession::~DbgTransportSession() called");
46 // No other threads are now using session resources. We're free to deallocate them as we wish (if they
47 // were allocated in the first place).
48 if (m_hTransportThread)
49 CloseHandle(m_hTransportThread);
50 if (m_rghEventReadyEvent[IPCET_OldStyle])
51 CloseHandle(m_rghEventReadyEvent[IPCET_OldStyle]);
52 if (m_rghEventReadyEvent[IPCET_DebugEvent])
53 CloseHandle(m_rghEventReadyEvent[IPCET_DebugEvent]);
55 delete [] m_pEventBuffers;
57 #ifdef RIGHT_SIDE_COMPILE
58 if (m_hSessionOpenEvent)
59 CloseHandle(m_hSessionOpenEvent);
62 CloseHandle(m_hProcessExited);
63 #endif // RIGHT_SIDE_COMPILE
66 m_sStateLock.Destroy();
69 // Allocates initial resources (including starting the transport thread). The session will start in the
70 // SS_Opening state. That is, the RS will immediately start trying to Connect() a connection while the LS will
71 // perform an accept()/Accept() to wait for a connection request. The RS needs an IP address and port number
72 // to initiate connections. These should be given in host byte order. The LS, on the other hand, requires the
73 // addresses of a couple of runtime data structures to service certain debugger requests that may be delivered
74 // once the session is established.
75 #ifdef RIGHT_SIDE_COMPILE
76 HRESULT DbgTransportSession::Init(DWORD pid, HANDLE hProcessExited)
77 #else // RIGHT_SIDE_COMPILE
78 HRESULT DbgTransportSession::Init(DebuggerIPCControlBlock *pDCB, AppDomainEnumerationIPCBlock *pADB)
79 #endif // RIGHT_SIDE_COMPILE
81 _ASSERTE(m_eState == SS_Closed);
83 // Start with a blank slate so that Shutdown() on a partially initialized instance will only do the
85 memset(this, 0, sizeof(*this));
87 // Because of the above memset the embeded classes/structs need to be reinitialized especially
88 // the two way pipe; it expects the in/out handles to be -1 instead of 0.
90 m_pipe = TwoWayPipe();
91 m_sStateLock = DbgTransportLock();
93 // Initialize all per-session state variables.
96 #ifdef RIGHT_SIDE_COMPILE
97 // The RS randomly allocates a session ID which is sent to the LS in the SessionRequest message. In the
98 // case of network errors during session formation this allows the LS to tell SessionRequest re-sends from
99 // a new request from a different RS.
100 HRESULT hr = CoCreateGuid(&m_sSessionID);
103 #endif // RIGHT_SIDE_COMPILE
106 #ifdef RIGHT_SIDE_COMPILE
109 if (!DuplicateHandle(GetCurrentProcess(),
113 0, // ignored since we are going to pass DUPLICATE_SAME_ACCESS
115 DUPLICATE_SAME_ACCESS))
117 return HRESULT_FROM_GetLastError();
120 m_fDebuggerAttached = false;
121 #else // RIGHT_SIDE_COMPILE
124 #endif // RIGHT_SIDE_COMPILE
127 m_fInitStateLock = true;
129 #ifdef RIGHT_SIDE_COMPILE
130 m_hSessionOpenEvent = WszCreateEvent(NULL, TRUE, FALSE, NULL); // Manual reset, not signalled
131 if (m_hSessionOpenEvent == NULL)
132 return E_OUTOFMEMORY;
133 #endif // RIGHT_SIDE_COMPILE
135 // Allocate some buffers to receive incoming events. The initial number is chosen arbitrarily, tune as
136 // necessary. This array will need to grow if it fills with unread events (it takes our client a little
137 // time to process each incoming receive). In general, however, one side will not send an unbounded stream
138 // of events to the other without waiting for some kind of response. More usual are small bursts of events
139 // to represent variable sized data (such as a stack trace).
140 m_cEventBuffers = 10;
141 m_pEventBuffers = (DbgEventBufferEntry *)new (nothrow) BYTE[m_cEventBuffers * sizeof(DbgEventBufferEntry)];
142 if (m_pEventBuffers == NULL)
143 return E_OUTOFMEMORY;
145 m_rghEventReadyEvent[IPCET_OldStyle] = WszCreateEvent(NULL, FALSE, FALSE, NULL); // Auto reset, not signalled
146 if (m_rghEventReadyEvent[IPCET_OldStyle] == NULL)
147 return E_OUTOFMEMORY;
149 m_rghEventReadyEvent[IPCET_DebugEvent] = WszCreateEvent(NULL, FALSE, FALSE, NULL); // Auto reset, not signalled
150 if (m_rghEventReadyEvent[IPCET_DebugEvent] == NULL)
151 return E_OUTOFMEMORY;
153 // Start the transport thread which handles forming and re-forming connections, driving the session
154 // state to SS_Open and receiving and initially processing all incoming traffic.
156 m_hTransportThread = CreateThread(NULL, 0, TransportWorkerStatic, this, 0, NULL);
157 if (m_hTransportThread == NULL)
160 return E_OUTOFMEMORY;
166 // Drive the session to the SS_Closed state, which will deallocate all remaining transport resources
167 // (including terminating the transport thread). If this is the RS and the session state is SS_Open at the
168 // time of this call a graceful disconnect will be attempted (which tells the LS to go back to SS_Opening to
169 // look for a new RS rather than interpreting the disconnection as a temporary error and going into
170 // SS_Resync). On either side the session will no longer be functional after this call returns (though Init()
171 // may be called again to start over from the beginning).
172 void DbgTransportSession::Shutdown()
174 DbgTransportLog(LC_Proxy, "DbgTransportSession::Shutdown() called");
176 // The transport thread is allocated last in Init() (since it uses all the other resources that Init()
177 // prepares). Don't do any transport related stuff unless this was allocated (which can happen if
178 // Shutdown() is called after an Init() failure).
180 if (m_hTransportThread)
182 // From SS_Open state try a graceful disconnect.
183 if (m_eState == SS_Open)
185 DbgTransportLog(LC_Session, "Sending 'SessionClose'");
186 DBG_TRANSPORT_INC_STAT(SentSessionClose);
188 sMessage.Init(MT_SessionClose);
189 SendMessage(&sMessage, false);
192 // Must take the state lock to make a state transition.
194 TransportLockHolder sLockHolder(&m_sStateLock);
196 // Remember previous state and transition to SS_Closed.
197 SessionState ePreviousState = m_eState;
198 m_eState = SS_Closed;
200 if (ePreviousState != SS_Closed)
205 } // Leave m_sStateLock
207 #ifdef RIGHT_SIDE_COMPILE
208 // Signal the m_hSessionOpenEvent now to quickly error out any callers of WaitForSessionToOpen().
209 SetEvent(m_hSessionOpenEvent);
210 #endif // RIGHT_SIDE_COMPILE
213 // The transport instance is no longer valid
217 #ifndef RIGHT_SIDE_COMPILE
219 // Cleans up the named pipe connection so no tmp files are left behind. Does only
220 // the minimum and must be safe to call at any time. Called during PAL ExitProcess,
221 // TerminateProcess and for unhandled native exceptions and asserts.
222 void DbgTransportSession::AbortConnection()
227 // API used only by the LS to drive the transport into a state where it won't accept connections. This is used
228 // when no proxy is detected at startup but it's too late to shutdown all of the debugging system easily. It's
229 // mainly paranoia to increase the protection of your system when the proxy isn't started.
230 void DbgTransportSession::Neuter()
232 // Simply set the session state to SS_Closed. The transport thread will switch itself off if it ever gets
233 // a connection but the rest of the transport resources remain valid (so the debugger helper thread won't
234 // AV on a deallocated handle, which might happen if we simply called Shutdown()).
235 m_eState = SS_Closed;
238 #else // RIGHT_SIDE_COMPILE
240 // Used by debugger side (RS) to cleanup the target (LS) named pipes
241 // and semaphores when the debugger detects the debuggee process exited.
242 void DbgTransportSession::CleanupTargetProcess()
244 m_pipe.CleanupTargetProcess();
247 // On the RS it may be useful to wait and see if the session can reach the SS_Open state. If the target
248 // runtime has terminated for some reason then we'll never reach the open state. So the method below gives the
249 // RS a way to try and establish a connection for a reasonable amount of time and to time out otherwise. They
250 // could then call Shutdown on the session and report an error back to the rest of the debugger. The method
251 // returns true if the session opened within the time given (in milliseconds) and false otherwise.
252 bool DbgTransportSession::WaitForSessionToOpen(DWORD dwTimeout)
254 DWORD dwRet = WaitForSingleObject(m_hSessionOpenEvent, dwTimeout);
255 if (m_eState == SS_Closed)
258 if (dwRet == WAIT_TIMEOUT)
259 DbgTransportLog(LC_Proxy, "DbgTransportSession::WaitForSessionToOpen(%u) timed out", dwTimeout);
261 return dwRet == WAIT_OBJECT_0;
264 //---------------------------------------------------------------------------------------
266 // A valid ticket is returned if no other client is currently acting as the debugger.
267 // If the caller passes in a valid ticket, this function will return true without invalidating the ticket.
270 // pTicket - out parameter; set to a valid ticket if the client has successfully registered as the debugger
273 // Return true if the client has successfully registered as the debugger.
276 bool DbgTransportSession::UseAsDebugger(DebugTicket * pTicket)
278 TransportLockHolder sLockHolder(&m_sStateLock);
279 if (m_fDebuggerAttached)
281 if (pTicket->IsValid())
283 // The client already holds a valid ticket.
288 // Another client of this session has already indicated that it's using this session to debug.
289 _ASSERTE(!pTicket->IsValid());
295 m_fDebuggerAttached = true;
301 //---------------------------------------------------------------------------------------
303 // A valid ticket is required in order for this function to succeed. After this function succeeds,
304 // another client can request to be the debugger.
307 // pTicket - the client's ticket; must be valid for this function to succeed
310 // Return true if the client has successfully unregistered as the debugger.
311 // Return false if no client is currently acting as the debugger or if the client's ticket is invalid.
314 bool DbgTransportSession::StopUsingAsDebugger(DebugTicket * pTicket)
316 TransportLockHolder sLockHolder(&m_sStateLock);
317 if (m_fDebuggerAttached && pTicket->IsValid())
319 // The caller is indeed the owner of the debug ticket.
320 m_fDebuggerAttached = false;
321 pTicket->SetInvalid();
329 #endif // RIGHT_SIDE_COMPILE
331 // Sends a pre-initialized event to the other side.
332 HRESULT DbgTransportSession::SendEvent(DebuggerIPCEvent *pEvent)
334 DbgTransportLog(LC_Events, "Sending '%s'", IPCENames::GetName(pEvent->type));
335 DBG_TRANSPORT_INC_STAT(SentEvent);
337 return SendEventWorker(pEvent, IPCET_OldStyle);
340 // Sends a pre-initialized event to the other side, but pretend that this is coming from the native pipeline.
341 // See code:IPCEventType for more information.
342 HRESULT DbgTransportSession::SendDebugEvent(DebuggerIPCEvent * pEvent)
344 DbgTransportLog(LC_Events, "Sending '%s' as DEBUG_EVENT", IPCENames::GetName(pEvent->type));
345 DBG_TRANSPORT_INC_STAT(SentEvent);
347 return SendEventWorker(pEvent, IPCET_DebugEvent);
350 // Retrieves the auto-reset handle which is signalled by the session each time a new event is received from
352 HANDLE DbgTransportSession::GetIPCEventReadyEvent()
354 return m_rghEventReadyEvent[IPCET_OldStyle];
357 // Retrieves the auto-reset handle which is signalled by the session each time a new event (disguised as a
358 // debug event) is received from the other side.
359 HANDLE DbgTransportSession::GetDebugEventReadyEvent()
361 return m_rghEventReadyEvent[IPCET_DebugEvent];
364 // Copies the last event received from the other side into the provided buffer. This should only be called
365 // (once) after the event returned from GetIPCEEventReadyEvent()/GetDebugEventReadyEvent() has been signalled.
366 void DbgTransportSession::GetNextEvent(DebuggerIPCEvent *pEvent, DWORD cbEvent)
368 _ASSERTE(cbEvent <= CorDBIPC_BUFFER_SIZE);
370 // Must acquire the state lock to synchronize us wrt to the transport thread (clients already guarantee
371 // they serialize calls to this and waiting on m_rghEventReadyEvent).
372 TransportLockHolder sLockHolder(&m_sStateLock);
374 // There must be at least one valid event waiting (this call does not block).
375 _ASSERTE(m_cValidEventBuffers);
377 // Copy the first valid event into the client's buffer.
378 memcpy(pEvent, &m_pEventBuffers[m_idxEventBufferHead].m_event, cbEvent);
380 // Move the index of the head of the valid list forward (which may in fact move it back to the start of
381 // the array since the list is circular). This reduces the number of valid entries by one. Note that these
382 // two adjustments do not affect the tail of the list in any way. In the limit case the head will end up
383 // pointing to the same event as the tail (and m_cValidEventBuffers will be zero).
384 m_idxEventBufferHead = (m_idxEventBufferHead + 1) % m_cEventBuffers;
385 m_cValidEventBuffers--;
386 _ASSERTE(((m_idxEventBufferHead + m_cValidEventBuffers) % m_cEventBuffers) == m_idxEventBufferTail);
388 // If there's at least one more valid event we can signal event ready now.
389 if (m_cValidEventBuffers)
391 SetEvent(m_rghEventReadyEvent[m_pEventBuffers[m_idxEventBufferHead].m_type]);
397 void MarshalDCBTransportToDCB(DebuggerIPCControlBlockTransport* pIn, DebuggerIPCControlBlock* pOut)
399 pOut->m_DCBSize = pIn->m_DCBSize;
400 pOut->m_verMajor = pIn->m_verMajor;
401 pOut->m_verMinor = pIn->m_verMinor;
402 pOut->m_checkedBuild = pIn->m_checkedBuild;
403 pOut->m_bHostingInFiber = pIn->m_bHostingInFiber;
404 pOut->padding2 = pIn->padding2;
405 pOut->padding3 = pIn->padding3;
407 pOut->m_leftSideProtocolCurrent = pIn->m_leftSideProtocolCurrent;
408 pOut->m_leftSideProtocolMinSupported = pIn->m_leftSideProtocolMinSupported;
410 pOut->m_rightSideProtocolCurrent = pIn->m_rightSideProtocolCurrent;
411 pOut->m_rightSideProtocolMinSupported = pIn->m_rightSideProtocolMinSupported;
413 pOut->m_errorHR = pIn->m_errorHR;
414 pOut->m_errorCode = pIn->m_errorCode;
416 #if defined(DBG_TARGET_WIN64)
417 pOut->padding4 = pIn->padding4;
418 #endif // DBG_TARGET_WIN64
422 //pOut->m_rightSideEventAvailable
423 //pOut->m_rightSideEventRead
424 //pOut->m_paddingObsoleteLSEA
425 //pOut->m_paddingObsoleteLSER
426 //pOut->m_rightSideProcessHandle
427 //pOut->m_leftSideUnmanagedWaitEvent
429 pOut->m_realHelperThreadId = pIn->m_realHelperThreadId;
430 pOut->m_helperThreadId = pIn->m_helperThreadId;
431 pOut->m_temporaryHelperThreadId = pIn->m_temporaryHelperThreadId;
432 pOut->m_CanaryThreadId = pIn->m_CanaryThreadId;
433 pOut->m_pRuntimeOffsets = pIn->m_pRuntimeOffsets;
434 pOut->m_helperThreadStartAddr = pIn->m_helperThreadStartAddr;
435 pOut->m_helperRemoteStartAddr = pIn->m_helperRemoteStartAddr;
436 pOut->m_specialThreadList = pIn->m_specialThreadList;
439 //pOut->m_receiveBuffer
442 pOut->m_specialThreadListLength = pIn->m_specialThreadListLength;
443 pOut->m_shutdownBegun = pIn->m_shutdownBegun;
444 pOut->m_rightSideIsWin32Debugger = pIn->m_rightSideIsWin32Debugger;
445 pOut->m_specialThreadListDirty = pIn->m_specialThreadListDirty;
447 pOut->m_rightSideShouldCreateHelperThread = pIn->m_rightSideShouldCreateHelperThread;
451 void MarshalDCBToDCBTransport(DebuggerIPCControlBlock* pIn, DebuggerIPCControlBlockTransport* pOut)
453 pOut->m_DCBSize = pIn->m_DCBSize;
454 pOut->m_verMajor = pIn->m_verMajor;
455 pOut->m_verMinor = pIn->m_verMinor;
456 pOut->m_checkedBuild = pIn->m_checkedBuild;
457 pOut->m_bHostingInFiber = pIn->m_bHostingInFiber;
458 pOut->padding2 = pIn->padding2;
459 pOut->padding3 = pIn->padding3;
461 pOut->m_leftSideProtocolCurrent = pIn->m_leftSideProtocolCurrent;
462 pOut->m_leftSideProtocolMinSupported = pIn->m_leftSideProtocolMinSupported;
464 pOut->m_rightSideProtocolCurrent = pIn->m_rightSideProtocolCurrent;
465 pOut->m_rightSideProtocolMinSupported = pIn->m_rightSideProtocolMinSupported;
467 pOut->m_errorHR = pIn->m_errorHR;
468 pOut->m_errorCode = pIn->m_errorCode;
470 #if defined(DBG_TARGET_WIN64)
471 pOut->padding4 = pIn->padding4;
472 #endif // DBG_TARGET_WIN64
474 pOut->m_realHelperThreadId = pIn->m_realHelperThreadId;
475 pOut->m_helperThreadId = pIn->m_helperThreadId;
476 pOut->m_temporaryHelperThreadId = pIn->m_temporaryHelperThreadId;
477 pOut->m_CanaryThreadId = pIn->m_CanaryThreadId;
478 pOut->m_pRuntimeOffsets = pIn->m_pRuntimeOffsets;
479 pOut->m_helperThreadStartAddr = pIn->m_helperThreadStartAddr;
480 pOut->m_helperRemoteStartAddr = pIn->m_helperRemoteStartAddr;
481 pOut->m_specialThreadList = pIn->m_specialThreadList;
483 pOut->m_specialThreadListLength = pIn->m_specialThreadListLength;
484 pOut->m_shutdownBegun = pIn->m_shutdownBegun;
485 pOut->m_rightSideIsWin32Debugger = pIn->m_rightSideIsWin32Debugger;
486 pOut->m_specialThreadListDirty = pIn->m_specialThreadListDirty;
488 pOut->m_rightSideShouldCreateHelperThread = pIn->m_rightSideShouldCreateHelperThread;
493 #ifdef RIGHT_SIDE_COMPILE
494 // Read and write memory on the LS from the RS.
495 HRESULT DbgTransportSession::ReadMemory(PBYTE pbRemoteAddress, PBYTE pbBuffer, SIZE_T cbBuffer)
497 DbgTransportLog(LC_Requests, "Sending 'ReadMemory(0x%08X, %u)'", pbRemoteAddress, cbBuffer);
498 DBG_TRANSPORT_INC_STAT(SentReadMemory);
501 sMessage.Init(MT_ReadMemory, NULL, 0, pbBuffer, (DWORD)cbBuffer);
502 sMessage.m_sHeader.TypeSpecificData.MemoryAccess.m_pbLeftSideBuffer = pbRemoteAddress;
503 sMessage.m_sHeader.TypeSpecificData.MemoryAccess.m_cbLeftSideBuffer = (DWORD)cbBuffer;
505 HRESULT hr = SendRequestMessageAndWait(&sMessage);
509 // If we reached here the send was successful but the actual memory operation may not have been (due to
510 // unmapped memory or page protections etc.). So the final result comes back to us in the reply.
511 return sMessage.m_sHeader.TypeSpecificData.MemoryAccess.m_hrResult;
514 HRESULT DbgTransportSession::WriteMemory(PBYTE pbRemoteAddress, PBYTE pbBuffer, SIZE_T cbBuffer)
516 DbgTransportLog(LC_Requests, "Sending 'WriteMemory(0x%08X, %u)'", pbRemoteAddress, cbBuffer);
517 DBG_TRANSPORT_INC_STAT(SentWriteMemory);
520 sMessage.Init(MT_WriteMemory, pbBuffer, (DWORD)cbBuffer);
521 sMessage.m_sHeader.TypeSpecificData.MemoryAccess.m_pbLeftSideBuffer = pbRemoteAddress;
522 sMessage.m_sHeader.TypeSpecificData.MemoryAccess.m_cbLeftSideBuffer = (DWORD)cbBuffer;
524 HRESULT hr = SendRequestMessageAndWait(&sMessage);
528 // If we reached here the send was successful but the actual memory operation may not have been (due to
529 // unmapped memory or page protections etc.). So the final result comes back to us in the reply.
530 return sMessage.m_sHeader.TypeSpecificData.MemoryAccess.m_hrResult;
533 HRESULT DbgTransportSession::VirtualUnwind(DWORD threadId, ULONG32 contextSize, PBYTE context)
535 DbgTransportLog(LC_Requests, "Sending 'VirtualUnwind'");
536 DBG_TRANSPORT_INC_STAT(SentVirtualUnwind);
539 sMessage.Init(MT_VirtualUnwind, context, contextSize, context, contextSize);
540 return SendRequestMessageAndWait(&sMessage);
543 // Read and write the debugger control block on the LS from the RS.
544 HRESULT DbgTransportSession::GetDCB(DebuggerIPCControlBlock *pDCB)
546 DbgTransportLog(LC_Requests, "Sending 'GetDCB'");
547 DBG_TRANSPORT_INC_STAT(SentGetDCB);
550 DebuggerIPCControlBlockTransport dcbt;
551 sMessage.Init(MT_GetDCB, NULL, 0, (PBYTE)&dcbt, sizeof(DebuggerIPCControlBlockTransport));
552 HRESULT ret = SendRequestMessageAndWait(&sMessage);
554 MarshalDCBTransportToDCB(&dcbt, pDCB);
558 HRESULT DbgTransportSession::SetDCB(DebuggerIPCControlBlock *pDCB)
560 DbgTransportLog(LC_Requests, "Sending 'SetDCB'");
561 DBG_TRANSPORT_INC_STAT(SentSetDCB);
563 DebuggerIPCControlBlockTransport dcbt;
564 MarshalDCBToDCBTransport(pDCB, &dcbt);
567 sMessage.Init(MT_SetDCB, (PBYTE)&dcbt, sizeof(DebuggerIPCControlBlockTransport));
568 return SendRequestMessageAndWait(&sMessage);
572 // Read the AppDomain control block on the LS from the RS.
573 HRESULT DbgTransportSession::GetAppDomainCB(AppDomainEnumerationIPCBlock *pADB)
575 DbgTransportLog(LC_Requests, "Sending 'GetAppDomainCB'");
576 DBG_TRANSPORT_INC_STAT(SentGetAppDomainCB);
579 sMessage.Init(MT_GetAppDomainCB, NULL, 0, (PBYTE)pADB, sizeof(AppDomainEnumerationIPCBlock));
580 return SendRequestMessageAndWait(&sMessage);
583 #endif // RIGHT_SIDE_COMPILE
585 // Worker function for code:DbgTransportSession::SendEvent and code:DbgTransportSession::SendDebugEvent.
586 HRESULT DbgTransportSession::SendEventWorker(DebuggerIPCEvent * pEvent, IPCEventType type)
588 DWORD cbEvent = GetEventSize(pEvent);
589 _ASSERTE(cbEvent <= CorDBIPC_BUFFER_SIZE);
592 sMessage.Init(MT_Event, (PBYTE)pEvent, cbEvent);
594 // Store the event type in the header as well, it's sometimes useful for debugging.
595 sMessage.m_sHeader.TypeSpecificData.Event.m_eIPCEventType = type;
596 sMessage.m_sHeader.TypeSpecificData.Event.m_eType = pEvent->type;
598 return SendMessage(&sMessage, false);
601 // Sends a pre-formatted message (including the data block, if any). The fWaitsForReply indicates whether the
602 // caller is going to block until some sort of reply message is received (for instance an event that must be
603 // ack'd or a request such as MT_GetDCB that needs a reply). SendMessage() uses this to determine whether it
604 // needs to buffer the message before placing it on the send queue (since it may need to resend the message
605 // after a transitory network failure).
606 HRESULT DbgTransportSession::SendMessage(Message *pMessage, bool fWaitsForReply)
608 // Serialize the whole operation under the state lock. In particular we need to make allocating the
609 // message ID atomic wrt placing the message on the connection (to ensure our IDs are seen in order by the
610 // other side). We also need to hold the lock while manipulating the send queue (to prevent corruption)
611 // and while determining whether to send immediately or not depending on the session state (to avoid
612 // posting a send on a closed and possibly recycled socket).
614 TransportLockHolder sLockHolder(&m_sStateLock);
616 // Perform any last updates to the header or data block here since we might be about to encrypt them.
618 // Give this message a unique ID (useful both to track which messages need to be resent on a network
619 // failure and to match replies to the original message).
620 pMessage->m_sHeader.m_dwId = m_dwNextMessageId++;
622 // Use this message send to piggyback an acknowledgement of the last message we processed from the
623 // other side (this will allow the other side to discard one or more buffered messages from its send
625 pMessage->m_sHeader.m_dwLastSeenId = m_dwLastMessageIdSeen;
627 // If the caller isn't waiting around for a reply we must make a copy of the message to place on the
629 pMessage->m_pOrigMessage = pMessage;
630 Message *pMessageCopy = NULL;
631 PBYTE pDataBlockCopy = NULL;
634 // Allocate a new message (includes an embedded message header).
635 pMessageCopy = new (nothrow) Message();
636 if (pMessageCopy == NULL)
637 return E_OUTOFMEMORY;
639 // Allocate a new data block if one is being used.
640 if (pMessage->m_pbDataBlock)
642 pDataBlockCopy = new (nothrow) BYTE[pMessage->m_cbDataBlock];
643 if (pDataBlockCopy == NULL)
646 return E_OUTOFMEMORY;
650 // Copy the message descriptor over.
651 memcpy(pMessageCopy, pMessage, sizeof(Message));
653 // And the data block if applicable.
655 memcpy(pDataBlockCopy, pMessage->m_pbDataBlock, pMessage->m_cbDataBlock);
657 // The message copy still points to the wrong data block (if there is one).
658 pMessageCopy->m_pbDataBlock = pDataBlockCopy;
660 // Point the copy back to the original message.
661 pMessageCopy->m_pOrigMessage = pMessage;
663 // From now on we'll use the copy.
664 pMessage = pMessageCopy;
667 // Check the session state.
668 if (m_eState == SS_Closed)
670 // SS_Closed is bad news, we'll never recover from that so error the send immediately.
674 delete [] pDataBlockCopy;
679 // Don't queue session management messages. We always recreate these if we need to re-send them.
680 if (pMessage->m_sHeader.m_eType > MT_SessionClose)
682 // Regardless of session state we always queue the message for at least as long as it takes us to
683 // be sure the other side has received the message.
684 if (m_pSendQueueLast == NULL)
686 // Queue is currently empty.
687 m_pSendQueueFirst = pMessage;
688 m_pSendQueueLast = pMessage;
689 pMessage->m_pNext = NULL;
693 // Place on end of queue.
694 m_pSendQueueLast->m_pNext = pMessage;
695 m_pSendQueueLast = pMessage;
696 pMessage->m_pNext = NULL;
700 // If the state is SS_Open we can send the message now.
701 if (m_eState == SS_Open)
703 // Send the message header block followed by the data block if it's provided. Any network error will
704 // be reported internally by SendBlock and result in a transition to the SS_Resync_NC state (and an
705 // eventual resend of the data).
706 if (SendBlock((PBYTE)&pMessage->m_sHeader, sizeof(MessageHeader)) && pMessage->m_pbDataBlock)
707 SendBlock(pMessage->m_pbDataBlock, pMessage->m_cbDataBlock);
710 // If the state wasn't open there's nothing more to be done. The state will eventually transition to
711 // either SS_Open (in which case the transport thread will send all pending messages for us at the
712 // transition point) or SS_Closed (where the transport thread will drain the queue and discard each
713 // message, setting m_fAborted if necessary).
715 } // Leave m_sStateLock
720 // Helper method for sending messages requiring a reply (such as MT_GetDCB) and waiting on the result.
721 HRESULT DbgTransportSession::SendRequestMessageAndWait(Message *pMessage)
723 // Allocate event to wait for reply on.
724 pMessage->m_hReplyEvent = WszCreateEvent(NULL, FALSE, FALSE, NULL); // Auto-reset, not signalled
725 if (pMessage->m_hReplyEvent == NULL)
726 return E_OUTOFMEMORY;
728 // Duplicate the handle to the event. It's necessary to have two handles to the same event because
729 // both this thread and the message pumping thread may be trying to access the handle at the same
730 // time (e.g. closing the handle). So we make a duplicate handle. This thread is responsible for
731 // closing hReplyEvent (the local variable) whereas the message pumping thread is responsible for
732 // closing the handle on the message.
733 HANDLE hReplyEvent = NULL;
734 if (!DuplicateHandle(GetCurrentProcess(),
735 pMessage->m_hReplyEvent,
738 0, // ignored since we are going to pass DUPLICATE_SAME_ACCESS
740 DUPLICATE_SAME_ACCESS))
742 return HRESULT_FROM_GetLastError();
746 HRESULT hr = SendMessage(pMessage, true);
749 // In this case, we need to close both handles since the message is never put into the send queue.
750 // This thread is the only one who has access to the message.
751 CloseHandle(pMessage->m_hReplyEvent);
752 CloseHandle(hReplyEvent);
756 // At this point, the message pumping thread may receive the reply any time. It may even receive the
757 // reply message even before we wait on the event. Keep this in mind.
759 // Wait for a reply (by the time this event is signalled the message header will have been overwritten by
760 // the reply and any output buffer provided will have been filled in).
761 #if defined(RIGHT_SIDE_COMPILE)
762 HANDLE rgEvents[] = { hReplyEvent, m_hProcessExited };
763 #else // !RIGHT_SIDE_COMPILE
764 HANDLE rgEvents[] = { hReplyEvent };
765 #endif // RIGHT_SIDE_COMPILE
767 DWORD dwResult = WaitForMultipleObjectsEx(sizeof(rgEvents)/sizeof(rgEvents[0]), rgEvents, FALSE, INFINITE, FALSE);
769 if (dwResult == WAIT_OBJECT_0)
771 // This is the normal case. The message pumping thread receives a reply from the debuggee process.
772 // It signals the event to wake up this thread.
773 CloseHandle(hReplyEvent);
775 // Check whether the session aborted us due to a Shutdown().
776 if (pMessage->m_fAborted)
779 #if defined(RIGHT_SIDE_COMPILE)
780 else if (dwResult == (WAIT_OBJECT_0 + 1))
782 // This is the complicated case. This thread wakes up because the debuggee process is terminated.
783 // At the same time, the message pumping thread may be in the process of handling the reply message.
784 // We need to be careful here because there is a race condition.
786 // Remove the original message from the send queue. This is because in the case of a blocking message,
787 // the message can be allocated on the stack. Thus, the message becomes invalid when we return from
788 // this function. The message pumping thread may have beaten this thread to it. That's ok since
789 // RemoveMessageFromSendQueue() takes the state lock.
790 Message * pOriginalMessage = RemoveMessageFromSendQueue(pMessage->m_sHeader.m_dwId);
791 _ASSERTE((pOriginalMessage == NULL) || (pOriginalMessage == pMessage));
793 // If the message pumping thread has beaten this thread to removing the original message, then this
794 // thread must wait until the message pumping thread is done with the message before returning.
795 // Otherwise, the message may become invalid when the message pumping thread is accessing it.
796 // Fortunately, in this case, we know the message pumping thread is going to signal the event.
797 if (pOriginalMessage == NULL)
799 WaitForSingleObject(hReplyEvent, INFINITE);
802 CloseHandle(hReplyEvent);
803 return CORDBG_E_PROCESS_TERMINATED;
805 #endif // RIGHT_SIDE_COMPILE
808 // Should never get here.
809 CloseHandle(hReplyEvent);
816 // Sends a single contiguous buffer of host memory over the connection. The caller is responsible for holding
817 // the state lock and ensuring the session state is SS_Open. Returns false if the send failed (the error will
818 // have already caused the recovery logic to kick in, so handling it is not required, the boolean is just
819 // returned so that any further blocks in the message are not sent).
820 bool DbgTransportSession::SendBlock(PBYTE pbBuffer, DWORD cbBuffer)
822 _ASSERTE(m_eState == SS_Opening || m_eState == SS_Resync || m_eState == SS_Open);
823 _ASSERTE(m_pipe.GetState() == TwoWayPipe::ServerConnected || m_pipe.GetState() == TwoWayPipe::ClientConnected);
824 _ASSERTE(cbBuffer > 0);
826 DBG_TRANSPORT_INC_STAT(SentBlocks);
827 DBG_TRANSPORT_ADD_STAT(SentBytes, cbBuffer);
829 //DbgTransportLog(LC_Proxy, "SendBlock(%08X, %u)", pbBuffer, cbBuffer);
831 if (DBG_TRANSPORT_SHOULD_INJECT_FAULT(Send))
834 fSuccess = (m_pipe.Write(pbBuffer, cbBuffer) == cbBuffer);
838 DbgTransportLog(LC_NetErrors, "Network error on Send()");
839 DBG_TRANSPORT_INC_STAT(SendErrors);
840 HandleNetworkError(true);
847 // Receives a single contiguous buffer of host memory over the connection. No state lock needs to be held
848 // (receives are serialized by the fact they're only performed on the transport thread). Returns false if a
849 // network error is encountered (which will automatically transition the session into the correct retry
851 bool DbgTransportSession::ReceiveBlock(PBYTE pbBuffer, DWORD cbBuffer)
853 _ASSERTE(m_pipe.GetState() == TwoWayPipe::ServerConnected || m_pipe.GetState() == TwoWayPipe::ClientConnected);
854 _ASSERTE(cbBuffer > 0);
856 DBG_TRANSPORT_INC_STAT(ReceivedBlocks);
857 DBG_TRANSPORT_ADD_STAT(ReceivedBytes, cbBuffer);
859 //DbgTransportLog(LC_Proxy, "ReceiveBlock(%08X, %u)", pbBuffer, cbBuffer);
862 if (DBG_TRANSPORT_SHOULD_INJECT_FAULT(Receive))
865 fSuccess = (m_pipe.Read(pbBuffer, cbBuffer) == cbBuffer);
869 DbgTransportLog(LC_NetErrors, "Network error on Receive()");
870 DBG_TRANSPORT_INC_STAT(ReceiveErrors);
871 HandleNetworkError(false);
878 // Called upon encountering a network error (e.g. an error from Send() or Receive()). This handles pushing the
879 // session state into SS_Resync_NC or SS_Opening_NC in order to start the recovery process.
880 void DbgTransportSession::HandleNetworkError(bool fCallerHoldsStateLock)
882 _ASSERTE(m_eState == SS_Open || m_eState == SS_Opening || m_eState == SS_Resync || !fCallerHoldsStateLock);
884 // Check the easy cases first which don't require us to take the lock (because we don't transition the
885 // state). These are the SS_Closed state (a network error doesn't matter when we're closing down the
886 // session anyway) and the SS_*_NC states (which indicate someone else beat us to it, closed the
887 // connection and has started recovery).
888 if (m_eState == SS_Closed ||
889 m_eState == SS_Opening_NC ||
890 m_eState == SS_Resync_NC)
893 // We need the state lock to perform a state transition.
894 if (!fCallerHoldsStateLock)
895 m_sStateLock.Enter();
902 // Still need to cope with the no-op states handled above since we could have transitioned into them
903 // before we took the lock.
907 // All work to transition SS_Opening to SS_Open is performed by the transport thread, so we know we're
908 // on that thread. Consequently it's just enough to set the state to SS_Opening_NC and the thread will
909 // notice the change when the SendMessage() or ReceiveBlock() call completes.
910 m_eState = SS_Opening_NC;
914 // Likewise, all the work to transition SS_Resync to SS_Open is performed by the transport thread, so
915 // we know we're on that thread.
916 m_eState = SS_Resync_NC;
920 // The state change to SS_Resync_NC will prompt the transport thread (which might be this thread) that
921 // it should discard the current connection and reform a new one. It will also cause sends to be
922 // queued instead of sent. In case we're not the transport thread and instead it is currently stuck in
923 // a Receive (I don't entirely trust the connection to immediately fail these on a network problem)
924 // we'll call CancelReceive() to abort the operation. The transport thread itself will handle the
925 // actual Destroy() (having one thread do this management greatly simplifies things).
926 m_eState = SS_Resync_NC;
931 _ASSERTE(!"Unknown session state");
934 if (!fCallerHoldsStateLock)
935 m_sStateLock.Leave();
938 // Scan the send queue and discard any messages which have been processed by the other side according to the
939 // specified ID). Messages waiting on a reply message (e.g. MT_GetDCB) will be retained until that reply is
940 // processed. FlushSendQueue will take the state lock.
941 void DbgTransportSession::FlushSendQueue(DWORD dwLastProcessedId)
943 // Must access the send queue under the state lock.
944 TransportLockHolder sLockHolder(&m_sStateLock);
946 // Note that message headers (and data blocks) may be encrypted. Use the cached fields in the Message
947 // structure to compare message IDs and types.
949 Message *pMsg = m_pSendQueueFirst;
950 Message *pLastMsg = NULL;
953 if (pMsg->m_sHeader.m_dwId <= dwLastProcessedId)
955 // Message has been seen and processed by other side.
956 // Check if we can discard it (i.e. it's not waiting on a reply message that needs the original
957 // request to hang around).
958 #ifdef RIGHT_SIDE_COMPILE
959 MessageType eType = pMsg->m_sHeader.m_eType;
960 if (eType != MT_ReadMemory &&
961 eType != MT_WriteMemory &&
962 eType != MT_VirtualUnwind &&
963 eType != MT_GetDCB &&
964 eType != MT_SetDCB &&
965 eType != MT_GetAppDomainCB)
966 #endif // RIGHT_SIDE_COMPILE
968 #ifdef RIGHT_SIDE_COMPILE
969 _ASSERTE(eType == MT_Event);
970 #endif // RIGHT_SIDE_COMPILE
972 // We can discard this message.
974 // Unlink it from the queue.
975 if (pLastMsg == NULL)
976 m_pSendQueueFirst = pMsg->m_pNext;
978 pLastMsg->m_pNext = pMsg->m_pNext;
979 if (m_pSendQueueLast == pMsg)
980 m_pSendQueueLast = pLastMsg;
982 Message *pDiscardMsg = pMsg;
983 pMsg = pMsg->m_pNext;
985 // If the message is a copy deallocate it (and the data block associated with it).
986 if (pDiscardMsg->m_pOrigMessage != pDiscardMsg)
988 if (pDiscardMsg->m_pbDataBlock)
989 delete [] pDiscardMsg->m_pbDataBlock;
998 pMsg = pMsg->m_pNext;
1002 #ifdef RIGHT_SIDE_COMPILE
1003 // Perform processing required to complete a request (such as MT_GetDCB) once a reply comes in. This includes
1004 // reading data from the connection into the output buffer, removing the original message from the send queue
1005 // and signalling the completion event. Returns true if no network error was encountered.
1006 bool DbgTransportSession::ProcessReply(MessageHeader *pHeader)
1008 // Locate original message on the send queue.
1009 Message *pMsg = RemoveMessageFromSendQueue(pHeader->m_dwReplyId);
1011 // This can happen if the thread blocked waiting for the replyl message has waken up because the debuggee
1012 // process has terminated. See code:DbgTransportSession::SendRequestMessageAndWait() for more info.
1018 // If there is a reply block but the caller hasn't specified a reply buffer.
1019 // This combination is not used any more.
1020 _ASSERTE(! ((pHeader->m_cbDataBlock != (DWORD)0) && (pMsg->m_pbReplyBlock == (PBYTE)NULL)) );
1022 // If there was an output buffer provided then we copy the data block in the reply into it (perhaps
1023 // decrypting it first). If the reply header indicates there is no data block then presumably the request
1024 // failed (which should be indicated in the TypeSpecificData of the reply, ala MT_ReadMemory).
1025 if (pMsg->m_pbReplyBlock && pHeader->m_cbDataBlock)
1027 _ASSERTE(pHeader->m_cbDataBlock == pMsg->m_cbReplyBlock);
1028 if (!ReceiveBlock(pMsg->m_pbReplyBlock, pMsg->m_cbReplyBlock))
1030 // Whoops. We hit an error trying to read the reply data. We need to push the original message
1031 // back on the queue and await a retry. Since this message must have been seen by the other side
1032 // we don't need to put it on the queue in order (it will never be resent). Easiest just to put it
1035 TransportLockHolder sLockHolder(&m_sStateLock);
1036 pMsg->m_pNext = m_pSendQueueFirst;
1037 m_pSendQueueFirst = pMsg;
1038 if (m_pSendQueueLast == NULL)
1039 m_pSendQueueLast = pMsg;
1041 } // Leave m_sStateLock
1045 // Copy TypeSpecificData from the reply back into the original message (it can contain additional status).
1046 // Be careful to update the real original message (the version on the queue will be a copy if we're using
1047 // a secure session).
1048 pMsg->m_pOrigMessage->m_sHeader.TypeSpecificData = pHeader->TypeSpecificData;
1050 // **** IMPORTANT NOTE ****
1051 // We're about to cause a side-effect visible to our client. From here on out (until we update the
1052 // session's idea of the last incoming message we processed back in the transport thread's main loop) we
1053 // must avoid any failures. If we fail before the update the other side will re-send the message which is
1054 // bad if we've already processed it. See the comment near the start of the SS_Open message dispatch logic
1055 // for more details.
1056 // **** IMPORTANT NOTE ****
1058 // Signal the completion event.
1059 SignalReplyEvent(pMsg);
1064 //---------------------------------------------------------------------------------------
1066 // Upon receiving a reply message, signal the event on the message to wake up the thread waiting for
1067 // the reply message and close the handle to the event.
1070 // pMessage - the reply message to be processed
1073 void DbgTransportSession::SignalReplyEvent(Message * pMessage)
1075 // Make a local copy of the event handle. As soon as we signal the event, the thread blocked waiting on
1076 // the reply may wake up and trash the message. See code:DbgTransportSession::SendRequestMessageAndWait()
1078 HANDLE hReplyEvent = pMessage->m_hReplyEvent;
1079 _ASSERTE(hReplyEvent != NULL);
1081 SetEvent(hReplyEvent);
1082 CloseHandle(hReplyEvent);
1085 //---------------------------------------------------------------------------------------
1087 // Given a message ID, find the matching message in the send queue. If there is no match, return NULL.
1088 // If there is a match, remove the message from the send queue and return it.
1091 // dwMessageId - the ID of the message to retrieve
1094 // NULL if the specified message cannot be found.
1095 // Otherwise return the specified message with the side effect that it's also removed from the send queue.
1098 // The caller is NOT responsible for taking the state lock. This function will do that.
1101 DbgTransportSession::Message * DbgTransportSession::RemoveMessageFromSendQueue(DWORD dwMessageId)
1103 // Locate original message on the send queue.
1104 Message *pMsg = NULL;
1106 TransportLockHolder sLockHolder(&m_sStateLock);
1108 pMsg = m_pSendQueueFirst;
1109 Message *pLastMsg = NULL;
1113 if (dwMessageId == pMsg->m_sHeader.m_dwId)
1115 // Found the original message that this is a reply to. Unlink it.
1116 if (pLastMsg == NULL)
1117 m_pSendQueueFirst = pMsg->m_pNext;
1119 pLastMsg->m_pNext = pMsg->m_pNext;
1121 if (m_pSendQueueLast == pMsg)
1122 m_pSendQueueLast = pLastMsg;
1127 pMsg = pMsg->m_pNext;
1129 } // Leave m_sStateLock
1136 #ifndef RIGHT_SIDE_COMPILE
1139 __attribute__((noinline))
1140 __attribute__((optnone))
1142 ProbeMemory(__in_ecount(cbBuffer) volatile PBYTE pbBuffer, DWORD cbBuffer, bool fWriteAccess)
1144 // Need an throw in this function to fool the C++ runtime into handling the
1145 // possible h/w exception below.
1146 if (pbBuffer == NULL)
1148 throw PAL_SEHException();
1151 // Simple one byte at a time probing
1152 while (cbBuffer > 0)
1154 volatile BYTE read = *pbBuffer;
1163 #endif // FEATURE_PAL
1165 // Check read and optionally write memory access to the specified range of bytes. Used to check
1166 // ReadProcessMemory and WriteProcessMemory requests.
1167 HRESULT DbgTransportSession::CheckBufferAccess(__in_ecount(cbBuffer) PBYTE pbBuffer, DWORD cbBuffer, bool fWriteAccess)
1169 // check for integer overflow
1170 if ((pbBuffer + cbBuffer) < pbBuffer)
1172 return HRESULT_FROM_WIN32(ERROR_ARITHMETIC_OVERFLOW);
1175 // VirtualQuery doesn't know much about memory allocated outside of PAL's VirtualAlloc
1176 // that's why on Unix we can't rely on in to detect invalid memory reads
1180 // Find the attributes of the largest set of pages with common attributes starting from our base address.
1181 MEMORY_BASIC_INFORMATION sMemInfo;
1182 VirtualQuery(pbBuffer, &sMemInfo, sizeof(sMemInfo));
1184 DbgTransportLog(LC_Proxy, "CBA(%08X,%08X): State:%08X Protect:%08X BA:%08X RS:%08X",
1185 pbBuffer, cbBuffer, sMemInfo.State, sMemInfo.Protect, sMemInfo.BaseAddress, sMemInfo.RegionSize);
1187 // The memory must be committed (i.e. have physical pages or backing store).
1188 if (sMemInfo.State != MEM_COMMIT)
1189 return HRESULT_FROM_WIN32(ERROR_INVALID_ADDRESS);
1191 // Check for compatible page protections. Lower byte of Protect has these (upper bytes have options we're
1192 // not interested in, cache modes and the like.
1193 DWORD dwProtect = sMemInfo.Protect & 0xff;
1196 ((dwProtect & (PAGE_EXECUTE_READWRITE | PAGE_EXECUTE_WRITECOPY | PAGE_READWRITE | PAGE_WRITECOPY)) == 0))
1197 return HRESULT_FROM_WIN32(ERROR_NOACCESS);
1198 else if (!fWriteAccess &&
1199 ((dwProtect & (PAGE_EXECUTE_READ | PAGE_EXECUTE_READWRITE | PAGE_EXECUTE_WRITECOPY | PAGE_READONLY | PAGE_READWRITE | PAGE_WRITECOPY)) == 0))
1200 return HRESULT_FROM_WIN32(ERROR_NOACCESS);
1202 // If the requested range is bigger than the region we have queried,
1203 // we need to continue on to check the next region.
1204 if ((pbBuffer + cbBuffer) > ((PBYTE)sMemInfo.BaseAddress + sMemInfo.RegionSize))
1206 PBYTE pbRegionEnd = reinterpret_cast<PBYTE>(sMemInfo.BaseAddress) + sMemInfo.RegionSize;
1207 cbBuffer = (DWORD)((pbBuffer + cbBuffer) - pbRegionEnd);
1208 pbBuffer = pbRegionEnd;
1212 // We are done. Set cbBuffer to 0 to exit this loop.
1216 while (cbBuffer > 0);
1220 // Need to explicit h/w exception holder so to catch them in ProbeMemory
1221 CatchHardwareExceptionHolder __catchHardwareException;
1223 ProbeMemory(pbBuffer, cbBuffer, fWriteAccess);
1227 return HRESULT_FROM_WIN32(ERROR_INVALID_ADDRESS);
1231 // The specified region has passed all of our checks.
1235 #endif // !RIGHT_SIDE_COMPILE
1237 // Initialize all session state to correct starting values. Used during Init() and on the LS when we
1238 // gracefully close one session and prepare for another.
1239 void DbgTransportSession::InitSessionState()
1241 DBG_TRANSPORT_INC_STAT(Sessions);
1243 m_dwMajorVersion = kCurrentMajorVersion;
1244 m_dwMinorVersion = kCurrentMinorVersion;
1246 memset(&m_sSessionID, 0, sizeof(m_sSessionID));
1248 m_pSendQueueFirst = NULL;
1249 m_pSendQueueLast = NULL;
1251 m_dwNextMessageId = 1;
1252 m_dwLastMessageIdSeen = 0;
1254 m_eState = SS_Opening_NC;
1256 m_cValidEventBuffers = 0;
1257 m_idxEventBufferHead = 0;
1258 m_idxEventBufferTail = 0;
1261 // The entry point of the transport worker thread. This one's static, so we immediately dispatch to an
1262 // instance method version defined below for convenience in the implementation.
1263 DWORD WINAPI DbgTransportSession::TransportWorkerStatic(LPVOID pvContext)
1265 ((DbgTransportSession*)pvContext)->TransportWorker();
1267 // Nobody looks at this result, the choice of 0 is arbitrary.
1271 // Macros used to simplify error and state transition handling within the transport worker loop. Errors are
1272 // classified as either transient or critical. Transient errors (typically those from network operations)
1273 // result in the connection being closed and rebuilt: we should eventually recover from them. Critical errors
1274 // are those that cause a transition to the SS_Closed state, which the session never recovers from. These are
1275 // normally due to protocol errors where we want to shut the transport down in case they are of malicious
1277 #define HANDLE_TRANSIENT_ERROR() do { \
1278 HandleNetworkError(false); \
1279 m_pipe.Disconnect(); \
1280 goto ResetConnection; \
1283 #define HANDLE_CRITICAL_ERROR() do { \
1284 m_eState = SS_Closed; \
1289 #pragma warning(push)
1290 #pragma warning(disable:21000) // Suppress PREFast warning about overly large function
1292 void DbgTransportSession::TransportWorker()
1294 _ASSERTE(m_eState == SS_Opening_NC);
1296 // Loop until shutdown. Each loop iteration involves forming a connection (or waiting for one to form)
1297 // followed by processing incoming messages on that connection until there's a failure (either here of
1298 // from a send on another thread) or the session shuts down. The connection is then closed and discarded
1299 // and we either go round the loop again (to recover our previous session state) or exit the method as
1300 // part of shutdown.
1302 while (m_eState != SS_Closed)
1304 _ASSERTE(m_eState == SS_Opening_NC || m_eState == SS_Resync_NC || m_eState == SS_Closed);
1306 DbgTransportLog(LC_Proxy, "Forming new connection");
1308 #ifdef RIGHT_SIDE_COMPILE
1309 // The session is definitely not open at this point.
1310 ResetEvent(m_hSessionOpenEvent);
1312 // On the right side we initiate the connection via Connect(). A failure is dealt with by waiting a
1313 // little while and retrying (the LS may take a little while to set up). If there's nobody listening
1314 // the debugger will eventually get bored waiting for us and shutdown the session, which will
1315 // terminate this loop.
1317 if (DBG_TRANSPORT_SHOULD_INJECT_FAULT(Connect))
1318 eStatus = SCS_NetworkFailure;
1321 if (m_pipe.Connect(m_pid))
1323 eStatus = SCS_Success;
1327 //not really sure that this is the real failure
1328 //TODO: we probably need to analyse GetErrorCode() here
1329 eStatus = SCS_NoListener;
1333 if (eStatus != SCS_Success)
1335 DbgTransportLog(LC_Proxy, "AllocateConnection() failed with %u\n", eStatus);
1336 DBG_TRANSPORT_INC_STAT(MiscErrors);
1337 _ASSERTE(m_pipe.GetState() != TwoWayPipe::ClientConnected);
1341 #else // RIGHT_SIDE_COMPILE
1343 if (DBG_TRANSPORT_SHOULD_INJECT_FAULT(Accept))
1344 eStatus = SCS_NetworkFailure;
1347 DWORD pid = GetCurrentProcessId();
1348 if (m_pipe.CreateServer(pid) && m_pipe.WaitForConnection())
1350 eStatus = SCS_Success;
1354 //not really sure that this is the real failure
1355 //TODO: we probably need to analyse GetErrorCode() here
1356 eStatus = SCS_NoListener;
1360 if (eStatus != SCS_Success)
1362 DbgTransportLog(LC_Proxy, "Accept() failed with %u\n", eStatus);
1363 DBG_TRANSPORT_INC_STAT(MiscErrors);
1364 _ASSERTE(m_pipe.GetState() != TwoWayPipe::ServerConnected);
1369 // Note that when resynching a session we may let in a connection from a different debugger. That's
1370 // OK, we'll reject his SessionRequest message in due course and drop the connection.
1371 #endif // RIGHT_SIDE_COMPILE
1373 DBG_TRANSPORT_INC_STAT(Connections);
1375 // We now have a connection. Transition to the next state (either SS_Opening or SS_Resync). The
1376 // primary purpose of this state transition is to let other threads know that this thread might now be
1377 // blocked on a Receive() on the newly formed connection (important if they want to transition the state
1380 TransportLockHolder sLockHolder(&m_sStateLock);
1382 if (m_eState == SS_Closed)
1384 else if (m_eState == SS_Opening_NC)
1385 m_eState = SS_Opening;
1386 else if (m_eState == SS_Resync_NC)
1387 m_eState = SS_Resync;
1389 _ASSERTE(!"Bad session state");
1390 } // Leave m_sStateLock
1393 // Now we have a connection in place. Start reading messages and processing them. Which messages are
1394 // valid depends on whether we're in SS_Opening or SS_Resync (the state can change at any time
1395 // asynchronously to us to either SS_Closed or SS_Resync_NC but we're guaranteed the connection stays
1396 // valid (though not necessarily useful) until we notice this state change and Destroy() it ourself).
1397 // We check the state after each network operation.
1399 // During the SS_Opening and SS_Resync states we're guarantee to be the only thread posting sends, so
1400 // we can break the rules and use SendBlock without acquiring the state lock. (We use SendBlock a lot
1401 // during these phases because we're using simple Session* messages which don't require the extra
1402 // processing SendMessage gives us such as encryption or placement on the send queue).
1404 MessageHeader sSendHeader;
1405 MessageHeader sReceiveHeader;
1407 memset(&sSendHeader, 0, sizeof(MessageHeader));
1409 if (m_eState == SS_Opening)
1411 #ifdef RIGHT_SIDE_COMPILE
1412 // The right side actually starts things off by sending a SessionRequest message.
1414 SessionRequestData sDataBlock;
1416 sSendHeader.m_eType = MT_SessionRequest;
1417 sSendHeader.TypeSpecificData.VersionInfo.m_dwMajorVersion = kCurrentMajorVersion;
1418 sSendHeader.TypeSpecificData.VersionInfo.m_dwMinorVersion = kCurrentMinorVersion;
1420 // The start of the data block always contains a session ID. This is a GUID randomly generated at
1422 sSendHeader.m_cbDataBlock = sizeof(SessionRequestData);
1423 memcpy(&sDataBlock.m_sSessionID, &m_sSessionID, sizeof(m_sSessionID));
1425 // Send the header block followed by the data block. For failures during SS_Opening we just close
1426 // the connection and retry from the beginning (the failing send will already have caused a
1427 // transition into SS_Opening_NC. No need to use the same resend logic that SS_Resync does, since
1428 // no user messages have been sent and we can simply recreate the SessionRequest.
1429 DbgTransportLog(LC_Session, "Sending 'SessionRequest'");
1430 DBG_TRANSPORT_INC_STAT(SentSessionRequest);
1431 if (!SendBlock((PBYTE)&sSendHeader, sizeof(MessageHeader)) ||
1432 !SendBlock((PBYTE)&sDataBlock, sSendHeader.m_cbDataBlock))
1433 HANDLE_TRANSIENT_ERROR();
1435 // Wait for a reply.
1436 if (!ReceiveBlock((PBYTE)&sReceiveHeader, sizeof(MessageHeader)))
1437 HANDLE_TRANSIENT_ERROR();
1439 DbgTransportLogMessageReceived(&sReceiveHeader);
1441 // This should be either a SessionAccept or SessionReject. Any other message type will be treated
1442 // as a SessionReject (i.e. an unrecoverable failure that will leave the session in SS_Closed
1444 if (sReceiveHeader.m_eType != MT_SessionAccept)
1446 _ASSERTE(!"Unexpected response to SessionRequest");
1447 HANDLE_CRITICAL_ERROR();
1450 // Validate the SessionAccept.
1451 if (sReceiveHeader.TypeSpecificData.VersionInfo.m_dwMajorVersion != kCurrentMajorVersion ||
1452 sReceiveHeader.m_cbDataBlock != (DWORD)0)
1454 _ASSERTE(!"Malformed SessionAccept received");
1455 HANDLE_CRITICAL_ERROR();
1458 // The LS might have negotiated the minor protocol version down.
1459 m_dwMinorVersion = sReceiveHeader.TypeSpecificData.VersionInfo.m_dwMinorVersion;
1460 #else // RIGHT_SIDE_COMPILE
1462 // On the left side we wait for a SessionRequest first.
1463 if (!ReceiveBlock((PBYTE)&sReceiveHeader, sizeof(MessageHeader)))
1464 HANDLE_TRANSIENT_ERROR();
1466 DbgTransportLogMessageReceived(&sReceiveHeader);
1468 if (sReceiveHeader.m_eType != MT_SessionRequest)
1470 _ASSERTE(!"Unexpected message type");
1471 HANDLE_CRITICAL_ERROR();
1474 // Validate the SessionRequest.
1475 if (sReceiveHeader.TypeSpecificData.VersionInfo.m_dwMajorVersion != kCurrentMajorVersion ||
1476 sReceiveHeader.m_cbDataBlock != (DWORD)sizeof(SessionRequestData))
1478 // Send a SessionReject message with the reason for rejection.
1479 sSendHeader.m_eType = MT_SessionReject;
1480 sSendHeader.TypeSpecificData.SessionReject.m_eReason = RR_IncompatibleVersion;
1481 sSendHeader.TypeSpecificData.SessionReject.m_dwMajorVersion = kCurrentMajorVersion;
1482 sSendHeader.TypeSpecificData.SessionReject.m_dwMinorVersion = kCurrentMinorVersion;
1484 DbgTransportLog(LC_Session, "Sending 'SessionReject(RR_IncompatibleVersion)'");
1485 DBG_TRANSPORT_INC_STAT(SentSessionReject);
1487 SendBlock((PBYTE)&sSendHeader, sizeof(MessageHeader));
1489 // Go back into the opening state rather than closed because we want to give the RS a chance
1490 // to correct the problem and try again.
1491 HANDLE_TRANSIENT_ERROR();
1494 // Read the data block.
1495 SessionRequestData sDataBlock;
1496 if (!ReceiveBlock((PBYTE)&sDataBlock, sizeof(SessionRequestData)))
1497 HANDLE_TRANSIENT_ERROR();
1499 // If the RS only understands a lower minor protocol version than us then remember that fact.
1500 if (sReceiveHeader.TypeSpecificData.VersionInfo.m_dwMinorVersion < m_dwMinorVersion)
1501 m_dwMinorVersion = sReceiveHeader.TypeSpecificData.VersionInfo.m_dwMinorVersion;
1503 // Send a SessionAccept message back.
1504 sSendHeader.m_eType = MT_SessionAccept;
1505 sSendHeader.m_cbDataBlock = 0;
1506 sSendHeader.TypeSpecificData.VersionInfo.m_dwMajorVersion = kCurrentMajorVersion;
1507 sSendHeader.TypeSpecificData.VersionInfo.m_dwMinorVersion = m_dwMinorVersion;
1509 DbgTransportLog(LC_Session, "Sending 'SessionAccept'");
1510 DBG_TRANSPORT_INC_STAT(SentSessionAccept);
1512 if (!SendBlock((PBYTE)&sSendHeader, sizeof(MessageHeader)))
1513 HANDLE_TRANSIENT_ERROR();
1514 #endif // RIGHT_SIDE_COMPILE
1516 // Everything pans out, we have a session formed. But we must send messages that queued up
1517 // before transitioning the state to open (otherwise a racing send could sneak in ahead).
1519 // Must access the send queue under the state lock.
1521 TransportLockHolder sLockHolder(&m_sStateLock);
1522 Message *pMsg = m_pSendQueueFirst;
1525 if (SendBlock((PBYTE)&pMsg->m_sHeader, sizeof(MessageHeader)) && pMsg->m_pbDataBlock)
1526 SendBlock(pMsg->m_pbDataBlock, pMsg->m_cbDataBlock);
1527 pMsg = pMsg->m_pNext;
1530 // Check none of the sends failed.
1531 if (m_eState != SS_Opening)
1533 m_pipe.Disconnect();
1536 } // Leave m_sStateLock
1538 // Finally we can transition to SS_Open.
1540 TransportLockHolder sLockHolder(&m_sStateLock);
1541 if (m_eState == SS_Closed)
1543 else if (m_eState == SS_Opening)
1546 _ASSERTE(!"Bad session state");
1547 } // Leave m_sStateLock
1549 #ifdef RIGHT_SIDE_COMPILE
1550 // Signal any WaitForSessionToOpen() waiters that we've gotten to SS_Open.
1551 SetEvent(m_hSessionOpenEvent);
1552 #endif // RIGHT_SIDE_COMPILE
1554 // We're ready to begin receiving normal incoming messages now.
1558 // The SS_Resync case. Send a message indicating the last message we saw from the other side and
1559 // wait for a similar message to arrive for us.
1561 sSendHeader.m_eType = MT_SessionResync;
1562 sSendHeader.m_dwLastSeenId = m_dwLastMessageIdSeen;
1564 DbgTransportLog(LC_Session, "Sending 'SessionResync'");
1565 DBG_TRANSPORT_INC_STAT(SentSessionResync);
1567 if (!SendBlock((PBYTE)&sSendHeader, sizeof(MessageHeader)))
1568 HANDLE_TRANSIENT_ERROR();
1570 if (!ReceiveBlock((PBYTE)&sReceiveHeader, sizeof(MessageHeader)))
1571 HANDLE_TRANSIENT_ERROR();
1573 #ifndef RIGHT_SIDE_COMPILE
1574 if (sReceiveHeader.m_eType == MT_SessionRequest)
1576 DbgTransportLogMessageReceived(&sReceiveHeader);
1578 // This SessionRequest could be from a different debugger. In this case we should send a
1579 // SessionReject to let them know we're not available and close the connection so we can
1580 // re-listen for the original debugger.
1581 // Or it could be the original debugger re-sending the SessionRequest because the connection
1582 // died as we sent the SessionAccept.
1583 // We distinguish the two cases by looking at the session ID in the request.
1584 bool fRequestResend = false;
1586 // Only read the data block if it matches our expectations of its size.
1587 if (sReceiveHeader.m_cbDataBlock == (DWORD)sizeof(SessionRequestData))
1589 SessionRequestData sDataBlock;
1590 if (!ReceiveBlock((PBYTE)&sDataBlock, sizeof(SessionRequestData)))
1591 HANDLE_TRANSIENT_ERROR();
1593 // Check the session ID for a match.
1594 if (memcmp(&sDataBlock.m_sSessionID, &m_sSessionID, sizeof(m_sSessionID)) == 0)
1595 // OK, everything checks out and this is a valid re-send of a SessionRequest.
1596 fRequestResend = true;
1601 // The RS never got our SessionAccept. We must resend it.
1602 memset(&sSendHeader, 0, sizeof(MessageHeader));
1603 sSendHeader.m_eType = MT_SessionAccept;
1604 sSendHeader.m_cbDataBlock = 0;
1605 sSendHeader.TypeSpecificData.VersionInfo.m_dwMajorVersion = kCurrentMajorVersion;
1606 sSendHeader.TypeSpecificData.VersionInfo.m_dwMinorVersion = m_dwMinorVersion;
1608 DbgTransportLog(LC_Session, "Sending 'SessionAccept'");
1609 DBG_TRANSPORT_INC_STAT(SentSessionAccept);
1611 if (!SendBlock((PBYTE)&sSendHeader, sizeof(MessageHeader)))
1612 HANDLE_TRANSIENT_ERROR();
1614 // Now simply reset the connection. The RS should get the SessionAccept and transition to
1615 // SS_Open then detect the connection loss and transition to SS_Resync_NC, which will
1616 // finally sync the two sides.
1617 HANDLE_TRANSIENT_ERROR();
1621 // This is the case where we must reject the request.
1622 memset(&sSendHeader, 0, sizeof(MessageHeader));
1623 sSendHeader.m_eType = MT_SessionReject;
1624 sSendHeader.TypeSpecificData.SessionReject.m_eReason = RR_AlreadyAttached;
1625 sSendHeader.TypeSpecificData.SessionReject.m_dwMajorVersion = kCurrentMajorVersion;
1626 sSendHeader.TypeSpecificData.SessionReject.m_dwMinorVersion = kCurrentMinorVersion;
1628 DbgTransportLog(LC_Session, "Sending 'SessionReject(RR_AlreadyAttached)'");
1629 DBG_TRANSPORT_INC_STAT(SentSessionReject);
1631 SendBlock((PBYTE)&sSendHeader, sizeof(MessageHeader));
1633 HANDLE_TRANSIENT_ERROR();
1636 #endif // !RIGHT_SIDE_COMPILE
1638 DbgTransportLogMessageReceived(&sReceiveHeader);
1640 // Handle all other invalid message types by shutting down (it may be an attempt to subvert the
1642 if (sReceiveHeader.m_eType != MT_SessionResync)
1644 _ASSERTE(!"Unexpected message type during SS_Resync");
1645 HANDLE_CRITICAL_ERROR();
1648 // We've got our resync message. Go through the send queue and resend any messages that haven't
1649 // been processed by the other side. Those that have been processed can be discarded (unless
1650 // they're waiting for another form of higher level acknowledgement, such as a reply message).
1652 // Discard unneeded messages first.
1653 FlushSendQueue(sReceiveHeader.m_dwLastSeenId);
1655 // Must access the send queue under the state lock.
1657 TransportLockHolder sLockHolder(&m_sStateLock);
1659 Message *pMsg = m_pSendQueueFirst;
1662 if (pMsg->m_sHeader.m_dwId > sReceiveHeader.m_dwLastSeenId)
1664 // The other side never saw this message, re-send it.
1665 DBG_TRANSPORT_INC_STAT(Resends);
1666 if (SendBlock((PBYTE)&pMsg->m_sHeader, sizeof(MessageHeader)) && pMsg->m_pbDataBlock)
1667 SendBlock(pMsg->m_pbDataBlock, pMsg->m_cbDataBlock);
1669 pMsg = pMsg->m_pNext;
1672 // Finished processing queued sends. We can transition to the SS_Open state now as long as there
1673 // wasn't a send failure or an asynchronous Shutdown().
1674 if (m_eState == SS_Resync)
1676 else if (m_eState == SS_Closed)
1678 else if (m_eState == SS_Resync_NC)
1680 m_pipe.Disconnect();
1684 _ASSERTE(!"Bad session state");
1685 } // Leave m_sStateLock
1688 // Once we get here we should be in SS_Open (can't assert this because Shutdown() can throw the state
1689 // into SS_Closed and we've just released SendMessage() calls on other threads that can transition us
1692 // We now loop receiving messages and processing them until the state changes.
1693 while (m_eState == SS_Open)
1695 // temporary data block used in DCB messages
1696 DebuggerIPCControlBlockTransport dcbt;
1698 // temporary virtual stack unwind context buffer
1699 CONTEXT frameContext;
1701 // Read a message header block.
1702 if (!ReceiveBlock((PBYTE)&sReceiveHeader, sizeof(MessageHeader)))
1703 HANDLE_TRANSIENT_ERROR();
1705 // Since we care about security here, perform some additional validation checks that make it
1706 // harder for a malicious sender to attack with random message data.
1707 if (sReceiveHeader.m_eType > MT_GetAppDomainCB ||
1708 (sReceiveHeader.m_dwId <= m_dwLastMessageIdSeen &&
1709 sReceiveHeader.m_dwId != (DWORD)0) ||
1710 (sReceiveHeader.m_dwReplyId >= m_dwNextMessageId &&
1711 sReceiveHeader.m_dwReplyId != (DWORD)0) ||
1712 (sReceiveHeader.m_dwLastSeenId >= m_dwNextMessageId &&
1713 sReceiveHeader.m_dwLastSeenId != (DWORD)0))
1715 _ASSERTE(!"Incoming message header looks bogus");
1716 HANDLE_CRITICAL_ERROR();
1719 DbgTransportLogMessageReceived(&sReceiveHeader);
1721 // Flush any entries in our send queue for messages that the other side has just confirmed
1722 // processed with this message.
1723 FlushSendQueue(sReceiveHeader.m_dwLastSeenId);
1725 #ifndef RIGHT_SIDE_COMPILE
1726 // State variables to track whether this message needs a reply and if so whether it consists of a
1727 // header only or a header and an optional data block.
1728 bool fReplyRequired = false;
1729 PBYTE pbOptReplyData = NULL;
1730 DWORD cbOptReplyData = 0;
1731 HRESULT hr = E_FAIL;
1733 // if you change the lifetime of resultBuffer, make sure you change pbOptReplyData to match.
1734 // In some cases pbOptReplyData will point at the memory held alive in resultBuffer
1735 WriteBuffer resultBuffer;
1736 ReadBuffer receiveBuffer;
1738 #endif // RIGHT_SIDE_COMPILE
1740 // Dispatch based on message type.
1742 // **** IMPORTANT NOTE ****
1744 // We must be very careful wrt to updating m_dwLastMessageIdSeen here. If we update it too soon
1745 // (we haven't finished receiving the entire message, for instance) then the other side won't
1746 // re-send the message on failure and we'll lose it. If we update it too late we might have
1747 // reported the message to our caller or produced any other side-effect we can't take back such as
1748 // sending a reply and then hit an error and reset the connection before we had a chance to record
1749 // the message as seen. In this case the other side will re-send the original message and we'll
1750 // repeat our actions, which is also very bad.
1752 // So we must be very disciplined here.
1754 // First we must read the message in its entirety (i.e. receive the data block if there is one)
1755 // without causing any side-effects. This ensures that any failure at this point will be handled
1756 // correctly (by the other side re-sending us the same message).
1758 // Then we process the message. At this point we are committed. The processing must always
1759 // succeed, or have no side-effect (that we care about) or we must have an additional scheme to
1760 // handle resynchronization in the event of failure. This ensures that we don't have the tricky
1761 // situation where we can't cope with a re-send of the message (because we've started processing
1762 // it) but can't report a failure to the other side (because we don't know how).
1764 // Finally we must ensure that there is no error path between the completion of processing and
1765 // updating the m_dwLastMessageIdSeen field. This ensures we don't accidently get re-sent a
1766 // message we've processed completely (it's really just a sub-case of the rule above, but it's
1767 // worth pointing out explicitly since it can be a subtle problem).
1769 // Request messages (such as MT_GetDCB) are an interesting case in point here. They all require a
1770 // reply and we can fail on the reply because we run out of system resources. This breaks the
1771 // second rule above (we fail halfway through processing). We should really preallocate enough
1772 // resources to send the reply before we begin processing of it but for now we don't since (a) the
1773 // SendMessage system isn't currently set up to make this easy and (b) we happen to know that all
1774 // the request types are effectively idempotent (even ReadMemory and WriteMemory since the RS is
1775 // holding the LS still while it does these). So instead we must carefully distinguish the case
1776 // where SendMessage fails without possibility of message transmission (e.g. out of memory) and
1777 // those where it fails for a transient network failure (where it will re-send the reply on
1778 // resync). This is easy enough to do since SendMessage returns a failure hresult for the first
1779 // case and success (and a state transition) for the second. In the first case we don't update
1780 // m_dwLastMessageIdSeen and instead wait for the request to be resent. In the second we make the
1781 // update because we know the reply will get through eventually.
1783 // **** IMPORTANT NOTE ****
1784 switch (sReceiveHeader.m_eType)
1786 case MT_SessionRequest:
1787 case MT_SessionAccept:
1788 case MT_SessionReject:
1789 case MT_SessionResync:
1790 // Illegal messages at this time, fail the transport entirely.
1791 m_eState = SS_Closed;
1794 case MT_SessionClose:
1795 // Close is legal on the LS and transitions to the SS_Opening_NC state. It's illegal on the RS
1796 // and should shutdown the transport.
1797 #ifdef RIGHT_SIDE_COMPILE
1798 m_eState = SS_Closed;
1800 #else // RIGHT_SIDE_COMPILE
1801 // We need to do some state cleanup here, since when we reform a connection (if ever, it will
1802 // be with a new session).
1804 TransportLockHolder sLockHolder(&m_sStateLock);
1806 // Check we're still in a good state before a clean restart.
1807 if (m_eState != SS_Open)
1809 m_eState = SS_Closed;
1813 m_pipe.Disconnect();
1815 // We could add code to drain the send queue here (like we have for SS_Closed at the end of
1816 // this method) but I'm pretty sure we can only get a graceful session close with no
1817 // outstanding sends. So just assert the queue is empty instead. If the assert fires and it's
1818 // not due to an issue we can add the logic here).
1819 _ASSERTE(m_pSendQueueFirst == NULL);
1820 _ASSERTE(m_pSendQueueLast == NULL);
1822 // This will reset all session specific state and transition us to SS_Opening_NC.
1824 } // Leave m_sStateLock
1826 goto ResetConnection;
1827 #endif // RIGHT_SIDE_COMPILE
1831 // Incoming debugger event.
1833 if (sReceiveHeader.m_cbDataBlock > CorDBIPC_BUFFER_SIZE)
1835 _ASSERTE(!"Oversized Event");
1836 HANDLE_CRITICAL_ERROR();
1839 // See if our array of buffered events has filled up. If so we'll need to re-allocate the
1840 // array to expand it.
1841 if (m_cValidEventBuffers == m_cEventBuffers)
1843 // Allocate a larger array.
1844 DWORD cNewEntries = m_cEventBuffers + 4;
1845 DbgEventBufferEntry * pNewBuffers = (DbgEventBufferEntry *)new (nothrow) BYTE[cNewEntries * sizeof(DbgEventBufferEntry)];
1846 if (pNewBuffers == NULL)
1847 HANDLE_TRANSIENT_ERROR();
1849 // We must take the lock to swap the new array in. Although this thread is the only one
1850 // that can expand the array, a client thread may be in GetNextEvent() reading from the
1853 TransportLockHolder sLockHolder(&m_sStateLock);
1855 // When we copy old array contents over we place the head of the list at the start of
1856 // the new array for simplicity. If the head happened to be at the start of the old
1857 // array anyway, this is even simpler.
1858 if (m_idxEventBufferHead == 0)
1859 memcpy(pNewBuffers, m_pEventBuffers, m_cEventBuffers * sizeof(DbgEventBufferEntry));
1862 // Otherwise we need to perform the copy in two segments: first we copy the head
1863 // of the list (starts at a non-zero index and runs to the end of the old array)
1864 // into the start of the new array.
1865 DWORD cHeadEntries = m_cEventBuffers - m_idxEventBufferHead;
1868 &m_pEventBuffers[m_idxEventBufferHead],
1869 cHeadEntries * sizeof(DbgEventBufferEntry));
1871 // Then we copy the remaining portion from the beginning of the old array upto to
1872 // the index of the head.
1873 memcpy(&pNewBuffers[cHeadEntries],
1875 m_idxEventBufferHead * sizeof(DbgEventBufferEntry));
1878 // Delete the old array.
1879 delete [] m_pEventBuffers;
1881 // Swap the new array in.
1882 m_pEventBuffers = pNewBuffers;
1883 m_cEventBuffers = cNewEntries;
1885 // The new array now has the head at index zero and the tail at the start of the
1887 m_idxEventBufferHead = 0;
1888 m_idxEventBufferTail = m_cValidEventBuffers;
1892 // We have at least one free buffer at this point (no threading issues, the only thread that
1893 // can add entries is this one).
1895 // Receive event data into the tail buffer (we want to do this without holding the state lock
1896 // and can do so safely since this is the only thread that can receive data and clients can do
1897 // nothing that impacts the location of the tail of the buffer list).
1898 if (!ReceiveBlock((PBYTE)&m_pEventBuffers[m_idxEventBufferTail].m_event, sReceiveHeader.m_cbDataBlock))
1899 HANDLE_TRANSIENT_ERROR();
1902 m_pEventBuffers[m_idxEventBufferTail].m_type = sReceiveHeader.TypeSpecificData.Event.m_eIPCEventType;
1904 // We must take the lock to update the count of valid entries though, since clients can
1905 // touch this field as well.
1906 TransportLockHolder sLockHolder(&m_sStateLock);
1908 m_cValidEventBuffers++;
1909 DWORD idxCurrentEvent = m_idxEventBufferTail;
1911 // Update tail of the list (strictly speaking this needn't be done under the lock, but the
1912 // code in GetNextEvent() does read it for an assert.
1913 m_idxEventBufferTail = (m_idxEventBufferTail + 1) % m_cEventBuffers;
1915 // If we just added the first valid event then wake up the client so they can call
1917 if (m_cValidEventBuffers == 1)
1918 SetEvent(m_rghEventReadyEvent[m_pEventBuffers[idxCurrentEvent].m_type]);
1924 #ifdef RIGHT_SIDE_COMPILE
1925 if (!ProcessReply(&sReceiveHeader))
1926 HANDLE_TRANSIENT_ERROR();
1927 #else // RIGHT_SIDE_COMPILE
1928 // The RS wants to read our memory. First check the range requested is both committed and
1929 // readable. If that succeeds we simply set the optional reply block to match the request region
1930 // (i.e. we send the memory directly).
1931 fReplyRequired = true;
1933 hr = CheckBufferAccess(sReceiveHeader.TypeSpecificData.MemoryAccess.m_pbLeftSideBuffer,
1934 sReceiveHeader.TypeSpecificData.MemoryAccess.m_cbLeftSideBuffer,
1936 sReceiveHeader.TypeSpecificData.MemoryAccess.m_hrResult = hr;
1939 pbOptReplyData = sReceiveHeader.TypeSpecificData.MemoryAccess.m_pbLeftSideBuffer;
1940 cbOptReplyData = sReceiveHeader.TypeSpecificData.MemoryAccess.m_cbLeftSideBuffer;
1942 #endif // RIGHT_SIDE_COMPILE
1945 case MT_WriteMemory:
1946 #ifdef RIGHT_SIDE_COMPILE
1947 if (!ProcessReply(&sReceiveHeader))
1948 HANDLE_TRANSIENT_ERROR();
1949 #else // RIGHT_SIDE_COMPILE
1950 // The RS wants to write our memory.
1951 if (sReceiveHeader.m_cbDataBlock != sReceiveHeader.TypeSpecificData.MemoryAccess.m_cbLeftSideBuffer)
1953 _ASSERTE(!"Inconsistent WriteMemory request");
1954 HANDLE_CRITICAL_ERROR();
1957 fReplyRequired = true;
1959 // Check the range requested is both committed and writeable. If that succeeds we simply read
1960 // the next incoming block into the destination buffer.
1961 hr = CheckBufferAccess(sReceiveHeader.TypeSpecificData.MemoryAccess.m_pbLeftSideBuffer,
1962 sReceiveHeader.TypeSpecificData.MemoryAccess.m_cbLeftSideBuffer,
1966 if (!ReceiveBlock(sReceiveHeader.TypeSpecificData.MemoryAccess.m_pbLeftSideBuffer,
1967 sReceiveHeader.TypeSpecificData.MemoryAccess.m_cbLeftSideBuffer))
1968 HANDLE_TRANSIENT_ERROR();
1972 sReceiveHeader.TypeSpecificData.MemoryAccess.m_hrResult = hr;
1974 // We might be failing the write attempt but we still need to read the update data to
1975 // drain it from the connection or we'll become unsynchronized (i.e. we'll treat the start
1976 // of the write data as the next message header). So read and discard the data into a
1979 DWORD cbBytesToRead = sReceiveHeader.TypeSpecificData.MemoryAccess.m_cbLeftSideBuffer;
1980 while (cbBytesToRead)
1982 DWORD cbTransfer = min(cbBytesToRead, sizeof(rgDummy));
1983 if (!ReceiveBlock(rgDummy, cbTransfer))
1984 HANDLE_TRANSIENT_ERROR();
1985 cbBytesToRead -= cbTransfer;
1988 #endif // RIGHT_SIDE_COMPILE
1991 case MT_VirtualUnwind:
1992 #ifdef RIGHT_SIDE_COMPILE
1993 if (!ProcessReply(&sReceiveHeader))
1994 HANDLE_TRANSIENT_ERROR();
1995 #else // RIGHT_SIDE_COMPILE
1996 if (sReceiveHeader.m_cbDataBlock != (DWORD)sizeof(frameContext))
1998 _ASSERTE(!"Inconsistent VirtualUnwind request");
1999 HANDLE_CRITICAL_ERROR();
2002 if (!ReceiveBlock((PBYTE)&frameContext, sizeof(frameContext)))
2004 HANDLE_TRANSIENT_ERROR();
2007 if (!PAL_VirtualUnwind(&frameContext, NULL))
2009 HANDLE_TRANSIENT_ERROR();
2012 fReplyRequired = true;
2013 pbOptReplyData = (PBYTE)&frameContext;
2014 cbOptReplyData = sizeof(frameContext);
2015 #endif // RIGHT_SIDE_COMPILE
2019 #ifdef RIGHT_SIDE_COMPILE
2020 if (!ProcessReply(&sReceiveHeader))
2021 HANDLE_TRANSIENT_ERROR();
2022 #else // RIGHT_SIDE_COMPILE
2023 fReplyRequired = true;
2024 MarshalDCBToDCBTransport(m_pDCB, &dcbt);
2025 pbOptReplyData = (PBYTE)&dcbt;
2026 cbOptReplyData = sizeof(DebuggerIPCControlBlockTransport);
2027 #endif // RIGHT_SIDE_COMPILE
2031 #ifdef RIGHT_SIDE_COMPILE
2032 if (!ProcessReply(&sReceiveHeader))
2033 HANDLE_TRANSIENT_ERROR();
2034 #else // RIGHT_SIDE_COMPILE
2035 if (sReceiveHeader.m_cbDataBlock != (DWORD)sizeof(DebuggerIPCControlBlockTransport))
2037 _ASSERTE(!"Inconsistent SetDCB request");
2038 HANDLE_CRITICAL_ERROR();
2041 fReplyRequired = true;
2043 if (!ReceiveBlock((PBYTE)&dcbt, sizeof(DebuggerIPCControlBlockTransport)))
2044 HANDLE_TRANSIENT_ERROR();
2046 MarshalDCBTransportToDCB(&dcbt, m_pDCB);
2047 #endif // RIGHT_SIDE_COMPILE
2050 case MT_GetAppDomainCB:
2051 #ifdef RIGHT_SIDE_COMPILE
2052 if (!ProcessReply(&sReceiveHeader))
2053 HANDLE_TRANSIENT_ERROR();
2054 #else // RIGHT_SIDE_COMPILE
2055 fReplyRequired = true;
2056 pbOptReplyData = (PBYTE)m_pADB;
2057 cbOptReplyData = sizeof(AppDomainEnumerationIPCBlock);
2058 #endif // RIGHT_SIDE_COMPILE
2062 _ASSERTE(!"Unknown message type");
2063 HANDLE_CRITICAL_ERROR();
2066 #ifndef RIGHT_SIDE_COMPILE
2067 // On the left side we may need to send a reply back.
2071 sReply.Init(sReceiveHeader.m_eType, pbOptReplyData, cbOptReplyData);
2072 sReply.m_sHeader.m_dwReplyId = sReceiveHeader.m_dwId;
2073 sReply.m_sHeader.TypeSpecificData = sReceiveHeader.TypeSpecificData;
2076 DbgTransportLog(LC_Requests, "Sending '%s' reply", MessageName(sReceiveHeader.m_eType));
2079 // We must be careful with the failure mode of SendMessage here to avoid the same request
2080 // being processed too many or too few times. See the comment above starting with 'IMPORTANT
2081 // NOTE' for more details. The upshot is that on SendMessage hresult failures (which indicate
2082 // the message will never be sent), we don't update m_dwLastMessageIdSeen and simply wait for
2083 // the request to be made again. When we get success, however, we must be careful to ensure
2084 // that m_dwLastMessageIdSeen gets updated even if a network error is reported. Otherwise on
2085 // the resync we'll both reprocess the request and re-send the original reply which is very
2087 hr = SendMessage(&sReply, false);
2090 HANDLE_TRANSIENT_ERROR(); // Message will never be sent, other side will retry
2092 // SendMessage doesn't report network errors (it simply queues the send and changes the
2093 // session state). So check for a network error here specifically so we can get started on the
2094 // resync. We must update m_dwLastMessageIdSeen first though, or the other side will retry the
2096 if (m_eState != SS_Open)
2098 _ASSERTE(sReceiveHeader.m_dwId > m_dwLastMessageIdSeen);
2099 m_dwLastMessageIdSeen = sReceiveHeader.m_dwId;
2100 HANDLE_TRANSIENT_ERROR();
2103 #endif // !RIGHT_SIDE_COMPILE
2105 if (sReceiveHeader.m_dwId != (DWORD)0)
2107 // We've now completed processing on the incoming message. Remember we've processed up to this
2108 // message ID so that on a resync the other side doesn't send it to us again.
2109 _ASSERTE(sReceiveHeader.m_dwId > m_dwLastMessageIdSeen);
2110 m_dwLastMessageIdSeen = sReceiveHeader.m_dwId;
2117 _ASSERTE(m_eState == SS_Closed);
2119 #ifdef RIGHT_SIDE_COMPILE
2120 // The session is definitely not open at this point.
2121 ResetEvent(m_hSessionOpenEvent);
2122 #endif // RIGHT_SIDE_COMPILE
2124 // Close the connection if we haven't done so already.
2125 m_pipe.Disconnect();
2127 // Drain any remaining entries in the send queue (aborting them when they need completions).
2129 TransportLockHolder sLockHolder(&m_sStateLock);
2132 while ((pMsg = m_pSendQueueFirst) != NULL)
2134 // Remove message from the queue.
2135 m_pSendQueueFirst = pMsg->m_pNext;
2137 // Determine whether the message needs to be deleted by us before we signal any completion (because
2138 // once we signal the completion pMsg might become invalid immediately if it's not a copy).
2139 bool fMustDelete = pMsg->m_pOrigMessage != pMsg;
2141 // If there's a waiter (i.e. we don't own the message) it know that the operation didn't really
2142 // complete, it was aborted.
2144 pMsg->m_pOrigMessage->m_fAborted = true;
2146 // Determine how to complete the message.
2147 switch (pMsg->m_sHeader.m_eType)
2149 case MT_SessionRequest:
2150 case MT_SessionAccept:
2151 case MT_SessionReject:
2152 case MT_SessionResync:
2153 case MT_SessionClose:
2154 _ASSERTE(!"Session management messages should not be on send queue");
2160 #ifdef RIGHT_SIDE_COMPILE
2162 case MT_WriteMemory:
2163 case MT_VirtualUnwind:
2166 case MT_GetAppDomainCB:
2167 // On the RS these are the original requests. Signal the completion event.
2168 SignalReplyEvent(pMsg);
2170 #else // RIGHT_SIDE_COMPILE
2172 case MT_WriteMemory:
2173 case MT_VirtualUnwind:
2176 case MT_GetAppDomainCB:
2177 // On the LS these are replies to the original request. Nobody's waiting on these.
2179 #endif // RIGHT_SIDE_COMPILE
2182 _ASSERTE(!"Unknown message type");
2185 // If the message was a copy, deallocate the resources now.
2188 if (pMsg->m_pbDataBlock)
2189 delete [] pMsg->m_pbDataBlock;
2193 } // Leave m_sStateLock
2195 // Now release all the resources allocated for the transport now that the
2196 // worker thread isn't using them anymore.
2200 // Given a fully initialized debugger event structure, return the size of the structure in bytes (this is not
2201 // trivial since DebuggerIPCEvent contains a large union member which can cause the portion containing
2202 // significant data to vary wildy from event to event).
2203 DWORD DbgTransportSession::GetEventSize(DebuggerIPCEvent *pEvent)
2205 DWORD cbBaseSize = offsetof(DebuggerIPCEvent, LeftSideStartupData);
2206 DWORD cbAdditionalSize = 0;
2208 switch (pEvent->type & DB_IPCE_TYPE_MASK)
2210 case DB_IPCE_SYNC_COMPLETE:
2211 case DB_IPCE_THREAD_ATTACH:
2212 case DB_IPCE_THREAD_DETACH:
2213 case DB_IPCE_USER_BREAKPOINT:
2214 case DB_IPCE_EXIT_APP_DOMAIN:
2215 case DB_IPCE_SET_DEBUG_STATE_RESULT:
2216 case DB_IPCE_FUNC_EVAL_ABORT_RESULT:
2217 case DB_IPCE_CONTROL_C_EVENT:
2218 case DB_IPCE_FUNC_EVAL_CLEANUP_RESULT:
2219 case DB_IPCE_SET_METHOD_JMC_STATUS_RESULT:
2220 case DB_IPCE_SET_MODULE_JMC_STATUS_RESULT:
2221 case DB_IPCE_FUNC_EVAL_RUDE_ABORT_RESULT:
2222 case DB_IPCE_INTERCEPT_EXCEPTION_RESULT:
2223 case DB_IPCE_INTERCEPT_EXCEPTION_COMPLETE:
2224 case DB_IPCE_CREATE_PROCESS:
2225 case DB_IPCE_SET_NGEN_COMPILER_FLAGS_RESULT:
2226 case DB_IPCE_LEFTSIDE_STARTUP:
2227 case DB_IPCE_ASYNC_BREAK:
2228 case DB_IPCE_CONTINUE:
2229 case DB_IPCE_ATTACHING:
2230 case DB_IPCE_GET_NGEN_COMPILER_FLAGS:
2231 case DB_IPCE_DETACH_FROM_PROCESS:
2232 case DB_IPCE_CONTROL_C_EVENT_RESULT:
2233 cbAdditionalSize = 0;
2236 case DB_IPCE_BREAKPOINT:
2237 cbAdditionalSize = sizeof(pEvent->BreakpointData);
2240 case DB_IPCE_LOAD_MODULE:
2241 cbAdditionalSize = sizeof(pEvent->LoadModuleData);
2244 case DB_IPCE_UNLOAD_MODULE:
2245 cbAdditionalSize = sizeof(pEvent->UnloadModuleData);
2248 case DB_IPCE_LOAD_CLASS:
2249 cbAdditionalSize = sizeof(pEvent->LoadClass);
2252 case DB_IPCE_UNLOAD_CLASS:
2253 cbAdditionalSize = sizeof(pEvent->UnloadClass);
2256 case DB_IPCE_EXCEPTION:
2257 cbAdditionalSize = sizeof(pEvent->Exception);
2260 case DB_IPCE_BREAKPOINT_ADD_RESULT:
2261 cbAdditionalSize = sizeof(pEvent->BreakpointData);
2264 case DB_IPCE_STEP_RESULT:
2265 cbAdditionalSize = sizeof(pEvent->StepData);
2266 if (pEvent->StepData.rangeCount)
2267 cbAdditionalSize += (pEvent->StepData.rangeCount - 1) * sizeof(COR_DEBUG_STEP_RANGE);
2270 case DB_IPCE_STEP_COMPLETE:
2271 cbAdditionalSize = sizeof(pEvent->StepData);
2274 case DB_IPCE_GET_BUFFER_RESULT:
2275 cbAdditionalSize = sizeof(pEvent->GetBufferResult);
2278 case DB_IPCE_RELEASE_BUFFER_RESULT:
2279 cbAdditionalSize = sizeof(pEvent->ReleaseBufferResult);
2282 case DB_IPCE_ENC_ADD_FIELD:
2283 cbAdditionalSize = sizeof(pEvent->EnCUpdate);
2286 case DB_IPCE_APPLY_CHANGES_RESULT:
2287 cbAdditionalSize = sizeof(pEvent->ApplyChangesResult);
2290 case DB_IPCE_FIRST_LOG_MESSAGE:
2291 cbAdditionalSize = sizeof(pEvent->FirstLogMessage);
2294 case DB_IPCE_LOGSWITCH_SET_MESSAGE:
2295 cbAdditionalSize = sizeof(pEvent->LogSwitchSettingMessage);
2298 case DB_IPCE_CREATE_APP_DOMAIN:
2299 cbAdditionalSize = sizeof(pEvent->AppDomainData);
2302 case DB_IPCE_LOAD_ASSEMBLY:
2303 cbAdditionalSize = sizeof(pEvent->AssemblyData);
2306 case DB_IPCE_UNLOAD_ASSEMBLY:
2307 cbAdditionalSize = sizeof(pEvent->AssemblyData);
2310 case DB_IPCE_FUNC_EVAL_SETUP_RESULT:
2311 cbAdditionalSize = sizeof(pEvent->FuncEvalSetupComplete);
2314 case DB_IPCE_FUNC_EVAL_COMPLETE:
2315 cbAdditionalSize = sizeof(pEvent->FuncEvalComplete);
2318 case DB_IPCE_SET_REFERENCE_RESULT:
2319 cbAdditionalSize = sizeof(pEvent->SetReference);
2322 case DB_IPCE_NAME_CHANGE:
2323 cbAdditionalSize = sizeof(pEvent->NameChange);
2326 case DB_IPCE_UPDATE_MODULE_SYMS:
2327 cbAdditionalSize = sizeof(pEvent->UpdateModuleSymsData);
2330 case DB_IPCE_ENC_REMAP:
2331 cbAdditionalSize = sizeof(pEvent->EnCRemap);
2334 case DB_IPCE_SET_VALUE_CLASS_RESULT:
2335 cbAdditionalSize = sizeof(pEvent->SetValueClass);
2338 case DB_IPCE_BREAKPOINT_SET_ERROR:
2339 cbAdditionalSize = sizeof(pEvent->BreakpointSetErrorData);
2342 case DB_IPCE_ENC_UPDATE_FUNCTION:
2343 cbAdditionalSize = sizeof(pEvent->EnCUpdate);
2346 case DB_IPCE_GET_METHOD_JMC_STATUS_RESULT:
2347 cbAdditionalSize = sizeof(pEvent->SetJMCFunctionStatus);
2350 case DB_IPCE_GET_THREAD_FOR_TASKID_RESULT:
2351 cbAdditionalSize = sizeof(pEvent->GetThreadForTaskIdResult);
2354 case DB_IPCE_CREATE_CONNECTION:
2355 cbAdditionalSize = sizeof(pEvent->CreateConnection);
2358 case DB_IPCE_DESTROY_CONNECTION:
2359 cbAdditionalSize = sizeof(pEvent->ConnectionChange);
2362 case DB_IPCE_CHANGE_CONNECTION:
2363 cbAdditionalSize = sizeof(pEvent->ConnectionChange);
2366 case DB_IPCE_EXCEPTION_CALLBACK2:
2367 cbAdditionalSize = sizeof(pEvent->ExceptionCallback2);
2370 case DB_IPCE_EXCEPTION_UNWIND:
2371 cbAdditionalSize = sizeof(pEvent->ExceptionUnwind);
2374 case DB_IPCE_CREATE_HANDLE_RESULT:
2375 cbAdditionalSize = sizeof(pEvent->CreateHandleResult);
2378 case DB_IPCE_ENC_REMAP_COMPLETE:
2379 cbAdditionalSize = sizeof(pEvent->EnCRemapComplete);
2382 case DB_IPCE_ENC_ADD_FUNCTION:
2383 cbAdditionalSize = sizeof(pEvent->EnCUpdate);
2386 case DB_IPCE_GET_NGEN_COMPILER_FLAGS_RESULT:
2387 cbAdditionalSize = sizeof(pEvent->JitDebugInfo);
2390 case DB_IPCE_MDA_NOTIFICATION:
2391 cbAdditionalSize = sizeof(pEvent->MDANotification);
2394 case DB_IPCE_GET_GCHANDLE_INFO_RESULT:
2395 cbAdditionalSize = sizeof(pEvent->GetGCHandleInfoResult);
2398 case DB_IPCE_SET_IP:
2399 cbAdditionalSize = sizeof(pEvent->SetIP);
2402 case DB_IPCE_BREAKPOINT_ADD:
2403 cbAdditionalSize = sizeof(pEvent->BreakpointData);
2406 case DB_IPCE_BREAKPOINT_REMOVE:
2407 cbAdditionalSize = sizeof(pEvent->BreakpointData);
2410 case DB_IPCE_STEP_CANCEL:
2411 cbAdditionalSize = sizeof(pEvent->StepData);
2415 cbAdditionalSize = sizeof(pEvent->StepData);
2416 if (pEvent->StepData.rangeCount)
2417 cbAdditionalSize += (pEvent->StepData.rangeCount - 1) * sizeof(COR_DEBUG_STEP_RANGE);
2420 case DB_IPCE_STEP_OUT:
2421 cbAdditionalSize = sizeof(pEvent->StepData);
2424 case DB_IPCE_GET_BUFFER:
2425 cbAdditionalSize = sizeof(pEvent->GetBuffer);
2428 case DB_IPCE_RELEASE_BUFFER:
2429 cbAdditionalSize = sizeof(pEvent->ReleaseBuffer);
2432 case DB_IPCE_SET_CLASS_LOAD_FLAG:
2433 cbAdditionalSize = sizeof(pEvent->SetClassLoad);
2436 case DB_IPCE_APPLY_CHANGES:
2437 cbAdditionalSize = sizeof(pEvent->ApplyChanges);
2440 case DB_IPCE_SET_NGEN_COMPILER_FLAGS:
2441 cbAdditionalSize = sizeof(pEvent->JitDebugInfo);
2444 case DB_IPCE_IS_TRANSITION_STUB:
2445 cbAdditionalSize = sizeof(pEvent->IsTransitionStub);
2448 case DB_IPCE_IS_TRANSITION_STUB_RESULT:
2449 cbAdditionalSize = sizeof(pEvent->IsTransitionStubResult);
2452 case DB_IPCE_MODIFY_LOGSWITCH:
2453 cbAdditionalSize = sizeof(pEvent->LogSwitchSettingMessage);
2456 case DB_IPCE_ENABLE_LOG_MESSAGES:
2457 cbAdditionalSize = sizeof(pEvent->LogSwitchSettingMessage);
2460 case DB_IPCE_FUNC_EVAL:
2461 cbAdditionalSize = sizeof(pEvent->FuncEval);
2464 case DB_IPCE_SET_REFERENCE:
2465 cbAdditionalSize = sizeof(pEvent->SetReference);
2468 case DB_IPCE_FUNC_EVAL_ABORT:
2469 cbAdditionalSize = sizeof(pEvent->FuncEvalAbort);
2472 case DB_IPCE_FUNC_EVAL_CLEANUP:
2473 cbAdditionalSize = sizeof(pEvent->FuncEvalCleanup);
2476 case DB_IPCE_SET_ALL_DEBUG_STATE:
2477 cbAdditionalSize = sizeof(pEvent->SetAllDebugState);
2480 case DB_IPCE_SET_VALUE_CLASS:
2481 cbAdditionalSize = sizeof(pEvent->SetValueClass);
2484 case DB_IPCE_SET_METHOD_JMC_STATUS:
2485 cbAdditionalSize = sizeof(pEvent->SetJMCFunctionStatus);
2488 case DB_IPCE_GET_METHOD_JMC_STATUS:
2489 cbAdditionalSize = sizeof(pEvent->SetJMCFunctionStatus);
2492 case DB_IPCE_SET_MODULE_JMC_STATUS:
2493 cbAdditionalSize = sizeof(pEvent->SetJMCFunctionStatus);
2496 case DB_IPCE_GET_THREAD_FOR_TASKID:
2497 cbAdditionalSize = sizeof(pEvent->GetThreadForTaskId);
2500 case DB_IPCE_FUNC_EVAL_RUDE_ABORT:
2501 cbAdditionalSize = sizeof(pEvent->FuncEvalRudeAbort);
2504 case DB_IPCE_CREATE_HANDLE:
2505 cbAdditionalSize = sizeof(pEvent->CreateHandle);
2508 case DB_IPCE_DISPOSE_HANDLE:
2509 cbAdditionalSize = sizeof(pEvent->DisposeHandle);
2512 case DB_IPCE_INTERCEPT_EXCEPTION:
2513 cbAdditionalSize = sizeof(pEvent->InterceptException);
2516 case DB_IPCE_GET_GCHANDLE_INFO:
2517 cbAdditionalSize = sizeof(pEvent->GetGCHandleInfo);
2520 case DB_IPCE_CUSTOM_NOTIFICATION:
2521 cbAdditionalSize = sizeof(pEvent->CustomNotification);
2525 printf("Unknown debugger event type: 0x%x\n", (pEvent->type & DB_IPCE_TYPE_MASK));
2526 _ASSERTE(!"Unknown debugger event type");
2529 return cbBaseSize + cbAdditionalSize;
2532 #pragma warning(pop)
2536 // Debug helper which returns the name associated with a MessageType.
2537 const char *DbgTransportSession::MessageName(MessageType eType)
2541 case MT_SessionRequest:
2542 return "SessionRequest";
2543 case MT_SessionAccept:
2544 return "SessionAccept";
2545 case MT_SessionReject:
2546 return "SessionReject";
2547 case MT_SessionResync:
2548 return "SessionResync";
2549 case MT_SessionClose:
2550 return "SessionClose";
2554 return "ReadMemory";
2555 case MT_WriteMemory:
2556 return "WriteMemory";
2557 case MT_VirtualUnwind:
2558 return "VirtualUnwind";
2563 case MT_GetAppDomainCB:
2564 return "GetAppDomainCB";
2566 _ASSERTE(!"Unknown message type");
2571 // Debug logging helper which logs an incoming message of any type (as long as logging for that message
2572 // class is currently enabled).
2573 void DbgTransportSession::DbgTransportLogMessageReceived(MessageHeader *pHeader)
2575 switch (pHeader->m_eType)
2577 case MT_SessionRequest:
2578 DbgTransportLog(LC_Session, "Received 'SessionRequest'");
2579 DBG_TRANSPORT_INC_STAT(ReceivedSessionRequest);
2581 case MT_SessionAccept:
2582 DbgTransportLog(LC_Session, "Received 'SessionAccept'");
2583 DBG_TRANSPORT_INC_STAT(ReceivedSessionAccept);
2585 case MT_SessionReject:
2586 DbgTransportLog(LC_Session, "Received 'SessionReject'");
2587 DBG_TRANSPORT_INC_STAT(ReceivedSessionReject);
2589 case MT_SessionResync:
2590 DbgTransportLog(LC_Session, "Received 'SessionResync'");
2591 DBG_TRANSPORT_INC_STAT(ReceivedSessionResync);
2593 case MT_SessionClose:
2594 DbgTransportLog(LC_Session, "Received 'SessionClose'");
2595 DBG_TRANSPORT_INC_STAT(ReceivedSessionClose);
2598 DbgTransportLog(LC_Events, "Received '%s'",
2599 IPCENames::GetName((DebuggerIPCEventType)(DWORD)pHeader->TypeSpecificData.Event.m_eType));
2600 DBG_TRANSPORT_INC_STAT(ReceivedEvent);
2602 #ifdef RIGHT_SIDE_COMPILE
2604 DbgTransportLog(LC_Requests, "Received 'ReadMemory(0x%08X, %u)' reply",
2605 (PBYTE)pHeader->TypeSpecificData.MemoryAccess.m_pbLeftSideBuffer,
2606 (DWORD)pHeader->TypeSpecificData.MemoryAccess.m_cbLeftSideBuffer);
2607 DBG_TRANSPORT_INC_STAT(ReceivedReadMemory);
2609 case MT_WriteMemory:
2610 DbgTransportLog(LC_Requests, "Received 'WriteMemory(0x%08X, %u)' reply",
2611 (PBYTE)pHeader->TypeSpecificData.MemoryAccess.m_pbLeftSideBuffer,
2612 (DWORD)pHeader->TypeSpecificData.MemoryAccess.m_cbLeftSideBuffer);
2613 DBG_TRANSPORT_INC_STAT(ReceivedWriteMemory);
2615 case MT_VirtualUnwind:
2616 DbgTransportLog(LC_Requests, "Received 'VirtualUnwind' reply");
2617 DBG_TRANSPORT_INC_STAT(ReceivedVirtualUnwind);
2620 DbgTransportLog(LC_Requests, "Received 'GetDCB' reply");
2621 DBG_TRANSPORT_INC_STAT(ReceivedGetDCB);
2624 DbgTransportLog(LC_Requests, "Received 'SetDCB' reply");
2625 DBG_TRANSPORT_INC_STAT(ReceivedSetDCB);
2627 case MT_GetAppDomainCB:
2628 DbgTransportLog(LC_Requests, "Received 'GetAppDomainCB' reply");
2629 DBG_TRANSPORT_INC_STAT(ReceivedGetAppDomainCB);
2631 #else // RIGHT_SIDE_COMPILE
2633 DbgTransportLog(LC_Requests, "Received 'ReadMemory(0x%08X, %u)'",
2634 (PBYTE)pHeader->TypeSpecificData.MemoryAccess.m_pbLeftSideBuffer,
2635 (DWORD)pHeader->TypeSpecificData.MemoryAccess.m_cbLeftSideBuffer);
2636 DBG_TRANSPORT_INC_STAT(ReceivedReadMemory);
2638 case MT_WriteMemory:
2639 DbgTransportLog(LC_Requests, "Received 'WriteMemory(0x%08X, %u)'",
2640 (PBYTE)pHeader->TypeSpecificData.MemoryAccess.m_pbLeftSideBuffer,
2641 (DWORD)pHeader->TypeSpecificData.MemoryAccess.m_cbLeftSideBuffer);
2642 DBG_TRANSPORT_INC_STAT(ReceivedWriteMemory);
2644 case MT_VirtualUnwind:
2645 DbgTransportLog(LC_Requests, "Received 'VirtualUnwind'");
2646 DBG_TRANSPORT_INC_STAT(ReceivedVirtualUnwind);
2649 DbgTransportLog(LC_Requests, "Received 'GetDCB'");
2650 DBG_TRANSPORT_INC_STAT(ReceivedGetDCB);
2653 DbgTransportLog(LC_Requests, "Received 'SetDCB'");
2654 DBG_TRANSPORT_INC_STAT(ReceivedSetDCB);
2656 case MT_GetAppDomainCB:
2657 DbgTransportLog(LC_Requests, "Received 'GetAppDomainCB'");
2658 DBG_TRANSPORT_INC_STAT(ReceivedGetAppDomainCB);
2660 #endif // RIGHT_SIDE_COMPILE
2662 _ASSERTE(!"Unknown message type");
2667 static CLRRandom s_faultInjectionRandom;
2669 // Helper method used by the DBG_TRANSPORT_SHOULD_INJECT_FAULT macro.
2670 bool DbgTransportSession::DbgTransportShouldInjectFault(DbgTransportFaultOp eOp, const char *szOpName)
2672 static DWORD s_dwFaultInjection = 0xffffffff;
2674 // Init the fault injection system if that hasn't already happened.
2675 if (s_dwFaultInjection == 0xffffffff)
2677 s_dwFaultInjection = CLRConfig::GetConfigValue(CLRConfig::INTERNAL_DbgTransportFaultInject);
2679 // Try for repeatable failures here by always initializing the random seed to a fixed value. But use
2680 // different seeds for the left and right sides or they'll end up in lock step. The
2681 // DBG_TRANSPORT_FAULT_THIS_SIDE macro is a convenient integer value that differs on each side.
2682 s_faultInjectionRandom.Init(DBG_TRANSPORT_FAULT_THIS_SIDE);
2684 // Clamp failure rate to a permissable value.
2685 if ((s_dwFaultInjection & DBG_TRANSPORT_FAULT_RATE_MASK) > 99)
2686 s_dwFaultInjection = (s_dwFaultInjection & ~DBG_TRANSPORT_FAULT_RATE_MASK) | 99;
2689 // Map current session state into the bitmask format used for fault injection control.
2695 dwState = FS_Opening;
2699 dwState = FS_Resync;
2707 _ASSERTE(!"Bad session state");
2710 if ((s_dwFaultInjection & DBG_TRANSPORT_FAULT_THIS_SIDE) &&
2711 (s_dwFaultInjection & eOp) &&
2712 (s_dwFaultInjection & dwState))
2714 // We're faulting this side, op and state. Roll the dice and see if this particular call should fail.
2715 DWORD dwChance = s_faultInjectionRandom.Next(100);
2716 if (dwChance < (s_dwFaultInjection & DBG_TRANSPORT_FAULT_RATE_MASK))
2718 DbgTransportLog(LC_FaultInject, "Injected fault for %s operation", szOpName);
2719 #if defined(FEATURE_CORESYSTEM)
2722 WSASetLastError(WSAEFAULT);
2723 #endif // defined(FEATURE_CORESYSTEM)
2732 // Lock abstraction code (hides difference in lock implementation between left and right side).
2733 #ifdef RIGHT_SIDE_COMPILE
2735 // On the right side we use a CRITICAL_SECTION.
2737 void DbgTransportLock::Init()
2739 InitializeCriticalSection(&m_sLock);
2742 void DbgTransportLock::Destroy()
2744 DeleteCriticalSection(&m_sLock);
2747 void DbgTransportLock::Enter()
2749 EnterCriticalSection(&m_sLock);
2752 void DbgTransportLock::Leave()
2754 LeaveCriticalSection(&m_sLock);
2756 #else // RIGHT_SIDE_COMPILE
2758 // On the left side we use a Crst.
2760 void DbgTransportLock::Init()
2762 m_sLock.Init(CrstDbgTransport, (CrstFlags)(CRST_UNSAFE_ANYMODE | CRST_DEBUGGER_THREAD | CRST_TAKEN_DURING_SHUTDOWN));
2765 void DbgTransportLock::Destroy()
2769 void DbgTransportLock::Enter()
2774 void DbgTransportLock::Leave()
2778 #endif // RIGHT_SIDE_COMPILE
2780 #endif // (!defined(RIGHT_SIDE_COMPILE) && defined(FEATURE_DBGIPC_TRANSPORT_VM)) || (defined(RIGHT_SIDE_COMPILE) && defined(FEATURE_DBGIPC_TRANSPORT_DI))