1// Licensed to the .NET Foundation under one or more agreements.
2// The .NET Foundation licenses this file to you under the MIT license.
3// See the LICENSE file in the project root for more information.
4
5
6#include "dbgtransportsession.h"
7
8#if (!defined(RIGHT_SIDE_COMPILE) && defined(FEATURE_DBGIPC_TRANSPORT_VM)) || (defined(RIGHT_SIDE_COMPILE) && defined(FEATURE_DBGIPC_TRANSPORT_DI))
9
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
12// IPC event itself.
13struct DbgEventBufferEntry
14{
15public:
16 IPCEventType m_type;
17 BYTE m_event[CorDBIPC_BUFFER_SIZE]; // buffer for the IPC event
18};
19
20//
21// Provides a robust and secure transport session between a debugger and a debuggee that are potentially on
22// different machines.
23//
24// See DbgTransportSession.h for further detailed comments.
25//
26
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).
30DbgTransportSession *g_pDbgTransport = NULL;
31
32#include "ddmarshalutil.h"
33#endif // !RIGHT_SIDE_COMPILE
34
35// No real work done in the constructor. Use Init() instead.
36DbgTransportSession::DbgTransportSession()
37{
38 m_ref = 1;
39 m_eState = SS_Closed;
40}
41
42DbgTransportSession::~DbgTransportSession()
43{
44 DbgTransportLog(LC_Proxy, "DbgTransportSession::~DbgTransportSession() called");
45
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]);
54 if (m_pEventBuffers)
55 delete [] m_pEventBuffers;
56
57#ifdef RIGHT_SIDE_COMPILE
58 if (m_hSessionOpenEvent)
59 CloseHandle(m_hSessionOpenEvent);
60
61 if (m_hProcessExited)
62 CloseHandle(m_hProcessExited);
63#endif // RIGHT_SIDE_COMPILE
64
65 if (m_fInitStateLock)
66 m_sStateLock.Destroy();
67}
68
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
76HRESULT DbgTransportSession::Init(const ProcessDescriptor& pd, HANDLE hProcessExited)
77#else // RIGHT_SIDE_COMPILE
78HRESULT DbgTransportSession::Init(DebuggerIPCControlBlock *pDCB, AppDomainEnumerationIPCBlock *pADB)
79#endif // RIGHT_SIDE_COMPILE
80{
81 _ASSERTE(m_eState == SS_Closed);
82
83 // Start with a blank slate so that Shutdown() on a partially initialized instance will only do the
84 // cleanup necessary.
85 memset(this, 0, sizeof(*this));
86
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.
89 m_ref = 1;
90 m_pipe = TwoWayPipe();
91 m_sStateLock = DbgTransportLock();
92
93 // Initialize all per-session state variables.
94 InitSessionState();
95
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);
101 if (FAILED(hr))
102 return hr;
103#endif // RIGHT_SIDE_COMPILE
104
105
106#ifdef RIGHT_SIDE_COMPILE
107 m_pd = pd;
108
109 if (!DuplicateHandle(GetCurrentProcess(),
110 hProcessExited,
111 GetCurrentProcess(),
112 &m_hProcessExited,
113 0, // ignored since we are going to pass DUPLICATE_SAME_ACCESS
114 FALSE,
115 DUPLICATE_SAME_ACCESS))
116 {
117 return HRESULT_FROM_GetLastError();
118 }
119
120 m_fDebuggerAttached = false;
121#else // RIGHT_SIDE_COMPILE
122 m_pDCB = pDCB;
123 m_pADB = pADB;
124#endif // RIGHT_SIDE_COMPILE
125
126 m_sStateLock.Init();
127 m_fInitStateLock = true;
128
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#else // RIGHT_SIDE_COMPILE
134 ProcessDescriptor pd = ProcessDescriptor::FromCurrentProcess();
135 if (!m_pipe.CreateServer(pd)) {
136 return E_OUTOFMEMORY;
137 }
138#endif // RIGHT_SIDE_COMPILE
139
140 // Allocate some buffers to receive incoming events. The initial number is chosen arbitrarily, tune as
141 // necessary. This array will need to grow if it fills with unread events (it takes our client a little
142 // time to process each incoming receive). In general, however, one side will not send an unbounded stream
143 // of events to the other without waiting for some kind of response. More usual are small bursts of events
144 // to represent variable sized data (such as a stack trace).
145 m_cEventBuffers = 10;
146 m_pEventBuffers = (DbgEventBufferEntry *)new (nothrow) BYTE[m_cEventBuffers * sizeof(DbgEventBufferEntry)];
147 if (m_pEventBuffers == NULL)
148 return E_OUTOFMEMORY;
149
150 m_rghEventReadyEvent[IPCET_OldStyle] = WszCreateEvent(NULL, FALSE, FALSE, NULL); // Auto reset, not signalled
151 if (m_rghEventReadyEvent[IPCET_OldStyle] == NULL)
152 return E_OUTOFMEMORY;
153
154 m_rghEventReadyEvent[IPCET_DebugEvent] = WszCreateEvent(NULL, FALSE, FALSE, NULL); // Auto reset, not signalled
155 if (m_rghEventReadyEvent[IPCET_DebugEvent] == NULL)
156 return E_OUTOFMEMORY;
157
158 // Start the transport thread which handles forming and re-forming connections, driving the session
159 // state to SS_Open and receiving and initially processing all incoming traffic.
160 AddRef();
161 m_hTransportThread = CreateThread(NULL, 0, TransportWorkerStatic, this, 0, NULL);
162 if (m_hTransportThread == NULL)
163 {
164 Release();
165 return E_OUTOFMEMORY;
166 }
167
168 return S_OK;
169}
170
171// Drive the session to the SS_Closed state, which will deallocate all remaining transport resources
172// (including terminating the transport thread). If this is the RS and the session state is SS_Open at the
173// time of this call a graceful disconnect will be attempted (which tells the LS to go back to SS_Opening to
174// look for a new RS rather than interpreting the disconnection as a temporary error and going into
175// SS_Resync). On either side the session will no longer be functional after this call returns (though Init()
176// may be called again to start over from the beginning).
177void DbgTransportSession::Shutdown()
178{
179 DbgTransportLog(LC_Proxy, "DbgTransportSession::Shutdown() called");
180
181 // The transport thread is allocated last in Init() (since it uses all the other resources that Init()
182 // prepares). Don't do any transport related stuff unless this was allocated (which can happen if
183 // Shutdown() is called after an Init() failure).
184
185 if (m_hTransportThread)
186 {
187 // From SS_Open state try a graceful disconnect.
188 if (m_eState == SS_Open)
189 {
190 DbgTransportLog(LC_Session, "Sending 'SessionClose'");
191 DBG_TRANSPORT_INC_STAT(SentSessionClose);
192 Message sMessage;
193 sMessage.Init(MT_SessionClose);
194 SendMessage(&sMessage, false);
195 }
196
197 // Must take the state lock to make a state transition.
198 {
199 TransportLockHolder sLockHolder(&m_sStateLock);
200
201 // Remember previous state and transition to SS_Closed.
202 SessionState ePreviousState = m_eState;
203 m_eState = SS_Closed;
204
205 if (ePreviousState != SS_Closed)
206 {
207 m_pipe.Disconnect();
208 }
209
210 } // Leave m_sStateLock
211
212#ifdef RIGHT_SIDE_COMPILE
213 // Signal the m_hSessionOpenEvent now to quickly error out any callers of WaitForSessionToOpen().
214 SetEvent(m_hSessionOpenEvent);
215#endif // RIGHT_SIDE_COMPILE
216 }
217
218 // The transport instance is no longer valid
219 Release();
220}
221
222#ifndef RIGHT_SIDE_COMPILE
223
224// Cleans up the named pipe connection so no tmp files are left behind. Does only
225// the minimum and must be safe to call at any time. Called during PAL ExitProcess,
226// TerminateProcess and for unhandled native exceptions and asserts.
227void DbgTransportSession::AbortConnection()
228{
229 m_pipe.Disconnect();
230}
231
232// API used only by the LS to drive the transport into a state where it won't accept connections. This is used
233// when no proxy is detected at startup but it's too late to shutdown all of the debugging system easily. It's
234// mainly paranoia to increase the protection of your system when the proxy isn't started.
235void DbgTransportSession::Neuter()
236{
237 // Simply set the session state to SS_Closed. The transport thread will switch itself off if it ever gets
238 // a connection but the rest of the transport resources remain valid (so the debugger helper thread won't
239 // AV on a deallocated handle, which might happen if we simply called Shutdown()).
240 m_eState = SS_Closed;
241}
242
243#else // RIGHT_SIDE_COMPILE
244
245// Used by debugger side (RS) to cleanup the target (LS) named pipes
246// and semaphores when the debugger detects the debuggee process exited.
247void DbgTransportSession::CleanupTargetProcess()
248{
249 m_pipe.CleanupTargetProcess();
250}
251
252// On the RS it may be useful to wait and see if the session can reach the SS_Open state. If the target
253// runtime has terminated for some reason then we'll never reach the open state. So the method below gives the
254// RS a way to try and establish a connection for a reasonable amount of time and to time out otherwise. They
255// could then call Shutdown on the session and report an error back to the rest of the debugger. The method
256// returns true if the session opened within the time given (in milliseconds) and false otherwise.
257bool DbgTransportSession::WaitForSessionToOpen(DWORD dwTimeout)
258{
259 DWORD dwRet = WaitForSingleObject(m_hSessionOpenEvent, dwTimeout);
260 if (m_eState == SS_Closed)
261 return false;
262
263 if (dwRet == WAIT_TIMEOUT)
264 DbgTransportLog(LC_Proxy, "DbgTransportSession::WaitForSessionToOpen(%u) timed out", dwTimeout);
265
266 return dwRet == WAIT_OBJECT_0;
267}
268
269//---------------------------------------------------------------------------------------
270//
271// A valid ticket is returned if no other client is currently acting as the debugger.
272// If the caller passes in a valid ticket, this function will return true without invalidating the ticket.
273//
274// Arguments:
275// pTicket - out parameter; set to a valid ticket if the client has successfully registered as the debugger
276//
277// Return Value:
278// Return true if the client has successfully registered as the debugger.
279//
280
281bool DbgTransportSession::UseAsDebugger(DebugTicket * pTicket)
282{
283 TransportLockHolder sLockHolder(&m_sStateLock);
284 if (m_fDebuggerAttached)
285 {
286 if (pTicket->IsValid())
287 {
288 // The client already holds a valid ticket.
289 return true;
290 }
291 else
292 {
293 // Another client of this session has already indicated that it's using this session to debug.
294 _ASSERTE(!pTicket->IsValid());
295 return false;
296 }
297 }
298 else
299 {
300 m_fDebuggerAttached = true;
301 pTicket->SetValid();
302 return true;
303 }
304}
305
306//---------------------------------------------------------------------------------------
307//
308// A valid ticket is required in order for this function to succeed. After this function succeeds,
309// another client can request to be the debugger.
310//
311// Arguments:
312// pTicket - the client's ticket; must be valid for this function to succeed
313//
314// Return Value:
315// Return true if the client has successfully unregistered as the debugger.
316// Return false if no client is currently acting as the debugger or if the client's ticket is invalid.
317//
318
319bool DbgTransportSession::StopUsingAsDebugger(DebugTicket * pTicket)
320{
321 TransportLockHolder sLockHolder(&m_sStateLock);
322 if (m_fDebuggerAttached && pTicket->IsValid())
323 {
324 // The caller is indeed the owner of the debug ticket.
325 m_fDebuggerAttached = false;
326 pTicket->SetInvalid();
327 return true;
328 }
329 else
330 {
331 return false;
332 }
333}
334#endif // RIGHT_SIDE_COMPILE
335
336// Sends a pre-initialized event to the other side.
337HRESULT DbgTransportSession::SendEvent(DebuggerIPCEvent *pEvent)
338{
339 DbgTransportLog(LC_Events, "Sending '%s'", IPCENames::GetName(pEvent->type));
340 DBG_TRANSPORT_INC_STAT(SentEvent);
341
342 return SendEventWorker(pEvent, IPCET_OldStyle);
343}
344
345// Sends a pre-initialized event to the other side, but pretend that this is coming from the native pipeline.
346// See code:IPCEventType for more information.
347HRESULT DbgTransportSession::SendDebugEvent(DebuggerIPCEvent * pEvent)
348{
349 DbgTransportLog(LC_Events, "Sending '%s' as DEBUG_EVENT", IPCENames::GetName(pEvent->type));
350 DBG_TRANSPORT_INC_STAT(SentEvent);
351
352 return SendEventWorker(pEvent, IPCET_DebugEvent);
353}
354
355// Retrieves the auto-reset handle which is signalled by the session each time a new event is received from
356// the other side.
357HANDLE DbgTransportSession::GetIPCEventReadyEvent()
358{
359 return m_rghEventReadyEvent[IPCET_OldStyle];
360}
361
362// Retrieves the auto-reset handle which is signalled by the session each time a new event (disguised as a
363// debug event) is received from the other side.
364HANDLE DbgTransportSession::GetDebugEventReadyEvent()
365{
366 return m_rghEventReadyEvent[IPCET_DebugEvent];
367}
368
369// Copies the last event received from the other side into the provided buffer. This should only be called
370// (once) after the event returned from GetIPCEEventReadyEvent()/GetDebugEventReadyEvent() has been signalled.
371void DbgTransportSession::GetNextEvent(DebuggerIPCEvent *pEvent, DWORD cbEvent)
372{
373 _ASSERTE(cbEvent <= CorDBIPC_BUFFER_SIZE);
374
375 // Must acquire the state lock to synchronize us wrt to the transport thread (clients already guarantee
376 // they serialize calls to this and waiting on m_rghEventReadyEvent).
377 TransportLockHolder sLockHolder(&m_sStateLock);
378
379 // There must be at least one valid event waiting (this call does not block).
380 _ASSERTE(m_cValidEventBuffers);
381
382 // Copy the first valid event into the client's buffer.
383 memcpy(pEvent, &m_pEventBuffers[m_idxEventBufferHead].m_event, cbEvent);
384
385 // Move the index of the head of the valid list forward (which may in fact move it back to the start of
386 // the array since the list is circular). This reduces the number of valid entries by one. Note that these
387 // two adjustments do not affect the tail of the list in any way. In the limit case the head will end up
388 // pointing to the same event as the tail (and m_cValidEventBuffers will be zero).
389 m_idxEventBufferHead = (m_idxEventBufferHead + 1) % m_cEventBuffers;
390 m_cValidEventBuffers--;
391 _ASSERTE(((m_idxEventBufferHead + m_cValidEventBuffers) % m_cEventBuffers) == m_idxEventBufferTail);
392
393 // If there's at least one more valid event we can signal event ready now.
394 if (m_cValidEventBuffers)
395 {
396 SetEvent(m_rghEventReadyEvent[m_pEventBuffers[m_idxEventBufferHead].m_type]);
397 }
398}
399
400
401
402void MarshalDCBTransportToDCB(DebuggerIPCControlBlockTransport* pIn, DebuggerIPCControlBlock* pOut)
403{
404 pOut->m_DCBSize = pIn->m_DCBSize;
405 pOut->m_verMajor = pIn->m_verMajor;
406 pOut->m_verMinor = pIn->m_verMinor;
407 pOut->m_checkedBuild = pIn->m_checkedBuild;
408 pOut->m_bHostingInFiber = pIn->m_bHostingInFiber;
409 pOut->padding2 = pIn->padding2;
410 pOut->padding3 = pIn->padding3;
411
412 pOut->m_leftSideProtocolCurrent = pIn->m_leftSideProtocolCurrent;
413 pOut->m_leftSideProtocolMinSupported = pIn->m_leftSideProtocolMinSupported;
414
415 pOut->m_rightSideProtocolCurrent = pIn->m_rightSideProtocolCurrent;
416 pOut->m_rightSideProtocolMinSupported = pIn->m_rightSideProtocolMinSupported;
417
418 pOut->m_errorHR = pIn->m_errorHR;
419 pOut->m_errorCode = pIn->m_errorCode;
420
421#if defined(DBG_TARGET_WIN64)
422 pOut->padding4 = pIn->padding4;
423#endif // DBG_TARGET_WIN64
424
425
426 //
427 //pOut->m_rightSideEventAvailable
428 //pOut->m_rightSideEventRead
429 //pOut->m_paddingObsoleteLSEA
430 //pOut->m_paddingObsoleteLSER
431 //pOut->m_rightSideProcessHandle
432 //pOut->m_leftSideUnmanagedWaitEvent
433
434 pOut->m_realHelperThreadId = pIn->m_realHelperThreadId;
435 pOut->m_helperThreadId = pIn->m_helperThreadId;
436 pOut->m_temporaryHelperThreadId = pIn->m_temporaryHelperThreadId;
437 pOut->m_CanaryThreadId = pIn->m_CanaryThreadId;
438 pOut->m_pRuntimeOffsets = pIn->m_pRuntimeOffsets;
439 pOut->m_helperThreadStartAddr = pIn->m_helperThreadStartAddr;
440 pOut->m_helperRemoteStartAddr = pIn->m_helperRemoteStartAddr;
441 pOut->m_specialThreadList = pIn->m_specialThreadList;
442
443 //
444 //pOut->m_receiveBuffer
445 //pOut->m_sendBuffer
446
447 pOut->m_specialThreadListLength = pIn->m_specialThreadListLength;
448 pOut->m_shutdownBegun = pIn->m_shutdownBegun;
449 pOut->m_rightSideIsWin32Debugger = pIn->m_rightSideIsWin32Debugger;
450 pOut->m_specialThreadListDirty = pIn->m_specialThreadListDirty;
451
452 pOut->m_rightSideShouldCreateHelperThread = pIn->m_rightSideShouldCreateHelperThread;
453
454}
455
456void MarshalDCBToDCBTransport(DebuggerIPCControlBlock* pIn, DebuggerIPCControlBlockTransport* pOut)
457{
458 pOut->m_DCBSize = pIn->m_DCBSize;
459 pOut->m_verMajor = pIn->m_verMajor;
460 pOut->m_verMinor = pIn->m_verMinor;
461 pOut->m_checkedBuild = pIn->m_checkedBuild;
462 pOut->m_bHostingInFiber = pIn->m_bHostingInFiber;
463 pOut->padding2 = pIn->padding2;
464 pOut->padding3 = pIn->padding3;
465
466 pOut->m_leftSideProtocolCurrent = pIn->m_leftSideProtocolCurrent;
467 pOut->m_leftSideProtocolMinSupported = pIn->m_leftSideProtocolMinSupported;
468
469 pOut->m_rightSideProtocolCurrent = pIn->m_rightSideProtocolCurrent;
470 pOut->m_rightSideProtocolMinSupported = pIn->m_rightSideProtocolMinSupported;
471
472 pOut->m_errorHR = pIn->m_errorHR;
473 pOut->m_errorCode = pIn->m_errorCode;
474
475#if defined(DBG_TARGET_WIN64)
476 pOut->padding4 = pIn->padding4;
477#endif // DBG_TARGET_WIN64
478
479 pOut->m_realHelperThreadId = pIn->m_realHelperThreadId;
480 pOut->m_helperThreadId = pIn->m_helperThreadId;
481 pOut->m_temporaryHelperThreadId = pIn->m_temporaryHelperThreadId;
482 pOut->m_CanaryThreadId = pIn->m_CanaryThreadId;
483 pOut->m_pRuntimeOffsets = pIn->m_pRuntimeOffsets;
484 pOut->m_helperThreadStartAddr = pIn->m_helperThreadStartAddr;
485 pOut->m_helperRemoteStartAddr = pIn->m_helperRemoteStartAddr;
486 pOut->m_specialThreadList = pIn->m_specialThreadList;
487
488 pOut->m_specialThreadListLength = pIn->m_specialThreadListLength;
489 pOut->m_shutdownBegun = pIn->m_shutdownBegun;
490 pOut->m_rightSideIsWin32Debugger = pIn->m_rightSideIsWin32Debugger;
491 pOut->m_specialThreadListDirty = pIn->m_specialThreadListDirty;
492
493 pOut->m_rightSideShouldCreateHelperThread = pIn->m_rightSideShouldCreateHelperThread;
494}
495
496
497
498#ifdef RIGHT_SIDE_COMPILE
499// Read and write memory on the LS from the RS.
500HRESULT DbgTransportSession::ReadMemory(PBYTE pbRemoteAddress, PBYTE pbBuffer, SIZE_T cbBuffer)
501{
502 DbgTransportLog(LC_Requests, "Sending 'ReadMemory(0x%08X, %u)'", pbRemoteAddress, cbBuffer);
503 DBG_TRANSPORT_INC_STAT(SentReadMemory);
504
505 Message sMessage;
506 sMessage.Init(MT_ReadMemory, NULL, 0, pbBuffer, (DWORD)cbBuffer);
507 sMessage.m_sHeader.TypeSpecificData.MemoryAccess.m_pbLeftSideBuffer = pbRemoteAddress;
508 sMessage.m_sHeader.TypeSpecificData.MemoryAccess.m_cbLeftSideBuffer = (DWORD)cbBuffer;
509
510 HRESULT hr = SendRequestMessageAndWait(&sMessage);
511 if (FAILED(hr))
512 return hr;
513
514 // If we reached here the send was successful but the actual memory operation may not have been (due to
515 // unmapped memory or page protections etc.). So the final result comes back to us in the reply.
516 return sMessage.m_sHeader.TypeSpecificData.MemoryAccess.m_hrResult;
517}
518
519HRESULT DbgTransportSession::WriteMemory(PBYTE pbRemoteAddress, PBYTE pbBuffer, SIZE_T cbBuffer)
520{
521 DbgTransportLog(LC_Requests, "Sending 'WriteMemory(0x%08X, %u)'", pbRemoteAddress, cbBuffer);
522 DBG_TRANSPORT_INC_STAT(SentWriteMemory);
523
524 Message sMessage;
525 sMessage.Init(MT_WriteMemory, pbBuffer, (DWORD)cbBuffer);
526 sMessage.m_sHeader.TypeSpecificData.MemoryAccess.m_pbLeftSideBuffer = pbRemoteAddress;
527 sMessage.m_sHeader.TypeSpecificData.MemoryAccess.m_cbLeftSideBuffer = (DWORD)cbBuffer;
528
529 HRESULT hr = SendRequestMessageAndWait(&sMessage);
530 if (FAILED(hr))
531 return hr;
532
533 // If we reached here the send was successful but the actual memory operation may not have been (due to
534 // unmapped memory or page protections etc.). So the final result comes back to us in the reply.
535 return sMessage.m_sHeader.TypeSpecificData.MemoryAccess.m_hrResult;
536}
537
538HRESULT DbgTransportSession::VirtualUnwind(DWORD threadId, ULONG32 contextSize, PBYTE context)
539{
540 DbgTransportLog(LC_Requests, "Sending 'VirtualUnwind'");
541 DBG_TRANSPORT_INC_STAT(SentVirtualUnwind);
542
543 Message sMessage;
544 sMessage.Init(MT_VirtualUnwind, context, contextSize, context, contextSize);
545 return SendRequestMessageAndWait(&sMessage);
546}
547
548// Read and write the debugger control block on the LS from the RS.
549HRESULT DbgTransportSession::GetDCB(DebuggerIPCControlBlock *pDCB)
550{
551 DbgTransportLog(LC_Requests, "Sending 'GetDCB'");
552 DBG_TRANSPORT_INC_STAT(SentGetDCB);
553
554 Message sMessage;
555 DebuggerIPCControlBlockTransport dcbt;
556 sMessage.Init(MT_GetDCB, NULL, 0, (PBYTE)&dcbt, sizeof(DebuggerIPCControlBlockTransport));
557 HRESULT ret = SendRequestMessageAndWait(&sMessage);
558
559 MarshalDCBTransportToDCB(&dcbt, pDCB);
560 return ret;
561}
562
563HRESULT DbgTransportSession::SetDCB(DebuggerIPCControlBlock *pDCB)
564{
565 DbgTransportLog(LC_Requests, "Sending 'SetDCB'");
566 DBG_TRANSPORT_INC_STAT(SentSetDCB);
567
568 DebuggerIPCControlBlockTransport dcbt;
569 MarshalDCBToDCBTransport(pDCB, &dcbt);
570
571 Message sMessage;
572 sMessage.Init(MT_SetDCB, (PBYTE)&dcbt, sizeof(DebuggerIPCControlBlockTransport));
573 return SendRequestMessageAndWait(&sMessage);
574
575}
576
577// Read the AppDomain control block on the LS from the RS.
578HRESULT DbgTransportSession::GetAppDomainCB(AppDomainEnumerationIPCBlock *pADB)
579{
580 DbgTransportLog(LC_Requests, "Sending 'GetAppDomainCB'");
581 DBG_TRANSPORT_INC_STAT(SentGetAppDomainCB);
582
583 Message sMessage;
584 sMessage.Init(MT_GetAppDomainCB, NULL, 0, (PBYTE)pADB, sizeof(AppDomainEnumerationIPCBlock));
585 return SendRequestMessageAndWait(&sMessage);
586}
587
588#endif // RIGHT_SIDE_COMPILE
589
590// Worker function for code:DbgTransportSession::SendEvent and code:DbgTransportSession::SendDebugEvent.
591HRESULT DbgTransportSession::SendEventWorker(DebuggerIPCEvent * pEvent, IPCEventType type)
592{
593 DWORD cbEvent = GetEventSize(pEvent);
594 _ASSERTE(cbEvent <= CorDBIPC_BUFFER_SIZE);
595
596 Message sMessage;
597 sMessage.Init(MT_Event, (PBYTE)pEvent, cbEvent);
598
599 // Store the event type in the header as well, it's sometimes useful for debugging.
600 sMessage.m_sHeader.TypeSpecificData.Event.m_eIPCEventType = type;
601 sMessage.m_sHeader.TypeSpecificData.Event.m_eType = pEvent->type;
602
603 return SendMessage(&sMessage, false);
604}
605
606// Sends a pre-formatted message (including the data block, if any). The fWaitsForReply indicates whether the
607// caller is going to block until some sort of reply message is received (for instance an event that must be
608// ack'd or a request such as MT_GetDCB that needs a reply). SendMessage() uses this to determine whether it
609// needs to buffer the message before placing it on the send queue (since it may need to resend the message
610// after a transitory network failure).
611HRESULT DbgTransportSession::SendMessage(Message *pMessage, bool fWaitsForReply)
612{
613 // Serialize the whole operation under the state lock. In particular we need to make allocating the
614 // message ID atomic wrt placing the message on the connection (to ensure our IDs are seen in order by the
615 // other side). We also need to hold the lock while manipulating the send queue (to prevent corruption)
616 // and while determining whether to send immediately or not depending on the session state (to avoid
617 // posting a send on a closed and possibly recycled socket).
618 {
619 TransportLockHolder sLockHolder(&m_sStateLock);
620
621 // Perform any last updates to the header or data block here since we might be about to encrypt them.
622
623 // Give this message a unique ID (useful both to track which messages need to be resent on a network
624 // failure and to match replies to the original message).
625 pMessage->m_sHeader.m_dwId = m_dwNextMessageId++;
626
627 // Use this message send to piggyback an acknowledgement of the last message we processed from the
628 // other side (this will allow the other side to discard one or more buffered messages from its send
629 // queue).
630 pMessage->m_sHeader.m_dwLastSeenId = m_dwLastMessageIdSeen;
631
632 // If the caller isn't waiting around for a reply we must make a copy of the message to place on the
633 // send queue.
634 pMessage->m_pOrigMessage = pMessage;
635 Message *pMessageCopy = NULL;
636 PBYTE pDataBlockCopy = NULL;
637 if (!fWaitsForReply)
638 {
639 // Allocate a new message (includes an embedded message header).
640 pMessageCopy = new (nothrow) Message();
641 if (pMessageCopy == NULL)
642 return E_OUTOFMEMORY;
643
644 // Allocate a new data block if one is being used.
645 if (pMessage->m_pbDataBlock)
646 {
647 pDataBlockCopy = new (nothrow) BYTE[pMessage->m_cbDataBlock];
648 if (pDataBlockCopy == NULL)
649 {
650 delete pMessageCopy;
651 return E_OUTOFMEMORY;
652 }
653 }
654
655 // Copy the message descriptor over.
656 memcpy(pMessageCopy, pMessage, sizeof(Message));
657
658 // And the data block if applicable.
659 if (pDataBlockCopy)
660 memcpy(pDataBlockCopy, pMessage->m_pbDataBlock, pMessage->m_cbDataBlock);
661
662 // The message copy still points to the wrong data block (if there is one).
663 pMessageCopy->m_pbDataBlock = pDataBlockCopy;
664
665 // Point the copy back to the original message.
666 pMessageCopy->m_pOrigMessage = pMessage;
667
668 // From now on we'll use the copy.
669 pMessage = pMessageCopy;
670 }
671
672 // Check the session state.
673 if (m_eState == SS_Closed)
674 {
675 // SS_Closed is bad news, we'll never recover from that so error the send immediately.
676 if (pMessageCopy)
677 delete pMessageCopy;
678 if (pDataBlockCopy)
679 delete [] pDataBlockCopy;
680
681 return E_ABORT;
682 }
683
684 // Don't queue session management messages. We always recreate these if we need to re-send them.
685 if (pMessage->m_sHeader.m_eType > MT_SessionClose)
686 {
687 // Regardless of session state we always queue the message for at least as long as it takes us to
688 // be sure the other side has received the message.
689 if (m_pSendQueueLast == NULL)
690 {
691 // Queue is currently empty.
692 m_pSendQueueFirst = pMessage;
693 m_pSendQueueLast = pMessage;
694 pMessage->m_pNext = NULL;
695 }
696 else
697 {
698 // Place on end of queue.
699 m_pSendQueueLast->m_pNext = pMessage;
700 m_pSendQueueLast = pMessage;
701 pMessage->m_pNext = NULL;
702 }
703 }
704
705 // If the state is SS_Open we can send the message now.
706 if (m_eState == SS_Open)
707 {
708 // Send the message header block followed by the data block if it's provided. Any network error will
709 // be reported internally by SendBlock and result in a transition to the SS_Resync_NC state (and an
710 // eventual resend of the data).
711 if (SendBlock((PBYTE)&pMessage->m_sHeader, sizeof(MessageHeader)) && pMessage->m_pbDataBlock)
712 SendBlock(pMessage->m_pbDataBlock, pMessage->m_cbDataBlock);
713 }
714
715 // If the state wasn't open there's nothing more to be done. The state will eventually transition to
716 // either SS_Open (in which case the transport thread will send all pending messages for us at the
717 // transition point) or SS_Closed (where the transport thread will drain the queue and discard each
718 // message, setting m_fAborted if necessary).
719
720 } // Leave m_sStateLock
721
722 return S_OK;
723}
724
725// Helper method for sending messages requiring a reply (such as MT_GetDCB) and waiting on the result.
726HRESULT DbgTransportSession::SendRequestMessageAndWait(Message *pMessage)
727{
728 // Allocate event to wait for reply on.
729 pMessage->m_hReplyEvent = WszCreateEvent(NULL, FALSE, FALSE, NULL); // Auto-reset, not signalled
730 if (pMessage->m_hReplyEvent == NULL)
731 return E_OUTOFMEMORY;
732
733 // Duplicate the handle to the event. It's necessary to have two handles to the same event because
734 // both this thread and the message pumping thread may be trying to access the handle at the same
735 // time (e.g. closing the handle). So we make a duplicate handle. This thread is responsible for
736 // closing hReplyEvent (the local variable) whereas the message pumping thread is responsible for
737 // closing the handle on the message.
738 HANDLE hReplyEvent = NULL;
739 if (!DuplicateHandle(GetCurrentProcess(),
740 pMessage->m_hReplyEvent,
741 GetCurrentProcess(),
742 &hReplyEvent,
743 0, // ignored since we are going to pass DUPLICATE_SAME_ACCESS
744 FALSE,
745 DUPLICATE_SAME_ACCESS))
746 {
747 return HRESULT_FROM_GetLastError();
748 }
749
750 // Send the request.
751 HRESULT hr = SendMessage(pMessage, true);
752 if (FAILED(hr))
753 {
754 // In this case, we need to close both handles since the message is never put into the send queue.
755 // This thread is the only one who has access to the message.
756 CloseHandle(pMessage->m_hReplyEvent);
757 CloseHandle(hReplyEvent);
758 return hr;
759 }
760
761 // At this point, the message pumping thread may receive the reply any time. It may even receive the
762 // reply message even before we wait on the event. Keep this in mind.
763
764 // Wait for a reply (by the time this event is signalled the message header will have been overwritten by
765 // the reply and any output buffer provided will have been filled in).
766#if defined(RIGHT_SIDE_COMPILE)
767 HANDLE rgEvents[] = { hReplyEvent, m_hProcessExited };
768#else // !RIGHT_SIDE_COMPILE
769 HANDLE rgEvents[] = { hReplyEvent };
770#endif // RIGHT_SIDE_COMPILE
771
772 DWORD dwResult = WaitForMultipleObjectsEx(sizeof(rgEvents)/sizeof(rgEvents[0]), rgEvents, FALSE, INFINITE, FALSE);
773
774 if (dwResult == WAIT_OBJECT_0)
775 {
776 // This is the normal case. The message pumping thread receives a reply from the debuggee process.
777 // It signals the event to wake up this thread.
778 CloseHandle(hReplyEvent);
779
780 // Check whether the session aborted us due to a Shutdown().
781 if (pMessage->m_fAborted)
782 return E_ABORT;
783 }
784#if defined(RIGHT_SIDE_COMPILE)
785 else if (dwResult == (WAIT_OBJECT_0 + 1))
786 {
787 // This is the complicated case. This thread wakes up because the debuggee process is terminated.
788 // At the same time, the message pumping thread may be in the process of handling the reply message.
789 // We need to be careful here because there is a race condition.
790
791 // Remove the original message from the send queue. This is because in the case of a blocking message,
792 // the message can be allocated on the stack. Thus, the message becomes invalid when we return from
793 // this function. The message pumping thread may have beaten this thread to it. That's ok since
794 // RemoveMessageFromSendQueue() takes the state lock.
795 Message * pOriginalMessage = RemoveMessageFromSendQueue(pMessage->m_sHeader.m_dwId);
796 _ASSERTE((pOriginalMessage == NULL) || (pOriginalMessage == pMessage));
797
798 // If the message pumping thread has beaten this thread to removing the original message, then this
799 // thread must wait until the message pumping thread is done with the message before returning.
800 // Otherwise, the message may become invalid when the message pumping thread is accessing it.
801 // Fortunately, in this case, we know the message pumping thread is going to signal the event.
802 if (pOriginalMessage == NULL)
803 {
804 WaitForSingleObject(hReplyEvent, INFINITE);
805 }
806
807 CloseHandle(hReplyEvent);
808 return CORDBG_E_PROCESS_TERMINATED;
809 }
810#endif // RIGHT_SIDE_COMPILE
811 else
812 {
813 // Should never get here.
814 CloseHandle(hReplyEvent);
815 UNREACHABLE();
816 }
817
818 return S_OK;
819}
820
821// Sends a single contiguous buffer of host memory over the connection. The caller is responsible for holding
822// the state lock and ensuring the session state is SS_Open. Returns false if the send failed (the error will
823// have already caused the recovery logic to kick in, so handling it is not required, the boolean is just
824// returned so that any further blocks in the message are not sent).
825bool DbgTransportSession::SendBlock(PBYTE pbBuffer, DWORD cbBuffer)
826{
827 _ASSERTE(m_eState == SS_Opening || m_eState == SS_Resync || m_eState == SS_Open);
828 _ASSERTE(m_pipe.GetState() == TwoWayPipe::ServerConnected || m_pipe.GetState() == TwoWayPipe::ClientConnected);
829 _ASSERTE(cbBuffer > 0);
830
831 DBG_TRANSPORT_INC_STAT(SentBlocks);
832 DBG_TRANSPORT_ADD_STAT(SentBytes, cbBuffer);
833
834 //DbgTransportLog(LC_Proxy, "SendBlock(%08X, %u)", pbBuffer, cbBuffer);
835 bool fSuccess;
836 if (DBG_TRANSPORT_SHOULD_INJECT_FAULT(Send))
837 fSuccess = false;
838 else
839 fSuccess = (m_pipe.Write(pbBuffer, cbBuffer) == cbBuffer);
840
841 if (!fSuccess)
842 {
843 DbgTransportLog(LC_NetErrors, "Network error on Send()");
844 DBG_TRANSPORT_INC_STAT(SendErrors);
845 HandleNetworkError(true);
846 return false;
847 }
848
849 return true;
850}
851
852// Receives a single contiguous buffer of host memory over the connection. No state lock needs to be held
853// (receives are serialized by the fact they're only performed on the transport thread). Returns false if a
854// network error is encountered (which will automatically transition the session into the correct retry
855// state).
856bool DbgTransportSession::ReceiveBlock(PBYTE pbBuffer, DWORD cbBuffer)
857{
858 _ASSERTE(m_pipe.GetState() == TwoWayPipe::ServerConnected || m_pipe.GetState() == TwoWayPipe::ClientConnected);
859 _ASSERTE(cbBuffer > 0);
860
861 DBG_TRANSPORT_INC_STAT(ReceivedBlocks);
862 DBG_TRANSPORT_ADD_STAT(ReceivedBytes, cbBuffer);
863
864 //DbgTransportLog(LC_Proxy, "ReceiveBlock(%08X, %u)", pbBuffer, cbBuffer);
865
866 bool fSuccess;
867 if (DBG_TRANSPORT_SHOULD_INJECT_FAULT(Receive))
868 fSuccess = false;
869 else
870 fSuccess = (m_pipe.Read(pbBuffer, cbBuffer) == cbBuffer);
871
872 if (!fSuccess)
873 {
874 DbgTransportLog(LC_NetErrors, "Network error on Receive()");
875 DBG_TRANSPORT_INC_STAT(ReceiveErrors);
876 HandleNetworkError(false);
877 return false;
878 }
879
880 return true;
881}
882
883// Called upon encountering a network error (e.g. an error from Send() or Receive()). This handles pushing the
884// session state into SS_Resync_NC or SS_Opening_NC in order to start the recovery process.
885void DbgTransportSession::HandleNetworkError(bool fCallerHoldsStateLock)
886{
887 _ASSERTE(m_eState == SS_Open || m_eState == SS_Opening || m_eState == SS_Resync || !fCallerHoldsStateLock);
888
889 // Check the easy cases first which don't require us to take the lock (because we don't transition the
890 // state). These are the SS_Closed state (a network error doesn't matter when we're closing down the
891 // session anyway) and the SS_*_NC states (which indicate someone else beat us to it, closed the
892 // connection and has started recovery).
893 if (m_eState == SS_Closed ||
894 m_eState == SS_Opening_NC ||
895 m_eState == SS_Resync_NC)
896 return;
897
898 // We need the state lock to perform a state transition.
899 if (!fCallerHoldsStateLock)
900 m_sStateLock.Enter();
901
902 switch (m_eState)
903 {
904 case SS_Closed:
905 case SS_Opening_NC:
906 case SS_Resync_NC:
907 // Still need to cope with the no-op states handled above since we could have transitioned into them
908 // before we took the lock.
909 break;
910
911 case SS_Opening:
912 // All work to transition SS_Opening to SS_Open is performed by the transport thread, so we know we're
913 // on that thread. Consequently it's just enough to set the state to SS_Opening_NC and the thread will
914 // notice the change when the SendMessage() or ReceiveBlock() call completes.
915 m_eState = SS_Opening_NC;
916 break;
917
918 case SS_Resync:
919 // Likewise, all the work to transition SS_Resync to SS_Open is performed by the transport thread, so
920 // we know we're on that thread.
921 m_eState = SS_Resync_NC;
922 break;
923
924 case SS_Open:
925 // The state change to SS_Resync_NC will prompt the transport thread (which might be this thread) that
926 // it should discard the current connection and reform a new one. It will also cause sends to be
927 // queued instead of sent. In case we're not the transport thread and instead it is currently stuck in
928 // a Receive (I don't entirely trust the connection to immediately fail these on a network problem)
929 // we'll call CancelReceive() to abort the operation. The transport thread itself will handle the
930 // actual Destroy() (having one thread do this management greatly simplifies things).
931 m_eState = SS_Resync_NC;
932 m_pipe.Disconnect();
933 break;
934
935 default:
936 _ASSERTE(!"Unknown session state");
937 }
938
939 if (!fCallerHoldsStateLock)
940 m_sStateLock.Leave();
941}
942
943// Scan the send queue and discard any messages which have been processed by the other side according to the
944// specified ID). Messages waiting on a reply message (e.g. MT_GetDCB) will be retained until that reply is
945// processed. FlushSendQueue will take the state lock.
946void DbgTransportSession::FlushSendQueue(DWORD dwLastProcessedId)
947{
948 // Must access the send queue under the state lock.
949 TransportLockHolder sLockHolder(&m_sStateLock);
950
951 // Note that message headers (and data blocks) may be encrypted. Use the cached fields in the Message
952 // structure to compare message IDs and types.
953
954 Message *pMsg = m_pSendQueueFirst;
955 Message *pLastMsg = NULL;
956 while (pMsg)
957 {
958 if (pMsg->m_sHeader.m_dwId <= dwLastProcessedId)
959 {
960 // Message has been seen and processed by other side.
961 // Check if we can discard it (i.e. it's not waiting on a reply message that needs the original
962 // request to hang around).
963#ifdef RIGHT_SIDE_COMPILE
964 MessageType eType = pMsg->m_sHeader.m_eType;
965 if (eType != MT_ReadMemory &&
966 eType != MT_WriteMemory &&
967 eType != MT_VirtualUnwind &&
968 eType != MT_GetDCB &&
969 eType != MT_SetDCB &&
970 eType != MT_GetAppDomainCB)
971#endif // RIGHT_SIDE_COMPILE
972 {
973#ifdef RIGHT_SIDE_COMPILE
974 _ASSERTE(eType == MT_Event);
975#endif // RIGHT_SIDE_COMPILE
976
977 // We can discard this message.
978
979 // Unlink it from the queue.
980 if (pLastMsg == NULL)
981 m_pSendQueueFirst = pMsg->m_pNext;
982 else
983 pLastMsg->m_pNext = pMsg->m_pNext;
984 if (m_pSendQueueLast == pMsg)
985 m_pSendQueueLast = pLastMsg;
986
987 Message *pDiscardMsg = pMsg;
988 pMsg = pMsg->m_pNext;
989
990 // If the message is a copy deallocate it (and the data block associated with it).
991 if (pDiscardMsg->m_pOrigMessage != pDiscardMsg)
992 {
993 if (pDiscardMsg->m_pbDataBlock)
994 delete [] pDiscardMsg->m_pbDataBlock;
995 delete pDiscardMsg;
996 }
997
998 continue;
999 }
1000 }
1001
1002 pLastMsg = pMsg;
1003 pMsg = pMsg->m_pNext;
1004 }
1005}
1006
1007#ifdef RIGHT_SIDE_COMPILE
1008// Perform processing required to complete a request (such as MT_GetDCB) once a reply comes in. This includes
1009// reading data from the connection into the output buffer, removing the original message from the send queue
1010// and signalling the completion event. Returns true if no network error was encountered.
1011bool DbgTransportSession::ProcessReply(MessageHeader *pHeader)
1012{
1013 // Locate original message on the send queue.
1014 Message *pMsg = RemoveMessageFromSendQueue(pHeader->m_dwReplyId);
1015
1016 // This can happen if the thread blocked waiting for the replyl message has waken up because the debuggee
1017 // process has terminated. See code:DbgTransportSession::SendRequestMessageAndWait() for more info.
1018 if (pMsg == NULL)
1019 {
1020 return true;
1021 }
1022
1023 // If there is a reply block but the caller hasn't specified a reply buffer.
1024 // This combination is not used any more.
1025 _ASSERTE(! ((pHeader->m_cbDataBlock != (DWORD)0) && (pMsg->m_pbReplyBlock == (PBYTE)NULL)) );
1026
1027 // If there was an output buffer provided then we copy the data block in the reply into it (perhaps
1028 // decrypting it first). If the reply header indicates there is no data block then presumably the request
1029 // failed (which should be indicated in the TypeSpecificData of the reply, ala MT_ReadMemory).
1030 if (pMsg->m_pbReplyBlock && pHeader->m_cbDataBlock)
1031 {
1032 _ASSERTE(pHeader->m_cbDataBlock == pMsg->m_cbReplyBlock);
1033 if (!ReceiveBlock(pMsg->m_pbReplyBlock, pMsg->m_cbReplyBlock))
1034 {
1035 // Whoops. We hit an error trying to read the reply data. We need to push the original message
1036 // back on the queue and await a retry. Since this message must have been seen by the other side
1037 // we don't need to put it on the queue in order (it will never be resent). Easiest just to put it
1038 // on the head.
1039 {
1040 TransportLockHolder sLockHolder(&m_sStateLock);
1041 pMsg->m_pNext = m_pSendQueueFirst;
1042 m_pSendQueueFirst = pMsg;
1043 if (m_pSendQueueLast == NULL)
1044 m_pSendQueueLast = pMsg;
1045 return false;
1046 } // Leave m_sStateLock
1047 }
1048 }
1049
1050 // Copy TypeSpecificData from the reply back into the original message (it can contain additional status).
1051 // Be careful to update the real original message (the version on the queue will be a copy if we're using
1052 // a secure session).
1053 pMsg->m_pOrigMessage->m_sHeader.TypeSpecificData = pHeader->TypeSpecificData;
1054
1055 // **** IMPORTANT NOTE ****
1056 // We're about to cause a side-effect visible to our client. From here on out (until we update the
1057 // session's idea of the last incoming message we processed back in the transport thread's main loop) we
1058 // must avoid any failures. If we fail before the update the other side will re-send the message which is
1059 // bad if we've already processed it. See the comment near the start of the SS_Open message dispatch logic
1060 // for more details.
1061 // **** IMPORTANT NOTE ****
1062
1063 // Signal the completion event.
1064 SignalReplyEvent(pMsg);
1065
1066 return true;
1067}
1068
1069//---------------------------------------------------------------------------------------
1070//
1071// Upon receiving a reply message, signal the event on the message to wake up the thread waiting for
1072// the reply message and close the handle to the event.
1073//
1074// Arguments:
1075// pMessage - the reply message to be processed
1076//
1077
1078void DbgTransportSession::SignalReplyEvent(Message * pMessage)
1079{
1080 // Make a local copy of the event handle. As soon as we signal the event, the thread blocked waiting on
1081 // the reply may wake up and trash the message. See code:DbgTransportSession::SendRequestMessageAndWait()
1082 // for more info.
1083 HANDLE hReplyEvent = pMessage->m_hReplyEvent;
1084 _ASSERTE(hReplyEvent != NULL);
1085
1086 SetEvent(hReplyEvent);
1087 CloseHandle(hReplyEvent);
1088}
1089
1090//---------------------------------------------------------------------------------------
1091//
1092// Given a message ID, find the matching message in the send queue. If there is no match, return NULL.
1093// If there is a match, remove the message from the send queue and return it.
1094//
1095// Arguments:
1096// dwMessageId - the ID of the message to retrieve
1097//
1098// Return Value:
1099// NULL if the specified message cannot be found.
1100// Otherwise return the specified message with the side effect that it's also removed from the send queue.
1101//
1102// Notes:
1103// The caller is NOT responsible for taking the state lock. This function will do that.
1104//
1105
1106DbgTransportSession::Message * DbgTransportSession::RemoveMessageFromSendQueue(DWORD dwMessageId)
1107{
1108 // Locate original message on the send queue.
1109 Message *pMsg = NULL;
1110 {
1111 TransportLockHolder sLockHolder(&m_sStateLock);
1112
1113 pMsg = m_pSendQueueFirst;
1114 Message *pLastMsg = NULL;
1115
1116 while (pMsg)
1117 {
1118 if (dwMessageId == pMsg->m_sHeader.m_dwId)
1119 {
1120 // Found the original message that this is a reply to. Unlink it.
1121 if (pLastMsg == NULL)
1122 m_pSendQueueFirst = pMsg->m_pNext;
1123 else
1124 pLastMsg->m_pNext = pMsg->m_pNext;
1125
1126 if (m_pSendQueueLast == pMsg)
1127 m_pSendQueueLast = pLastMsg;
1128 break;
1129 }
1130
1131 pLastMsg = pMsg;
1132 pMsg = pMsg->m_pNext;
1133 }
1134 } // Leave m_sStateLock
1135
1136 // could be NULL
1137 return pMsg;
1138}
1139#endif
1140
1141#ifndef RIGHT_SIDE_COMPILE
1142
1143// Check read and optionally write memory access to the specified range of bytes. Used to check
1144// ReadProcessMemory and WriteProcessMemory requests.
1145HRESULT DbgTransportSession::CheckBufferAccess(__in_ecount(cbBuffer) PBYTE pbBuffer, DWORD cbBuffer, bool fWriteAccess)
1146{
1147 // check for integer overflow
1148 if ((pbBuffer + cbBuffer) < pbBuffer)
1149 {
1150 return HRESULT_FROM_WIN32(ERROR_ARITHMETIC_OVERFLOW);
1151 }
1152
1153 // VirtualQuery doesn't know much about memory allocated outside of PAL's VirtualAlloc
1154 // that's why on Unix we can't rely on in to detect invalid memory reads
1155#ifndef FEATURE_PAL
1156 do
1157 {
1158 // Find the attributes of the largest set of pages with common attributes starting from our base address.
1159 MEMORY_BASIC_INFORMATION sMemInfo;
1160 VirtualQuery(pbBuffer, &sMemInfo, sizeof(sMemInfo));
1161
1162 DbgTransportLog(LC_Proxy, "CBA(%08X,%08X): State:%08X Protect:%08X BA:%08X RS:%08X",
1163 pbBuffer, cbBuffer, sMemInfo.State, sMemInfo.Protect, sMemInfo.BaseAddress, sMemInfo.RegionSize);
1164
1165 // The memory must be committed (i.e. have physical pages or backing store).
1166 if (sMemInfo.State != MEM_COMMIT)
1167 return HRESULT_FROM_WIN32(ERROR_INVALID_ADDRESS);
1168
1169 // Check for compatible page protections. Lower byte of Protect has these (upper bytes have options we're
1170 // not interested in, cache modes and the like.
1171 DWORD dwProtect = sMemInfo.Protect & 0xff;
1172
1173 if (fWriteAccess &&
1174 ((dwProtect & (PAGE_EXECUTE_READWRITE | PAGE_EXECUTE_WRITECOPY | PAGE_READWRITE | PAGE_WRITECOPY)) == 0))
1175 return HRESULT_FROM_WIN32(ERROR_NOACCESS);
1176 else if (!fWriteAccess &&
1177 ((dwProtect & (PAGE_EXECUTE_READ | PAGE_EXECUTE_READWRITE | PAGE_EXECUTE_WRITECOPY | PAGE_READONLY | PAGE_READWRITE | PAGE_WRITECOPY)) == 0))
1178 return HRESULT_FROM_WIN32(ERROR_NOACCESS);
1179
1180 // If the requested range is bigger than the region we have queried,
1181 // we need to continue on to check the next region.
1182 if ((pbBuffer + cbBuffer) > ((PBYTE)sMemInfo.BaseAddress + sMemInfo.RegionSize))
1183 {
1184 PBYTE pbRegionEnd = reinterpret_cast<PBYTE>(sMemInfo.BaseAddress) + sMemInfo.RegionSize;
1185 cbBuffer = (DWORD)((pbBuffer + cbBuffer) - pbRegionEnd);
1186 pbBuffer = pbRegionEnd;
1187 }
1188 else
1189 {
1190 // We are done. Set cbBuffer to 0 to exit this loop.
1191 cbBuffer = 0;
1192 }
1193 }
1194 while (cbBuffer > 0);
1195#else
1196 if (!PAL_ProbeMemory(pbBuffer, cbBuffer, fWriteAccess))
1197 {
1198 return HRESULT_FROM_WIN32(ERROR_INVALID_ADDRESS);
1199 }
1200#endif
1201
1202 // The specified region has passed all of our checks.
1203 return S_OK;
1204}
1205
1206#endif // !RIGHT_SIDE_COMPILE
1207
1208// Initialize all session state to correct starting values. Used during Init() and on the LS when we
1209// gracefully close one session and prepare for another.
1210void DbgTransportSession::InitSessionState()
1211{
1212 DBG_TRANSPORT_INC_STAT(Sessions);
1213
1214 m_dwMajorVersion = kCurrentMajorVersion;
1215 m_dwMinorVersion = kCurrentMinorVersion;
1216
1217 memset(&m_sSessionID, 0, sizeof(m_sSessionID));
1218
1219 m_pSendQueueFirst = NULL;
1220 m_pSendQueueLast = NULL;
1221
1222 m_dwNextMessageId = 1;
1223 m_dwLastMessageIdSeen = 0;
1224
1225 m_eState = SS_Opening_NC;
1226
1227 m_cValidEventBuffers = 0;
1228 m_idxEventBufferHead = 0;
1229 m_idxEventBufferTail = 0;
1230}
1231
1232// The entry point of the transport worker thread. This one's static, so we immediately dispatch to an
1233// instance method version defined below for convenience in the implementation.
1234DWORD WINAPI DbgTransportSession::TransportWorkerStatic(LPVOID pvContext)
1235{
1236 ((DbgTransportSession*)pvContext)->TransportWorker();
1237
1238 // Nobody looks at this result, the choice of 0 is arbitrary.
1239 return 0;
1240}
1241
1242// Macros used to simplify error and state transition handling within the transport worker loop. Errors are
1243// classified as either transient or critical. Transient errors (typically those from network operations)
1244// result in the connection being closed and rebuilt: we should eventually recover from them. Critical errors
1245// are those that cause a transition to the SS_Closed state, which the session never recovers from. These are
1246// normally due to protocol errors where we want to shut the transport down in case they are of malicious
1247// origin.
1248#define HANDLE_TRANSIENT_ERROR() do { \
1249 HandleNetworkError(false); \
1250 m_pipe.Disconnect(); \
1251 goto ResetConnection; \
1252} while (false)
1253
1254#define HANDLE_CRITICAL_ERROR() do { \
1255 m_eState = SS_Closed; \
1256 goto Shutdown; \
1257} while (false)
1258
1259#ifdef _PREFAST_
1260#pragma warning(push)
1261#pragma warning(disable:21000) // Suppress PREFast warning about overly large function
1262#endif
1263void DbgTransportSession::TransportWorker()
1264{
1265 _ASSERTE(m_eState == SS_Opening_NC);
1266
1267 // Loop until shutdown. Each loop iteration involves forming a connection (or waiting for one to form)
1268 // followed by processing incoming messages on that connection until there's a failure (either here of
1269 // from a send on another thread) or the session shuts down. The connection is then closed and discarded
1270 // and we either go round the loop again (to recover our previous session state) or exit the method as
1271 // part of shutdown.
1272 ResetConnection:
1273 while (m_eState != SS_Closed)
1274 {
1275 _ASSERTE(m_eState == SS_Opening_NC || m_eState == SS_Resync_NC || m_eState == SS_Closed);
1276
1277 DbgTransportLog(LC_Proxy, "Forming new connection");
1278
1279#ifdef RIGHT_SIDE_COMPILE
1280 // The session is definitely not open at this point.
1281 ResetEvent(m_hSessionOpenEvent);
1282
1283 // On the right side we initiate the connection via Connect(). A failure is dealt with by waiting a
1284 // little while and retrying (the LS may take a little while to set up). If there's nobody listening
1285 // the debugger will eventually get bored waiting for us and shutdown the session, which will
1286 // terminate this loop.
1287 ConnStatus eStatus;
1288 if (DBG_TRANSPORT_SHOULD_INJECT_FAULT(Connect))
1289 eStatus = SCS_NetworkFailure;
1290 else
1291 {
1292 if (m_pipe.Connect(m_pd))
1293 {
1294 eStatus = SCS_Success;
1295 }
1296 else
1297 {
1298 //not really sure that this is the real failure
1299 //TODO: we probably need to analyse GetErrorCode() here
1300 eStatus = SCS_NoListener;
1301 }
1302 }
1303
1304 if (eStatus != SCS_Success)
1305 {
1306 DbgTransportLog(LC_Proxy, "AllocateConnection() failed with %u\n", eStatus);
1307 DBG_TRANSPORT_INC_STAT(MiscErrors);
1308 _ASSERTE(m_pipe.GetState() != TwoWayPipe::ClientConnected);
1309 Sleep(1000);
1310 continue;
1311 }
1312#else // RIGHT_SIDE_COMPILE
1313 ConnStatus eStatus;
1314 if (DBG_TRANSPORT_SHOULD_INJECT_FAULT(Accept))
1315 eStatus = SCS_NetworkFailure;
1316 else
1317 {
1318 ProcessDescriptor pd = ProcessDescriptor::FromCurrentProcess();
1319 if ((m_pipe.GetState() == TwoWayPipe::Created || m_pipe.CreateServer(pd)) &&
1320 m_pipe.WaitForConnection())
1321 {
1322 eStatus = SCS_Success;
1323 }
1324 else
1325 {
1326 //not really sure that this is the real failure
1327 //TODO: we probably need to analyse GetErrorCode() here
1328 eStatus = SCS_NoListener;
1329 }
1330 }
1331
1332 if (eStatus != SCS_Success)
1333 {
1334 DbgTransportLog(LC_Proxy, "Accept() failed with %u\n", eStatus);
1335 DBG_TRANSPORT_INC_STAT(MiscErrors);
1336 _ASSERTE(m_pipe.GetState() != TwoWayPipe::ServerConnected);
1337 Sleep(1000);
1338 continue;
1339 }
1340
1341 // Note that when resynching a session we may let in a connection from a different debugger. That's
1342 // OK, we'll reject his SessionRequest message in due course and drop the connection.
1343#endif // RIGHT_SIDE_COMPILE
1344
1345 DBG_TRANSPORT_INC_STAT(Connections);
1346
1347 // We now have a connection. Transition to the next state (either SS_Opening or SS_Resync). The
1348 // primary purpose of this state transition is to let other threads know that this thread might now be
1349 // blocked on a Receive() on the newly formed connection (important if they want to transition the state
1350 // to SS_Closed).
1351 {
1352 TransportLockHolder sLockHolder(&m_sStateLock);
1353
1354 if (m_eState == SS_Closed)
1355 break;
1356 else if (m_eState == SS_Opening_NC)
1357 m_eState = SS_Opening;
1358 else if (m_eState == SS_Resync_NC)
1359 m_eState = SS_Resync;
1360 else
1361 _ASSERTE(!"Bad session state");
1362 } // Leave m_sStateLock
1363
1364
1365 // Now we have a connection in place. Start reading messages and processing them. Which messages are
1366 // valid depends on whether we're in SS_Opening or SS_Resync (the state can change at any time
1367 // asynchronously to us to either SS_Closed or SS_Resync_NC but we're guaranteed the connection stays
1368 // valid (though not necessarily useful) until we notice this state change and Destroy() it ourself).
1369 // We check the state after each network operation.
1370
1371 // During the SS_Opening and SS_Resync states we're guarantee to be the only thread posting sends, so
1372 // we can break the rules and use SendBlock without acquiring the state lock. (We use SendBlock a lot
1373 // during these phases because we're using simple Session* messages which don't require the extra
1374 // processing SendMessage gives us such as encryption or placement on the send queue).
1375
1376 MessageHeader sSendHeader;
1377 MessageHeader sReceiveHeader;
1378
1379 memset(&sSendHeader, 0, sizeof(MessageHeader));
1380
1381 if (m_eState == SS_Opening)
1382 {
1383#ifdef RIGHT_SIDE_COMPILE
1384 // The right side actually starts things off by sending a SessionRequest message.
1385
1386 SessionRequestData sDataBlock;
1387
1388 sSendHeader.m_eType = MT_SessionRequest;
1389 sSendHeader.TypeSpecificData.VersionInfo.m_dwMajorVersion = kCurrentMajorVersion;
1390 sSendHeader.TypeSpecificData.VersionInfo.m_dwMinorVersion = kCurrentMinorVersion;
1391
1392 // The start of the data block always contains a session ID. This is a GUID randomly generated at
1393 // Init() time.
1394 sSendHeader.m_cbDataBlock = sizeof(SessionRequestData);
1395 memcpy(&sDataBlock.m_sSessionID, &m_sSessionID, sizeof(m_sSessionID));
1396
1397 // Send the header block followed by the data block. For failures during SS_Opening we just close
1398 // the connection and retry from the beginning (the failing send will already have caused a
1399 // transition into SS_Opening_NC. No need to use the same resend logic that SS_Resync does, since
1400 // no user messages have been sent and we can simply recreate the SessionRequest.
1401 DbgTransportLog(LC_Session, "Sending 'SessionRequest'");
1402 DBG_TRANSPORT_INC_STAT(SentSessionRequest);
1403 if (!SendBlock((PBYTE)&sSendHeader, sizeof(MessageHeader)) ||
1404 !SendBlock((PBYTE)&sDataBlock, sSendHeader.m_cbDataBlock))
1405 HANDLE_TRANSIENT_ERROR();
1406
1407 // Wait for a reply.
1408 if (!ReceiveBlock((PBYTE)&sReceiveHeader, sizeof(MessageHeader)))
1409 HANDLE_TRANSIENT_ERROR();
1410
1411 DbgTransportLogMessageReceived(&sReceiveHeader);
1412
1413 // This should be either a SessionAccept or SessionReject. Any other message type will be treated
1414 // as a SessionReject (i.e. an unrecoverable failure that will leave the session in SS_Closed
1415 // permanently).
1416 if (sReceiveHeader.m_eType != MT_SessionAccept)
1417 {
1418 _ASSERTE(!"Unexpected response to SessionRequest");
1419 HANDLE_CRITICAL_ERROR();
1420 }
1421
1422 // Validate the SessionAccept.
1423 if (sReceiveHeader.TypeSpecificData.VersionInfo.m_dwMajorVersion != kCurrentMajorVersion ||
1424 sReceiveHeader.m_cbDataBlock != (DWORD)0)
1425 {
1426 _ASSERTE(!"Malformed SessionAccept received");
1427 HANDLE_CRITICAL_ERROR();
1428 }
1429
1430 // The LS might have negotiated the minor protocol version down.
1431 m_dwMinorVersion = sReceiveHeader.TypeSpecificData.VersionInfo.m_dwMinorVersion;
1432#else // RIGHT_SIDE_COMPILE
1433
1434 // On the left side we wait for a SessionRequest first.
1435 if (!ReceiveBlock((PBYTE)&sReceiveHeader, sizeof(MessageHeader)))
1436 HANDLE_TRANSIENT_ERROR();
1437
1438 DbgTransportLogMessageReceived(&sReceiveHeader);
1439
1440 if (sReceiveHeader.m_eType != MT_SessionRequest)
1441 {
1442 _ASSERTE(!"Unexpected message type");
1443 HANDLE_CRITICAL_ERROR();
1444 }
1445
1446 // Validate the SessionRequest.
1447 if (sReceiveHeader.TypeSpecificData.VersionInfo.m_dwMajorVersion != kCurrentMajorVersion ||
1448 sReceiveHeader.m_cbDataBlock != (DWORD)sizeof(SessionRequestData))
1449 {
1450 // Send a SessionReject message with the reason for rejection.
1451 sSendHeader.m_eType = MT_SessionReject;
1452 sSendHeader.TypeSpecificData.SessionReject.m_eReason = RR_IncompatibleVersion;
1453 sSendHeader.TypeSpecificData.SessionReject.m_dwMajorVersion = kCurrentMajorVersion;
1454 sSendHeader.TypeSpecificData.SessionReject.m_dwMinorVersion = kCurrentMinorVersion;
1455
1456 DbgTransportLog(LC_Session, "Sending 'SessionReject(RR_IncompatibleVersion)'");
1457 DBG_TRANSPORT_INC_STAT(SentSessionReject);
1458
1459 SendBlock((PBYTE)&sSendHeader, sizeof(MessageHeader));
1460
1461 // Go back into the opening state rather than closed because we want to give the RS a chance
1462 // to correct the problem and try again.
1463 HANDLE_TRANSIENT_ERROR();
1464 }
1465
1466 // Read the data block.
1467 SessionRequestData sDataBlock;
1468 if (!ReceiveBlock((PBYTE)&sDataBlock, sizeof(SessionRequestData)))
1469 HANDLE_TRANSIENT_ERROR();
1470
1471 // If the RS only understands a lower minor protocol version than us then remember that fact.
1472 if (sReceiveHeader.TypeSpecificData.VersionInfo.m_dwMinorVersion < m_dwMinorVersion)
1473 m_dwMinorVersion = sReceiveHeader.TypeSpecificData.VersionInfo.m_dwMinorVersion;
1474
1475 // Send a SessionAccept message back.
1476 sSendHeader.m_eType = MT_SessionAccept;
1477 sSendHeader.m_cbDataBlock = 0;
1478 sSendHeader.TypeSpecificData.VersionInfo.m_dwMajorVersion = kCurrentMajorVersion;
1479 sSendHeader.TypeSpecificData.VersionInfo.m_dwMinorVersion = m_dwMinorVersion;
1480
1481 DbgTransportLog(LC_Session, "Sending 'SessionAccept'");
1482 DBG_TRANSPORT_INC_STAT(SentSessionAccept);
1483
1484 if (!SendBlock((PBYTE)&sSendHeader, sizeof(MessageHeader)))
1485 HANDLE_TRANSIENT_ERROR();
1486#endif // RIGHT_SIDE_COMPILE
1487
1488 // Everything pans out, we have a session formed. But we must send messages that queued up
1489 // before transitioning the state to open (otherwise a racing send could sneak in ahead).
1490
1491 // Must access the send queue under the state lock.
1492 {
1493 TransportLockHolder sLockHolder(&m_sStateLock);
1494 Message *pMsg = m_pSendQueueFirst;
1495 while (pMsg)
1496 {
1497 if (SendBlock((PBYTE)&pMsg->m_sHeader, sizeof(MessageHeader)) && pMsg->m_pbDataBlock)
1498 SendBlock(pMsg->m_pbDataBlock, pMsg->m_cbDataBlock);
1499 pMsg = pMsg->m_pNext;
1500 }
1501
1502 // Check none of the sends failed.
1503 if (m_eState != SS_Opening)
1504 {
1505 m_pipe.Disconnect();
1506 continue;
1507 }
1508 } // Leave m_sStateLock
1509
1510 // Finally we can transition to SS_Open.
1511 {
1512 TransportLockHolder sLockHolder(&m_sStateLock);
1513 if (m_eState == SS_Closed)
1514 break;
1515 else if (m_eState == SS_Opening)
1516 m_eState = SS_Open;
1517 else
1518 _ASSERTE(!"Bad session state");
1519 } // Leave m_sStateLock
1520
1521#ifdef RIGHT_SIDE_COMPILE
1522 // Signal any WaitForSessionToOpen() waiters that we've gotten to SS_Open.
1523 SetEvent(m_hSessionOpenEvent);
1524#endif // RIGHT_SIDE_COMPILE
1525
1526 // We're ready to begin receiving normal incoming messages now.
1527 }
1528 else
1529 {
1530 // The SS_Resync case. Send a message indicating the last message we saw from the other side and
1531 // wait for a similar message to arrive for us.
1532
1533 sSendHeader.m_eType = MT_SessionResync;
1534 sSendHeader.m_dwLastSeenId = m_dwLastMessageIdSeen;
1535
1536 DbgTransportLog(LC_Session, "Sending 'SessionResync'");
1537 DBG_TRANSPORT_INC_STAT(SentSessionResync);
1538
1539 if (!SendBlock((PBYTE)&sSendHeader, sizeof(MessageHeader)))
1540 HANDLE_TRANSIENT_ERROR();
1541
1542 if (!ReceiveBlock((PBYTE)&sReceiveHeader, sizeof(MessageHeader)))
1543 HANDLE_TRANSIENT_ERROR();
1544
1545#ifndef RIGHT_SIDE_COMPILE
1546 if (sReceiveHeader.m_eType == MT_SessionRequest)
1547 {
1548 DbgTransportLogMessageReceived(&sReceiveHeader);
1549
1550 // This SessionRequest could be from a different debugger. In this case we should send a
1551 // SessionReject to let them know we're not available and close the connection so we can
1552 // re-listen for the original debugger.
1553 // Or it could be the original debugger re-sending the SessionRequest because the connection
1554 // died as we sent the SessionAccept.
1555 // We distinguish the two cases by looking at the session ID in the request.
1556 bool fRequestResend = false;
1557
1558 // Only read the data block if it matches our expectations of its size.
1559 if (sReceiveHeader.m_cbDataBlock == (DWORD)sizeof(SessionRequestData))
1560 {
1561 SessionRequestData sDataBlock;
1562 if (!ReceiveBlock((PBYTE)&sDataBlock, sizeof(SessionRequestData)))
1563 HANDLE_TRANSIENT_ERROR();
1564
1565 // Check the session ID for a match.
1566 if (memcmp(&sDataBlock.m_sSessionID, &m_sSessionID, sizeof(m_sSessionID)) == 0)
1567 // OK, everything checks out and this is a valid re-send of a SessionRequest.
1568 fRequestResend = true;
1569 }
1570
1571 if (fRequestResend)
1572 {
1573 // The RS never got our SessionAccept. We must resend it.
1574 memset(&sSendHeader, 0, sizeof(MessageHeader));
1575 sSendHeader.m_eType = MT_SessionAccept;
1576 sSendHeader.m_cbDataBlock = 0;
1577 sSendHeader.TypeSpecificData.VersionInfo.m_dwMajorVersion = kCurrentMajorVersion;
1578 sSendHeader.TypeSpecificData.VersionInfo.m_dwMinorVersion = m_dwMinorVersion;
1579
1580 DbgTransportLog(LC_Session, "Sending 'SessionAccept'");
1581 DBG_TRANSPORT_INC_STAT(SentSessionAccept);
1582
1583 if (!SendBlock((PBYTE)&sSendHeader, sizeof(MessageHeader)))
1584 HANDLE_TRANSIENT_ERROR();
1585
1586 // Now simply reset the connection. The RS should get the SessionAccept and transition to
1587 // SS_Open then detect the connection loss and transition to SS_Resync_NC, which will
1588 // finally sync the two sides.
1589 HANDLE_TRANSIENT_ERROR();
1590 }
1591 else
1592 {
1593 // This is the case where we must reject the request.
1594 memset(&sSendHeader, 0, sizeof(MessageHeader));
1595 sSendHeader.m_eType = MT_SessionReject;
1596 sSendHeader.TypeSpecificData.SessionReject.m_eReason = RR_AlreadyAttached;
1597 sSendHeader.TypeSpecificData.SessionReject.m_dwMajorVersion = kCurrentMajorVersion;
1598 sSendHeader.TypeSpecificData.SessionReject.m_dwMinorVersion = kCurrentMinorVersion;
1599
1600 DbgTransportLog(LC_Session, "Sending 'SessionReject(RR_AlreadyAttached)'");
1601 DBG_TRANSPORT_INC_STAT(SentSessionReject);
1602
1603 SendBlock((PBYTE)&sSendHeader, sizeof(MessageHeader));
1604
1605 HANDLE_TRANSIENT_ERROR();
1606 }
1607 }
1608#endif // !RIGHT_SIDE_COMPILE
1609
1610 DbgTransportLogMessageReceived(&sReceiveHeader);
1611
1612 // Handle all other invalid message types by shutting down (it may be an attempt to subvert the
1613 // protocol).
1614 if (sReceiveHeader.m_eType != MT_SessionResync)
1615 {
1616 _ASSERTE(!"Unexpected message type during SS_Resync");
1617 HANDLE_CRITICAL_ERROR();
1618 }
1619
1620 // We've got our resync message. Go through the send queue and resend any messages that haven't
1621 // been processed by the other side. Those that have been processed can be discarded (unless
1622 // they're waiting for another form of higher level acknowledgement, such as a reply message).
1623
1624 // Discard unneeded messages first.
1625 FlushSendQueue(sReceiveHeader.m_dwLastSeenId);
1626
1627 // Must access the send queue under the state lock.
1628 {
1629 TransportLockHolder sLockHolder(&m_sStateLock);
1630
1631 Message *pMsg = m_pSendQueueFirst;
1632 while (pMsg)
1633 {
1634 if (pMsg->m_sHeader.m_dwId > sReceiveHeader.m_dwLastSeenId)
1635 {
1636 // The other side never saw this message, re-send it.
1637 DBG_TRANSPORT_INC_STAT(Resends);
1638 if (SendBlock((PBYTE)&pMsg->m_sHeader, sizeof(MessageHeader)) && pMsg->m_pbDataBlock)
1639 SendBlock(pMsg->m_pbDataBlock, pMsg->m_cbDataBlock);
1640 }
1641 pMsg = pMsg->m_pNext;
1642 }
1643
1644 // Finished processing queued sends. We can transition to the SS_Open state now as long as there
1645 // wasn't a send failure or an asynchronous Shutdown().
1646 if (m_eState == SS_Resync)
1647 m_eState = SS_Open;
1648 else if (m_eState == SS_Closed)
1649 break;
1650 else if (m_eState == SS_Resync_NC)
1651 {
1652 m_pipe.Disconnect();
1653 continue;
1654 }
1655 else
1656 _ASSERTE(!"Bad session state");
1657 } // Leave m_sStateLock
1658 }
1659
1660 // Once we get here we should be in SS_Open (can't assert this because Shutdown() can throw the state
1661 // into SS_Closed and we've just released SendMessage() calls on other threads that can transition us
1662 // into SS_Resync).
1663
1664 // We now loop receiving messages and processing them until the state changes.
1665 while (m_eState == SS_Open)
1666 {
1667 // temporary data block used in DCB messages
1668 DebuggerIPCControlBlockTransport dcbt;
1669
1670 // temporary virtual stack unwind context buffer
1671 CONTEXT frameContext;
1672
1673 // Read a message header block.
1674 if (!ReceiveBlock((PBYTE)&sReceiveHeader, sizeof(MessageHeader)))
1675 HANDLE_TRANSIENT_ERROR();
1676
1677 // Since we care about security here, perform some additional validation checks that make it
1678 // harder for a malicious sender to attack with random message data.
1679 if (sReceiveHeader.m_eType > MT_GetAppDomainCB ||
1680 (sReceiveHeader.m_dwId <= m_dwLastMessageIdSeen &&
1681 sReceiveHeader.m_dwId != (DWORD)0) ||
1682 (sReceiveHeader.m_dwReplyId >= m_dwNextMessageId &&
1683 sReceiveHeader.m_dwReplyId != (DWORD)0) ||
1684 (sReceiveHeader.m_dwLastSeenId >= m_dwNextMessageId &&
1685 sReceiveHeader.m_dwLastSeenId != (DWORD)0))
1686 {
1687 _ASSERTE(!"Incoming message header looks bogus");
1688 HANDLE_CRITICAL_ERROR();
1689 }
1690
1691 DbgTransportLogMessageReceived(&sReceiveHeader);
1692
1693 // Flush any entries in our send queue for messages that the other side has just confirmed
1694 // processed with this message.
1695 FlushSendQueue(sReceiveHeader.m_dwLastSeenId);
1696
1697#ifndef RIGHT_SIDE_COMPILE
1698 // State variables to track whether this message needs a reply and if so whether it consists of a
1699 // header only or a header and an optional data block.
1700 bool fReplyRequired = false;
1701 PBYTE pbOptReplyData = NULL;
1702 DWORD cbOptReplyData = 0;
1703 HRESULT hr = E_FAIL;
1704
1705 // if you change the lifetime of resultBuffer, make sure you change pbOptReplyData to match.
1706 // In some cases pbOptReplyData will point at the memory held alive in resultBuffer
1707 WriteBuffer resultBuffer;
1708 ReadBuffer receiveBuffer;
1709
1710#endif // RIGHT_SIDE_COMPILE
1711
1712 // Dispatch based on message type.
1713 //
1714 // **** IMPORTANT NOTE ****
1715 //
1716 // We must be very careful wrt to updating m_dwLastMessageIdSeen here. If we update it too soon
1717 // (we haven't finished receiving the entire message, for instance) then the other side won't
1718 // re-send the message on failure and we'll lose it. If we update it too late we might have
1719 // reported the message to our caller or produced any other side-effect we can't take back such as
1720 // sending a reply and then hit an error and reset the connection before we had a chance to record
1721 // the message as seen. In this case the other side will re-send the original message and we'll
1722 // repeat our actions, which is also very bad.
1723 //
1724 // So we must be very disciplined here.
1725 //
1726 // First we must read the message in its entirety (i.e. receive the data block if there is one)
1727 // without causing any side-effects. This ensures that any failure at this point will be handled
1728 // correctly (by the other side re-sending us the same message).
1729 //
1730 // Then we process the message. At this point we are committed. The processing must always
1731 // succeed, or have no side-effect (that we care about) or we must have an additional scheme to
1732 // handle resynchronization in the event of failure. This ensures that we don't have the tricky
1733 // situation where we can't cope with a re-send of the message (because we've started processing
1734 // it) but can't report a failure to the other side (because we don't know how).
1735 //
1736 // Finally we must ensure that there is no error path between the completion of processing and
1737 // updating the m_dwLastMessageIdSeen field. This ensures we don't accidently get re-sent a
1738 // message we've processed completely (it's really just a sub-case of the rule above, but it's
1739 // worth pointing out explicitly since it can be a subtle problem).
1740 //
1741 // Request messages (such as MT_GetDCB) are an interesting case in point here. They all require a
1742 // reply and we can fail on the reply because we run out of system resources. This breaks the
1743 // second rule above (we fail halfway through processing). We should really preallocate enough
1744 // resources to send the reply before we begin processing of it but for now we don't since (a) the
1745 // SendMessage system isn't currently set up to make this easy and (b) we happen to know that all
1746 // the request types are effectively idempotent (even ReadMemory and WriteMemory since the RS is
1747 // holding the LS still while it does these). So instead we must carefully distinguish the case
1748 // where SendMessage fails without possibility of message transmission (e.g. out of memory) and
1749 // those where it fails for a transient network failure (where it will re-send the reply on
1750 // resync). This is easy enough to do since SendMessage returns a failure hresult for the first
1751 // case and success (and a state transition) for the second. In the first case we don't update
1752 // m_dwLastMessageIdSeen and instead wait for the request to be resent. In the second we make the
1753 // update because we know the reply will get through eventually.
1754 //
1755 // **** IMPORTANT NOTE ****
1756 switch (sReceiveHeader.m_eType)
1757 {
1758 case MT_SessionRequest:
1759 case MT_SessionAccept:
1760 case MT_SessionReject:
1761 case MT_SessionResync:
1762 // Illegal messages at this time, fail the transport entirely.
1763 m_eState = SS_Closed;
1764 break;
1765
1766 case MT_SessionClose:
1767 // Close is legal on the LS and transitions to the SS_Opening_NC state. It's illegal on the RS
1768 // and should shutdown the transport.
1769#ifdef RIGHT_SIDE_COMPILE
1770 m_eState = SS_Closed;
1771 break;
1772#else // RIGHT_SIDE_COMPILE
1773 // We need to do some state cleanup here, since when we reform a connection (if ever, it will
1774 // be with a new session).
1775 {
1776 TransportLockHolder sLockHolder(&m_sStateLock);
1777
1778 // Check we're still in a good state before a clean restart.
1779 if (m_eState != SS_Open)
1780 {
1781 m_eState = SS_Closed;
1782 break;
1783 }
1784
1785 m_pipe.Disconnect();
1786
1787 // We could add code to drain the send queue here (like we have for SS_Closed at the end of
1788 // this method) but I'm pretty sure we can only get a graceful session close with no
1789 // outstanding sends. So just assert the queue is empty instead. If the assert fires and it's
1790 // not due to an issue we can add the logic here).
1791 _ASSERTE(m_pSendQueueFirst == NULL);
1792 _ASSERTE(m_pSendQueueLast == NULL);
1793
1794 // This will reset all session specific state and transition us to SS_Opening_NC.
1795 InitSessionState();
1796 } // Leave m_sStateLock
1797
1798 goto ResetConnection;
1799#endif // RIGHT_SIDE_COMPILE
1800
1801 case MT_Event:
1802 {
1803 // Incoming debugger event.
1804
1805 if (sReceiveHeader.m_cbDataBlock > CorDBIPC_BUFFER_SIZE)
1806 {
1807 _ASSERTE(!"Oversized Event");
1808 HANDLE_CRITICAL_ERROR();
1809 }
1810
1811 // See if our array of buffered events has filled up. If so we'll need to re-allocate the
1812 // array to expand it.
1813 if (m_cValidEventBuffers == m_cEventBuffers)
1814 {
1815 // Allocate a larger array.
1816 DWORD cNewEntries = m_cEventBuffers + 4;
1817 DbgEventBufferEntry * pNewBuffers = (DbgEventBufferEntry *)new (nothrow) BYTE[cNewEntries * sizeof(DbgEventBufferEntry)];
1818 if (pNewBuffers == NULL)
1819 HANDLE_TRANSIENT_ERROR();
1820
1821 // We must take the lock to swap the new array in. Although this thread is the only one
1822 // that can expand the array, a client thread may be in GetNextEvent() reading from the
1823 // old version.
1824 {
1825 TransportLockHolder sLockHolder(&m_sStateLock);
1826
1827 // When we copy old array contents over we place the head of the list at the start of
1828 // the new array for simplicity. If the head happened to be at the start of the old
1829 // array anyway, this is even simpler.
1830 if (m_idxEventBufferHead == 0)
1831 memcpy(pNewBuffers, m_pEventBuffers, m_cEventBuffers * sizeof(DbgEventBufferEntry));
1832 else
1833 {
1834 // Otherwise we need to perform the copy in two segments: first we copy the head
1835 // of the list (starts at a non-zero index and runs to the end of the old array)
1836 // into the start of the new array.
1837 DWORD cHeadEntries = m_cEventBuffers - m_idxEventBufferHead;
1838
1839 memcpy(pNewBuffers,
1840 &m_pEventBuffers[m_idxEventBufferHead],
1841 cHeadEntries * sizeof(DbgEventBufferEntry));
1842
1843 // Then we copy the remaining portion from the beginning of the old array upto to
1844 // the index of the head.
1845 memcpy(&pNewBuffers[cHeadEntries],
1846 m_pEventBuffers,
1847 m_idxEventBufferHead * sizeof(DbgEventBufferEntry));
1848 }
1849
1850 // Delete the old array.
1851 delete [] m_pEventBuffers;
1852
1853 // Swap the new array in.
1854 m_pEventBuffers = pNewBuffers;
1855 m_cEventBuffers = cNewEntries;
1856
1857 // The new array now has the head at index zero and the tail at the start of the
1858 // new entries.
1859 m_idxEventBufferHead = 0;
1860 m_idxEventBufferTail = m_cValidEventBuffers;
1861 }
1862 }
1863
1864 // We have at least one free buffer at this point (no threading issues, the only thread that
1865 // can add entries is this one).
1866
1867 // Receive event data into the tail buffer (we want to do this without holding the state lock
1868 // and can do so safely since this is the only thread that can receive data and clients can do
1869 // nothing that impacts the location of the tail of the buffer list).
1870 if (!ReceiveBlock((PBYTE)&m_pEventBuffers[m_idxEventBufferTail].m_event, sReceiveHeader.m_cbDataBlock))
1871 HANDLE_TRANSIENT_ERROR();
1872
1873 {
1874 m_pEventBuffers[m_idxEventBufferTail].m_type = sReceiveHeader.TypeSpecificData.Event.m_eIPCEventType;
1875
1876 // We must take the lock to update the count of valid entries though, since clients can
1877 // touch this field as well.
1878 TransportLockHolder sLockHolder(&m_sStateLock);
1879
1880 m_cValidEventBuffers++;
1881 DWORD idxCurrentEvent = m_idxEventBufferTail;
1882
1883 // Update tail of the list (strictly speaking this needn't be done under the lock, but the
1884 // code in GetNextEvent() does read it for an assert.
1885 m_idxEventBufferTail = (m_idxEventBufferTail + 1) % m_cEventBuffers;
1886
1887 // If we just added the first valid event then wake up the client so they can call
1888 // GetNextEvent().
1889 if (m_cValidEventBuffers == 1)
1890 SetEvent(m_rghEventReadyEvent[m_pEventBuffers[idxCurrentEvent].m_type]);
1891 }
1892 }
1893 break;
1894
1895 case MT_ReadMemory:
1896#ifdef RIGHT_SIDE_COMPILE
1897 if (!ProcessReply(&sReceiveHeader))
1898 HANDLE_TRANSIENT_ERROR();
1899#else // RIGHT_SIDE_COMPILE
1900 // The RS wants to read our memory. First check the range requested is both committed and
1901 // readable. If that succeeds we simply set the optional reply block to match the request region
1902 // (i.e. we send the memory directly).
1903 fReplyRequired = true;
1904
1905 hr = CheckBufferAccess(sReceiveHeader.TypeSpecificData.MemoryAccess.m_pbLeftSideBuffer,
1906 sReceiveHeader.TypeSpecificData.MemoryAccess.m_cbLeftSideBuffer,
1907 false);
1908 sReceiveHeader.TypeSpecificData.MemoryAccess.m_hrResult = hr;
1909 if (SUCCEEDED(hr))
1910 {
1911 pbOptReplyData = sReceiveHeader.TypeSpecificData.MemoryAccess.m_pbLeftSideBuffer;
1912 cbOptReplyData = sReceiveHeader.TypeSpecificData.MemoryAccess.m_cbLeftSideBuffer;
1913 }
1914#endif // RIGHT_SIDE_COMPILE
1915 break;
1916
1917 case MT_WriteMemory:
1918#ifdef RIGHT_SIDE_COMPILE
1919 if (!ProcessReply(&sReceiveHeader))
1920 HANDLE_TRANSIENT_ERROR();
1921#else // RIGHT_SIDE_COMPILE
1922 // The RS wants to write our memory.
1923 if (sReceiveHeader.m_cbDataBlock != sReceiveHeader.TypeSpecificData.MemoryAccess.m_cbLeftSideBuffer)
1924 {
1925 _ASSERTE(!"Inconsistent WriteMemory request");
1926 HANDLE_CRITICAL_ERROR();
1927 }
1928
1929 fReplyRequired = true;
1930
1931 // Check the range requested is both committed and writeable. If that succeeds we simply read
1932 // the next incoming block into the destination buffer.
1933 hr = CheckBufferAccess(sReceiveHeader.TypeSpecificData.MemoryAccess.m_pbLeftSideBuffer,
1934 sReceiveHeader.TypeSpecificData.MemoryAccess.m_cbLeftSideBuffer,
1935 true);
1936 if (SUCCEEDED(hr))
1937 {
1938 if (!ReceiveBlock(sReceiveHeader.TypeSpecificData.MemoryAccess.m_pbLeftSideBuffer,
1939 sReceiveHeader.TypeSpecificData.MemoryAccess.m_cbLeftSideBuffer))
1940 HANDLE_TRANSIENT_ERROR();
1941 }
1942 else
1943 {
1944 sReceiveHeader.TypeSpecificData.MemoryAccess.m_hrResult = hr;
1945
1946 // We might be failing the write attempt but we still need to read the update data to
1947 // drain it from the connection or we'll become unsynchronized (i.e. we'll treat the start
1948 // of the write data as the next message header). So read and discard the data into a
1949 // dummy buffer.
1950 BYTE rgDummy[256];
1951 DWORD cbBytesToRead = sReceiveHeader.TypeSpecificData.MemoryAccess.m_cbLeftSideBuffer;
1952 while (cbBytesToRead)
1953 {
1954 DWORD cbTransfer = min(cbBytesToRead, sizeof(rgDummy));
1955 if (!ReceiveBlock(rgDummy, cbTransfer))
1956 HANDLE_TRANSIENT_ERROR();
1957 cbBytesToRead -= cbTransfer;
1958 }
1959 }
1960#endif // RIGHT_SIDE_COMPILE
1961 break;
1962
1963 case MT_VirtualUnwind:
1964#ifdef RIGHT_SIDE_COMPILE
1965 if (!ProcessReply(&sReceiveHeader))
1966 HANDLE_TRANSIENT_ERROR();
1967#else // RIGHT_SIDE_COMPILE
1968 if (sReceiveHeader.m_cbDataBlock != (DWORD)sizeof(frameContext))
1969 {
1970 _ASSERTE(!"Inconsistent VirtualUnwind request");
1971 HANDLE_CRITICAL_ERROR();
1972 }
1973
1974 if (!ReceiveBlock((PBYTE)&frameContext, sizeof(frameContext)))
1975 {
1976 HANDLE_TRANSIENT_ERROR();
1977 }
1978
1979 if (!PAL_VirtualUnwind(&frameContext, NULL))
1980 {
1981 HANDLE_TRANSIENT_ERROR();
1982 }
1983
1984 fReplyRequired = true;
1985 pbOptReplyData = (PBYTE)&frameContext;
1986 cbOptReplyData = sizeof(frameContext);
1987#endif // RIGHT_SIDE_COMPILE
1988 break;
1989
1990 case MT_GetDCB:
1991#ifdef RIGHT_SIDE_COMPILE
1992 if (!ProcessReply(&sReceiveHeader))
1993 HANDLE_TRANSIENT_ERROR();
1994#else // RIGHT_SIDE_COMPILE
1995 fReplyRequired = true;
1996 MarshalDCBToDCBTransport(m_pDCB, &dcbt);
1997 pbOptReplyData = (PBYTE)&dcbt;
1998 cbOptReplyData = sizeof(DebuggerIPCControlBlockTransport);
1999#endif // RIGHT_SIDE_COMPILE
2000 break;
2001
2002 case MT_SetDCB:
2003#ifdef RIGHT_SIDE_COMPILE
2004 if (!ProcessReply(&sReceiveHeader))
2005 HANDLE_TRANSIENT_ERROR();
2006#else // RIGHT_SIDE_COMPILE
2007 if (sReceiveHeader.m_cbDataBlock != (DWORD)sizeof(DebuggerIPCControlBlockTransport))
2008 {
2009 _ASSERTE(!"Inconsistent SetDCB request");
2010 HANDLE_CRITICAL_ERROR();
2011 }
2012
2013 fReplyRequired = true;
2014
2015 if (!ReceiveBlock((PBYTE)&dcbt, sizeof(DebuggerIPCControlBlockTransport)))
2016 HANDLE_TRANSIENT_ERROR();
2017
2018 MarshalDCBTransportToDCB(&dcbt, m_pDCB);
2019#endif // RIGHT_SIDE_COMPILE
2020 break;
2021
2022 case MT_GetAppDomainCB:
2023#ifdef RIGHT_SIDE_COMPILE
2024 if (!ProcessReply(&sReceiveHeader))
2025 HANDLE_TRANSIENT_ERROR();
2026#else // RIGHT_SIDE_COMPILE
2027 fReplyRequired = true;
2028 pbOptReplyData = (PBYTE)m_pADB;
2029 cbOptReplyData = sizeof(AppDomainEnumerationIPCBlock);
2030#endif // RIGHT_SIDE_COMPILE
2031 break;
2032
2033 default:
2034 _ASSERTE(!"Unknown message type");
2035 HANDLE_CRITICAL_ERROR();
2036 }
2037
2038#ifndef RIGHT_SIDE_COMPILE
2039 // On the left side we may need to send a reply back.
2040 if (fReplyRequired)
2041 {
2042 Message sReply;
2043 sReply.Init(sReceiveHeader.m_eType, pbOptReplyData, cbOptReplyData);
2044 sReply.m_sHeader.m_dwReplyId = sReceiveHeader.m_dwId;
2045 sReply.m_sHeader.TypeSpecificData = sReceiveHeader.TypeSpecificData;
2046
2047#ifdef _DEBUG
2048 DbgTransportLog(LC_Requests, "Sending '%s' reply", MessageName(sReceiveHeader.m_eType));
2049#endif // _DEBUG
2050
2051 // We must be careful with the failure mode of SendMessage here to avoid the same request
2052 // being processed too many or too few times. See the comment above starting with 'IMPORTANT
2053 // NOTE' for more details. The upshot is that on SendMessage hresult failures (which indicate
2054 // the message will never be sent), we don't update m_dwLastMessageIdSeen and simply wait for
2055 // the request to be made again. When we get success, however, we must be careful to ensure
2056 // that m_dwLastMessageIdSeen gets updated even if a network error is reported. Otherwise on
2057 // the resync we'll both reprocess the request and re-send the original reply which is very
2058 // very bad.
2059 hr = SendMessage(&sReply, false);
2060
2061 if (FAILED(hr))
2062 HANDLE_TRANSIENT_ERROR(); // Message will never be sent, other side will retry
2063
2064 // SendMessage doesn't report network errors (it simply queues the send and changes the
2065 // session state). So check for a network error here specifically so we can get started on the
2066 // resync. We must update m_dwLastMessageIdSeen first though, or the other side will retry the
2067 // request.
2068 if (m_eState != SS_Open)
2069 {
2070 _ASSERTE(sReceiveHeader.m_dwId > m_dwLastMessageIdSeen);
2071 m_dwLastMessageIdSeen = sReceiveHeader.m_dwId;
2072 HANDLE_TRANSIENT_ERROR();
2073 }
2074 }
2075#endif // !RIGHT_SIDE_COMPILE
2076
2077 if (sReceiveHeader.m_dwId != (DWORD)0)
2078 {
2079 // We've now completed processing on the incoming message. Remember we've processed up to this
2080 // message ID so that on a resync the other side doesn't send it to us again.
2081 _ASSERTE(sReceiveHeader.m_dwId > m_dwLastMessageIdSeen);
2082 m_dwLastMessageIdSeen = sReceiveHeader.m_dwId;
2083 }
2084 }
2085 }
2086
2087 Shutdown:
2088
2089 _ASSERTE(m_eState == SS_Closed);
2090
2091#ifdef RIGHT_SIDE_COMPILE
2092 // The session is definitely not open at this point.
2093 ResetEvent(m_hSessionOpenEvent);
2094#endif // RIGHT_SIDE_COMPILE
2095
2096 // Close the connection if we haven't done so already.
2097 m_pipe.Disconnect();
2098
2099 // Drain any remaining entries in the send queue (aborting them when they need completions).
2100 {
2101 TransportLockHolder sLockHolder(&m_sStateLock);
2102
2103 Message *pMsg;
2104 while ((pMsg = m_pSendQueueFirst) != NULL)
2105 {
2106 // Remove message from the queue.
2107 m_pSendQueueFirst = pMsg->m_pNext;
2108
2109 // Determine whether the message needs to be deleted by us before we signal any completion (because
2110 // once we signal the completion pMsg might become invalid immediately if it's not a copy).
2111 bool fMustDelete = pMsg->m_pOrigMessage != pMsg;
2112
2113 // If there's a waiter (i.e. we don't own the message) it know that the operation didn't really
2114 // complete, it was aborted.
2115 if (!fMustDelete)
2116 pMsg->m_pOrigMessage->m_fAborted = true;
2117
2118 // Determine how to complete the message.
2119 switch (pMsg->m_sHeader.m_eType)
2120 {
2121 case MT_SessionRequest:
2122 case MT_SessionAccept:
2123 case MT_SessionReject:
2124 case MT_SessionResync:
2125 case MT_SessionClose:
2126 _ASSERTE(!"Session management messages should not be on send queue");
2127 break;
2128
2129 case MT_Event:
2130 break;
2131
2132#ifdef RIGHT_SIDE_COMPILE
2133 case MT_ReadMemory:
2134 case MT_WriteMemory:
2135 case MT_VirtualUnwind:
2136 case MT_GetDCB:
2137 case MT_SetDCB:
2138 case MT_GetAppDomainCB:
2139 // On the RS these are the original requests. Signal the completion event.
2140 SignalReplyEvent(pMsg);
2141 break;
2142#else // RIGHT_SIDE_COMPILE
2143 case MT_ReadMemory:
2144 case MT_WriteMemory:
2145 case MT_VirtualUnwind:
2146 case MT_GetDCB:
2147 case MT_SetDCB:
2148 case MT_GetAppDomainCB:
2149 // On the LS these are replies to the original request. Nobody's waiting on these.
2150 break;
2151#endif // RIGHT_SIDE_COMPILE
2152
2153 default:
2154 _ASSERTE(!"Unknown message type");
2155 }
2156
2157 // If the message was a copy, deallocate the resources now.
2158 if (fMustDelete)
2159 {
2160 if (pMsg->m_pbDataBlock)
2161 delete [] pMsg->m_pbDataBlock;
2162 delete pMsg;
2163 }
2164 }
2165 } // Leave m_sStateLock
2166
2167 // Now release all the resources allocated for the transport now that the
2168 // worker thread isn't using them anymore.
2169 Release();
2170}
2171
2172// Given a fully initialized debugger event structure, return the size of the structure in bytes (this is not
2173// trivial since DebuggerIPCEvent contains a large union member which can cause the portion containing
2174// significant data to vary wildy from event to event).
2175DWORD DbgTransportSession::GetEventSize(DebuggerIPCEvent *pEvent)
2176{
2177 DWORD cbBaseSize = offsetof(DebuggerIPCEvent, LeftSideStartupData);
2178 DWORD cbAdditionalSize = 0;
2179
2180 switch (pEvent->type & DB_IPCE_TYPE_MASK)
2181 {
2182 case DB_IPCE_SYNC_COMPLETE:
2183 case DB_IPCE_THREAD_ATTACH:
2184 case DB_IPCE_THREAD_DETACH:
2185 case DB_IPCE_USER_BREAKPOINT:
2186 case DB_IPCE_EXIT_APP_DOMAIN:
2187 case DB_IPCE_SET_DEBUG_STATE_RESULT:
2188 case DB_IPCE_FUNC_EVAL_ABORT_RESULT:
2189 case DB_IPCE_CONTROL_C_EVENT:
2190 case DB_IPCE_FUNC_EVAL_CLEANUP_RESULT:
2191 case DB_IPCE_SET_METHOD_JMC_STATUS_RESULT:
2192 case DB_IPCE_SET_MODULE_JMC_STATUS_RESULT:
2193 case DB_IPCE_FUNC_EVAL_RUDE_ABORT_RESULT:
2194 case DB_IPCE_INTERCEPT_EXCEPTION_RESULT:
2195 case DB_IPCE_INTERCEPT_EXCEPTION_COMPLETE:
2196 case DB_IPCE_CREATE_PROCESS:
2197 case DB_IPCE_SET_NGEN_COMPILER_FLAGS_RESULT:
2198 case DB_IPCE_LEFTSIDE_STARTUP:
2199 case DB_IPCE_ASYNC_BREAK:
2200 case DB_IPCE_CONTINUE:
2201 case DB_IPCE_ATTACHING:
2202 case DB_IPCE_GET_NGEN_COMPILER_FLAGS:
2203 case DB_IPCE_DETACH_FROM_PROCESS:
2204 case DB_IPCE_CONTROL_C_EVENT_RESULT:
2205 case DB_IPCE_BEFORE_GARBAGE_COLLECTION:
2206 case DB_IPCE_AFTER_GARBAGE_COLLECTION:
2207 cbAdditionalSize = 0;
2208 break;
2209 case DB_IPCE_DATA_BREAKPOINT:
2210 cbAdditionalSize = sizeof(pEvent->DataBreakpointData);
2211 break;
2212
2213 case DB_IPCE_BREAKPOINT:
2214 cbAdditionalSize = sizeof(pEvent->BreakpointData);
2215 break;
2216
2217 case DB_IPCE_LOAD_MODULE:
2218 cbAdditionalSize = sizeof(pEvent->LoadModuleData);
2219 break;
2220
2221 case DB_IPCE_UNLOAD_MODULE:
2222 cbAdditionalSize = sizeof(pEvent->UnloadModuleData);
2223 break;
2224
2225 case DB_IPCE_LOAD_CLASS:
2226 cbAdditionalSize = sizeof(pEvent->LoadClass);
2227 break;
2228
2229 case DB_IPCE_UNLOAD_CLASS:
2230 cbAdditionalSize = sizeof(pEvent->UnloadClass);
2231 break;
2232
2233 case DB_IPCE_EXCEPTION:
2234 cbAdditionalSize = sizeof(pEvent->Exception);
2235 break;
2236
2237 case DB_IPCE_BREAKPOINT_ADD_RESULT:
2238 cbAdditionalSize = sizeof(pEvent->BreakpointData);
2239 break;
2240
2241 case DB_IPCE_STEP_RESULT:
2242 cbAdditionalSize = sizeof(pEvent->StepData);
2243 if (pEvent->StepData.rangeCount)
2244 cbAdditionalSize += (pEvent->StepData.rangeCount - 1) * sizeof(COR_DEBUG_STEP_RANGE);
2245 break;
2246
2247 case DB_IPCE_STEP_COMPLETE:
2248 cbAdditionalSize = sizeof(pEvent->StepData);
2249 break;
2250
2251 case DB_IPCE_GET_BUFFER_RESULT:
2252 cbAdditionalSize = sizeof(pEvent->GetBufferResult);
2253 break;
2254
2255 case DB_IPCE_RELEASE_BUFFER_RESULT:
2256 cbAdditionalSize = sizeof(pEvent->ReleaseBufferResult);
2257 break;
2258
2259 case DB_IPCE_ENC_ADD_FIELD:
2260 cbAdditionalSize = sizeof(pEvent->EnCUpdate);
2261 break;
2262
2263 case DB_IPCE_APPLY_CHANGES_RESULT:
2264 cbAdditionalSize = sizeof(pEvent->ApplyChangesResult);
2265 break;
2266
2267 case DB_IPCE_FIRST_LOG_MESSAGE:
2268 cbAdditionalSize = sizeof(pEvent->FirstLogMessage);
2269 break;
2270
2271 case DB_IPCE_LOGSWITCH_SET_MESSAGE:
2272 cbAdditionalSize = sizeof(pEvent->LogSwitchSettingMessage);
2273 break;
2274
2275 case DB_IPCE_CREATE_APP_DOMAIN:
2276 cbAdditionalSize = sizeof(pEvent->AppDomainData);
2277 break;
2278
2279 case DB_IPCE_LOAD_ASSEMBLY:
2280 cbAdditionalSize = sizeof(pEvent->AssemblyData);
2281 break;
2282
2283 case DB_IPCE_UNLOAD_ASSEMBLY:
2284 cbAdditionalSize = sizeof(pEvent->AssemblyData);
2285 break;
2286
2287 case DB_IPCE_FUNC_EVAL_SETUP_RESULT:
2288 cbAdditionalSize = sizeof(pEvent->FuncEvalSetupComplete);
2289 break;
2290
2291 case DB_IPCE_FUNC_EVAL_COMPLETE:
2292 cbAdditionalSize = sizeof(pEvent->FuncEvalComplete);
2293 break;
2294
2295 case DB_IPCE_SET_REFERENCE_RESULT:
2296 cbAdditionalSize = sizeof(pEvent->SetReference);
2297 break;
2298
2299 case DB_IPCE_NAME_CHANGE:
2300 cbAdditionalSize = sizeof(pEvent->NameChange);
2301 break;
2302
2303 case DB_IPCE_UPDATE_MODULE_SYMS:
2304 cbAdditionalSize = sizeof(pEvent->UpdateModuleSymsData);
2305 break;
2306
2307 case DB_IPCE_ENC_REMAP:
2308 cbAdditionalSize = sizeof(pEvent->EnCRemap);
2309 break;
2310
2311 case DB_IPCE_SET_VALUE_CLASS_RESULT:
2312 cbAdditionalSize = sizeof(pEvent->SetValueClass);
2313 break;
2314
2315 case DB_IPCE_BREAKPOINT_SET_ERROR:
2316 cbAdditionalSize = sizeof(pEvent->BreakpointSetErrorData);
2317 break;
2318
2319 case DB_IPCE_ENC_UPDATE_FUNCTION:
2320 cbAdditionalSize = sizeof(pEvent->EnCUpdate);
2321 break;
2322
2323 case DB_IPCE_GET_METHOD_JMC_STATUS_RESULT:
2324 cbAdditionalSize = sizeof(pEvent->SetJMCFunctionStatus);
2325 break;
2326
2327 case DB_IPCE_GET_THREAD_FOR_TASKID_RESULT:
2328 cbAdditionalSize = sizeof(pEvent->GetThreadForTaskIdResult);
2329 break;
2330
2331 case DB_IPCE_CREATE_CONNECTION:
2332 cbAdditionalSize = sizeof(pEvent->CreateConnection);
2333 break;
2334
2335 case DB_IPCE_DESTROY_CONNECTION:
2336 cbAdditionalSize = sizeof(pEvent->ConnectionChange);
2337 break;
2338
2339 case DB_IPCE_CHANGE_CONNECTION:
2340 cbAdditionalSize = sizeof(pEvent->ConnectionChange);
2341 break;
2342
2343 case DB_IPCE_EXCEPTION_CALLBACK2:
2344 cbAdditionalSize = sizeof(pEvent->ExceptionCallback2);
2345 break;
2346
2347 case DB_IPCE_EXCEPTION_UNWIND:
2348 cbAdditionalSize = sizeof(pEvent->ExceptionUnwind);
2349 break;
2350
2351 case DB_IPCE_CREATE_HANDLE_RESULT:
2352 cbAdditionalSize = sizeof(pEvent->CreateHandleResult);
2353 break;
2354
2355 case DB_IPCE_ENC_REMAP_COMPLETE:
2356 cbAdditionalSize = sizeof(pEvent->EnCRemapComplete);
2357 break;
2358
2359 case DB_IPCE_ENC_ADD_FUNCTION:
2360 cbAdditionalSize = sizeof(pEvent->EnCUpdate);
2361 break;
2362
2363 case DB_IPCE_GET_NGEN_COMPILER_FLAGS_RESULT:
2364 cbAdditionalSize = sizeof(pEvent->JitDebugInfo);
2365 break;
2366
2367 case DB_IPCE_MDA_NOTIFICATION:
2368 cbAdditionalSize = sizeof(pEvent->MDANotification);
2369 break;
2370
2371 case DB_IPCE_GET_GCHANDLE_INFO_RESULT:
2372 cbAdditionalSize = sizeof(pEvent->GetGCHandleInfoResult);
2373 break;
2374
2375 case DB_IPCE_SET_IP:
2376 cbAdditionalSize = sizeof(pEvent->SetIP);
2377 break;
2378
2379 case DB_IPCE_BREAKPOINT_ADD:
2380 cbAdditionalSize = sizeof(pEvent->BreakpointData);
2381 break;
2382
2383 case DB_IPCE_BREAKPOINT_REMOVE:
2384 cbAdditionalSize = sizeof(pEvent->BreakpointData);
2385 break;
2386
2387 case DB_IPCE_STEP_CANCEL:
2388 cbAdditionalSize = sizeof(pEvent->StepData);
2389 break;
2390
2391 case DB_IPCE_STEP:
2392 cbAdditionalSize = sizeof(pEvent->StepData);
2393 if (pEvent->StepData.rangeCount)
2394 cbAdditionalSize += (pEvent->StepData.rangeCount - 1) * sizeof(COR_DEBUG_STEP_RANGE);
2395 break;
2396
2397 case DB_IPCE_STEP_OUT:
2398 cbAdditionalSize = sizeof(pEvent->StepData);
2399 break;
2400
2401 case DB_IPCE_GET_BUFFER:
2402 cbAdditionalSize = sizeof(pEvent->GetBuffer);
2403 break;
2404
2405 case DB_IPCE_RELEASE_BUFFER:
2406 cbAdditionalSize = sizeof(pEvent->ReleaseBuffer);
2407 break;
2408
2409 case DB_IPCE_SET_CLASS_LOAD_FLAG:
2410 cbAdditionalSize = sizeof(pEvent->SetClassLoad);
2411 break;
2412
2413 case DB_IPCE_APPLY_CHANGES:
2414 cbAdditionalSize = sizeof(pEvent->ApplyChanges);
2415 break;
2416
2417 case DB_IPCE_SET_NGEN_COMPILER_FLAGS:
2418 cbAdditionalSize = sizeof(pEvent->JitDebugInfo);
2419 break;
2420
2421 case DB_IPCE_IS_TRANSITION_STUB:
2422 cbAdditionalSize = sizeof(pEvent->IsTransitionStub);
2423 break;
2424
2425 case DB_IPCE_IS_TRANSITION_STUB_RESULT:
2426 cbAdditionalSize = sizeof(pEvent->IsTransitionStubResult);
2427 break;
2428
2429 case DB_IPCE_MODIFY_LOGSWITCH:
2430 cbAdditionalSize = sizeof(pEvent->LogSwitchSettingMessage);
2431 break;
2432
2433 case DB_IPCE_ENABLE_LOG_MESSAGES:
2434 cbAdditionalSize = sizeof(pEvent->LogSwitchSettingMessage);
2435 break;
2436
2437 case DB_IPCE_FUNC_EVAL:
2438 cbAdditionalSize = sizeof(pEvent->FuncEval);
2439 break;
2440
2441 case DB_IPCE_SET_REFERENCE:
2442 cbAdditionalSize = sizeof(pEvent->SetReference);
2443 break;
2444
2445 case DB_IPCE_FUNC_EVAL_ABORT:
2446 cbAdditionalSize = sizeof(pEvent->FuncEvalAbort);
2447 break;
2448
2449 case DB_IPCE_FUNC_EVAL_CLEANUP:
2450 cbAdditionalSize = sizeof(pEvent->FuncEvalCleanup);
2451 break;
2452
2453 case DB_IPCE_SET_ALL_DEBUG_STATE:
2454 cbAdditionalSize = sizeof(pEvent->SetAllDebugState);
2455 break;
2456
2457 case DB_IPCE_SET_VALUE_CLASS:
2458 cbAdditionalSize = sizeof(pEvent->SetValueClass);
2459 break;
2460
2461 case DB_IPCE_SET_METHOD_JMC_STATUS:
2462 cbAdditionalSize = sizeof(pEvent->SetJMCFunctionStatus);
2463 break;
2464
2465 case DB_IPCE_GET_METHOD_JMC_STATUS:
2466 cbAdditionalSize = sizeof(pEvent->SetJMCFunctionStatus);
2467 break;
2468
2469 case DB_IPCE_SET_MODULE_JMC_STATUS:
2470 cbAdditionalSize = sizeof(pEvent->SetJMCFunctionStatus);
2471 break;
2472
2473 case DB_IPCE_GET_THREAD_FOR_TASKID:
2474 cbAdditionalSize = sizeof(pEvent->GetThreadForTaskId);
2475 break;
2476
2477 case DB_IPCE_FUNC_EVAL_RUDE_ABORT:
2478 cbAdditionalSize = sizeof(pEvent->FuncEvalRudeAbort);
2479 break;
2480
2481 case DB_IPCE_CREATE_HANDLE:
2482 cbAdditionalSize = sizeof(pEvent->CreateHandle);
2483 break;
2484
2485 case DB_IPCE_DISPOSE_HANDLE:
2486 cbAdditionalSize = sizeof(pEvent->DisposeHandle);
2487 break;
2488
2489 case DB_IPCE_INTERCEPT_EXCEPTION:
2490 cbAdditionalSize = sizeof(pEvent->InterceptException);
2491 break;
2492
2493 case DB_IPCE_GET_GCHANDLE_INFO:
2494 cbAdditionalSize = sizeof(pEvent->GetGCHandleInfo);
2495 break;
2496
2497 case DB_IPCE_CUSTOM_NOTIFICATION:
2498 cbAdditionalSize = sizeof(pEvent->CustomNotification);
2499 break;
2500
2501 default:
2502 printf("Unknown debugger event type: 0x%x\n", (pEvent->type & DB_IPCE_TYPE_MASK));
2503 _ASSERTE(!"Unknown debugger event type");
2504 }
2505
2506 return cbBaseSize + cbAdditionalSize;
2507}
2508#ifdef _PREFAST_
2509#pragma warning(pop)
2510#endif
2511
2512#ifdef _DEBUG
2513// Debug helper which returns the name associated with a MessageType.
2514const char *DbgTransportSession::MessageName(MessageType eType)
2515{
2516 switch (eType)
2517 {
2518 case MT_SessionRequest:
2519 return "SessionRequest";
2520 case MT_SessionAccept:
2521 return "SessionAccept";
2522 case MT_SessionReject:
2523 return "SessionReject";
2524 case MT_SessionResync:
2525 return "SessionResync";
2526 case MT_SessionClose:
2527 return "SessionClose";
2528 case MT_Event:
2529 return "Event";
2530 case MT_ReadMemory:
2531 return "ReadMemory";
2532 case MT_WriteMemory:
2533 return "WriteMemory";
2534 case MT_VirtualUnwind:
2535 return "VirtualUnwind";
2536 case MT_GetDCB:
2537 return "GetDCB";
2538 case MT_SetDCB:
2539 return "SetDCB";
2540 case MT_GetAppDomainCB:
2541 return "GetAppDomainCB";
2542 default:
2543 _ASSERTE(!"Unknown message type");
2544 return NULL;
2545 }
2546}
2547
2548// Debug logging helper which logs an incoming message of any type (as long as logging for that message
2549// class is currently enabled).
2550void DbgTransportSession::DbgTransportLogMessageReceived(MessageHeader *pHeader)
2551{
2552 switch (pHeader->m_eType)
2553 {
2554 case MT_SessionRequest:
2555 DbgTransportLog(LC_Session, "Received 'SessionRequest'");
2556 DBG_TRANSPORT_INC_STAT(ReceivedSessionRequest);
2557 return;
2558 case MT_SessionAccept:
2559 DbgTransportLog(LC_Session, "Received 'SessionAccept'");
2560 DBG_TRANSPORT_INC_STAT(ReceivedSessionAccept);
2561 return;
2562 case MT_SessionReject:
2563 DbgTransportLog(LC_Session, "Received 'SessionReject'");
2564 DBG_TRANSPORT_INC_STAT(ReceivedSessionReject);
2565 return;
2566 case MT_SessionResync:
2567 DbgTransportLog(LC_Session, "Received 'SessionResync'");
2568 DBG_TRANSPORT_INC_STAT(ReceivedSessionResync);
2569 return;
2570 case MT_SessionClose:
2571 DbgTransportLog(LC_Session, "Received 'SessionClose'");
2572 DBG_TRANSPORT_INC_STAT(ReceivedSessionClose);
2573 return;
2574 case MT_Event:
2575 DbgTransportLog(LC_Events, "Received '%s'",
2576 IPCENames::GetName((DebuggerIPCEventType)(DWORD)pHeader->TypeSpecificData.Event.m_eType));
2577 DBG_TRANSPORT_INC_STAT(ReceivedEvent);
2578 return;
2579#ifdef RIGHT_SIDE_COMPILE
2580 case MT_ReadMemory:
2581 DbgTransportLog(LC_Requests, "Received 'ReadMemory(0x%08X, %u)' reply",
2582 (PBYTE)pHeader->TypeSpecificData.MemoryAccess.m_pbLeftSideBuffer,
2583 (DWORD)pHeader->TypeSpecificData.MemoryAccess.m_cbLeftSideBuffer);
2584 DBG_TRANSPORT_INC_STAT(ReceivedReadMemory);
2585 return;
2586 case MT_WriteMemory:
2587 DbgTransportLog(LC_Requests, "Received 'WriteMemory(0x%08X, %u)' reply",
2588 (PBYTE)pHeader->TypeSpecificData.MemoryAccess.m_pbLeftSideBuffer,
2589 (DWORD)pHeader->TypeSpecificData.MemoryAccess.m_cbLeftSideBuffer);
2590 DBG_TRANSPORT_INC_STAT(ReceivedWriteMemory);
2591 return;
2592 case MT_VirtualUnwind:
2593 DbgTransportLog(LC_Requests, "Received 'VirtualUnwind' reply");
2594 DBG_TRANSPORT_INC_STAT(ReceivedVirtualUnwind);
2595 return;
2596 case MT_GetDCB:
2597 DbgTransportLog(LC_Requests, "Received 'GetDCB' reply");
2598 DBG_TRANSPORT_INC_STAT(ReceivedGetDCB);
2599 return;
2600 case MT_SetDCB:
2601 DbgTransportLog(LC_Requests, "Received 'SetDCB' reply");
2602 DBG_TRANSPORT_INC_STAT(ReceivedSetDCB);
2603 return;
2604 case MT_GetAppDomainCB:
2605 DbgTransportLog(LC_Requests, "Received 'GetAppDomainCB' reply");
2606 DBG_TRANSPORT_INC_STAT(ReceivedGetAppDomainCB);
2607 return;
2608#else // RIGHT_SIDE_COMPILE
2609 case MT_ReadMemory:
2610 DbgTransportLog(LC_Requests, "Received 'ReadMemory(0x%08X, %u)'",
2611 (PBYTE)pHeader->TypeSpecificData.MemoryAccess.m_pbLeftSideBuffer,
2612 (DWORD)pHeader->TypeSpecificData.MemoryAccess.m_cbLeftSideBuffer);
2613 DBG_TRANSPORT_INC_STAT(ReceivedReadMemory);
2614 return;
2615 case MT_WriteMemory:
2616 DbgTransportLog(LC_Requests, "Received 'WriteMemory(0x%08X, %u)'",
2617 (PBYTE)pHeader->TypeSpecificData.MemoryAccess.m_pbLeftSideBuffer,
2618 (DWORD)pHeader->TypeSpecificData.MemoryAccess.m_cbLeftSideBuffer);
2619 DBG_TRANSPORT_INC_STAT(ReceivedWriteMemory);
2620 return;
2621 case MT_VirtualUnwind:
2622 DbgTransportLog(LC_Requests, "Received 'VirtualUnwind'");
2623 DBG_TRANSPORT_INC_STAT(ReceivedVirtualUnwind);
2624 return;
2625 case MT_GetDCB:
2626 DbgTransportLog(LC_Requests, "Received 'GetDCB'");
2627 DBG_TRANSPORT_INC_STAT(ReceivedGetDCB);
2628 return;
2629 case MT_SetDCB:
2630 DbgTransportLog(LC_Requests, "Received 'SetDCB'");
2631 DBG_TRANSPORT_INC_STAT(ReceivedSetDCB);
2632 return;
2633 case MT_GetAppDomainCB:
2634 DbgTransportLog(LC_Requests, "Received 'GetAppDomainCB'");
2635 DBG_TRANSPORT_INC_STAT(ReceivedGetAppDomainCB);
2636 return;
2637#endif // RIGHT_SIDE_COMPILE
2638 default:
2639 _ASSERTE(!"Unknown message type");
2640 return;
2641 }
2642}
2643
2644static CLRRandom s_faultInjectionRandom;
2645
2646// Helper method used by the DBG_TRANSPORT_SHOULD_INJECT_FAULT macro.
2647bool DbgTransportSession::DbgTransportShouldInjectFault(DbgTransportFaultOp eOp, const char *szOpName)
2648{
2649 static DWORD s_dwFaultInjection = 0xffffffff;
2650
2651 // Init the fault injection system if that hasn't already happened.
2652 if (s_dwFaultInjection == 0xffffffff)
2653 {
2654 s_dwFaultInjection = CLRConfig::GetConfigValue(CLRConfig::INTERNAL_DbgTransportFaultInject);
2655
2656 // Try for repeatable failures here by always initializing the random seed to a fixed value. But use
2657 // different seeds for the left and right sides or they'll end up in lock step. The
2658 // DBG_TRANSPORT_FAULT_THIS_SIDE macro is a convenient integer value that differs on each side.
2659 s_faultInjectionRandom.Init(DBG_TRANSPORT_FAULT_THIS_SIDE);
2660
2661 // Clamp failure rate to a permissable value.
2662 if ((s_dwFaultInjection & DBG_TRANSPORT_FAULT_RATE_MASK) > 99)
2663 s_dwFaultInjection = (s_dwFaultInjection & ~DBG_TRANSPORT_FAULT_RATE_MASK) | 99;
2664 }
2665
2666 // Map current session state into the bitmask format used for fault injection control.
2667 DWORD dwState = 0;
2668 switch (m_eState)
2669 {
2670 case SS_Opening_NC:
2671 case SS_Opening:
2672 dwState = FS_Opening;
2673 break;
2674 case SS_Resync_NC:
2675 case SS_Resync:
2676 dwState = FS_Resync;
2677 break;
2678 case SS_Open:
2679 dwState = FS_Open;
2680 break;
2681 case SS_Closed:
2682 break;
2683 default:
2684 _ASSERTE(!"Bad session state");
2685 }
2686
2687 if ((s_dwFaultInjection & DBG_TRANSPORT_FAULT_THIS_SIDE) &&
2688 (s_dwFaultInjection & eOp) &&
2689 (s_dwFaultInjection & dwState))
2690 {
2691 // We're faulting this side, op and state. Roll the dice and see if this particular call should fail.
2692 DWORD dwChance = s_faultInjectionRandom.Next(100);
2693 if (dwChance < (s_dwFaultInjection & DBG_TRANSPORT_FAULT_RATE_MASK))
2694 {
2695 DbgTransportLog(LC_FaultInject, "Injected fault for %s operation", szOpName);
2696#if defined(FEATURE_CORESYSTEM)
2697 // not supported
2698#else
2699 WSASetLastError(WSAEFAULT);
2700#endif // defined(FEATURE_CORESYSTEM)
2701 return true;
2702 }
2703 }
2704
2705 return false;
2706}
2707#endif // _DEBUG
2708
2709// Lock abstraction code (hides difference in lock implementation between left and right side).
2710#ifdef RIGHT_SIDE_COMPILE
2711
2712// On the right side we use a CRITICAL_SECTION.
2713
2714void DbgTransportLock::Init()
2715{
2716 InitializeCriticalSection(&m_sLock);
2717}
2718
2719void DbgTransportLock::Destroy()
2720{
2721 DeleteCriticalSection(&m_sLock);
2722}
2723
2724void DbgTransportLock::Enter()
2725{
2726 EnterCriticalSection(&m_sLock);
2727}
2728
2729void DbgTransportLock::Leave()
2730{
2731 LeaveCriticalSection(&m_sLock);
2732}
2733#else // RIGHT_SIDE_COMPILE
2734
2735// On the left side we use a Crst.
2736
2737void DbgTransportLock::Init()
2738{
2739 m_sLock.Init(CrstDbgTransport, (CrstFlags)(CRST_UNSAFE_ANYMODE | CRST_DEBUGGER_THREAD | CRST_TAKEN_DURING_SHUTDOWN));
2740}
2741
2742void DbgTransportLock::Destroy()
2743{
2744}
2745
2746void DbgTransportLock::Enter()
2747{
2748 m_sLock.Enter();
2749}
2750
2751void DbgTransportLock::Leave()
2752{
2753 m_sLock.Leave();
2754}
2755#endif // RIGHT_SIDE_COMPILE
2756
2757#endif // (!defined(RIGHT_SIDE_COMPILE) && defined(FEATURE_DBGIPC_TRANSPORT_VM)) || (defined(RIGHT_SIDE_COMPILE) && defined(FEATURE_DBGIPC_TRANSPORT_DI))
2758