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#ifndef __DBG_TRANSPORT_SESSION_INCLUDED
7#define __DBG_TRANSPORT_SESSION_INCLUDED
8
9#ifndef RIGHT_SIDE_COMPILE
10#include <utilcode.h>
11#include <crst.h>
12
13#endif // !RIGHT_SIDE_COMPILE
14
15#if defined(FEATURE_DBGIPC_TRANSPORT_VM) || defined(FEATURE_DBGIPC_TRANSPORT_DI)
16
17#include <twowaypipe.h>
18
19/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
20 DbgTransportSession was originally designed around cross-machine debugging via sockets and it is supposed to
21 handle network interruptions. Right now we use pipes (see TwoWaypipe) and don't expect to have connection issues.
22 But there seem to be no good reason to try hard to get rid of existing working protocol even if it's a bit
23 cautious about connection quality. So please KEEP IN MIND THAT SOME COMMENTS REFERING TO NETWORK AND SOCKETS
24 CAN BE OUTDATED.
25 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
26
27//
28// Provides a robust and secure transport session between a debugger and a debuggee that are potentially on
29// different machines.
30//
31// The following terminology is used for the wire protocol. The smallest meaningful entity written to or read
32// from the connection is a "message". This consists of one or maybe two "blocks" where a block is a
33// contiguous region of memory in the host machine. The first block is always a "message header" which is
34// fixed size (allowing the receiver to know how many bytes to read off the stream oriented connection) and
35// has type codes and other fields which the receiver can use to determine if another block is part of the
36// message (and if so, exactly how large that block is). Many management messages consist only of a message
37// header block, while operations such as sending a debugger event structure involve a message header followed
38// by a block containing the actual event structure.
39//
40// Message acknowledgement (sometimes abbreviated to ack) refers to a system of marking all messages with an
41// ID and noting and reporting which IDs we've seen from our peer. We piggy back the highest seen ID on all
42// outgoing messages and this is used by the infrastructure to communicate the fact that a sender can release
43// its copy of an outbound message since it successfully made it across the communications channel and won't
44// need to be resent in the case of a network failure.
45//
46// This file uses the debugger conventions for naming the two endpoints of the session: the left side or LS is
47// the side with the runtime while the right side (RS) is the side with the debugger.
48//
49
50// The structure of this file necessitates a certain number of forward references (particularly in the
51// comments). If you see a term you don't understand please do a search for it further down the file, where
52// hopefully you will find a detailed definition (and if not, please add one).
53
54struct DebuggerIPCEvent;
55struct DbgEventBufferEntry;
56
57// Some simple ad-hoc debug only transport logging. This output is too chatty for an exisitng CLR logging
58// channel (and we've run out of bits for an additional channel) and is likely to be of limited use to anyone
59// besides the transport developer (and even then only occasionally).
60//
61// To enable use 'set|export COMPlus_DbgTransportLog=X' where X is 1 for RS logging, 2 for LS logging and 3
62// for both (default is disabled). Use 'set|export COMPlus_DbgTransportLogClass=X' where X is the hex
63// representation of one or more DbgTransportLogClass flags defined below (default is all classes enabled).
64// For instance, 'set COMPlus_DbgTransportLogClass=f' will enable only message send and receive logging (for
65// all message types).
66enum DbgTransportLogEnable
67{
68 LE_None = 0x00000000,
69 LE_LeftSide = 0x00000001,
70 LE_RightSide = 0x00000002,
71 LE_Unknown = 0xffffffff,
72};
73
74enum DbgTransportLogClass
75{
76 LC_None = 0x00000000,
77 LC_Events = 0x00000001, // Sending and receiving debugger events
78 LC_Session = 0x00000002, // Sending and receiving session messages
79 LC_Requests = 0x00000004, // Sending requests such as MT_GetDCB and receiving replies
80 LC_EventAcks = 0x00000008, // Sending and receiving debugger event acks (DEPRECATED)
81 LC_NetErrors = 0x00000010, // Network errors
82 LC_FaultInject = 0x00000020, // Artificially injected network faults
83 LC_Proxy = 0x00000040, // Proxy interactions
84 LC_All = 0xffffffff,
85 LC_Always = 0xffffffff, // Always log, regardless of class setting
86};
87
88// Status codes that can be returned by various APIs that indicate some conditions of the error that a caller
89// might usefully pass on to a user (environmental factors that the user might have some control over).
90enum ConnStatus
91{
92 SCS_Success, // The request succeeded
93 SCS_OutOfMemory, // The request failed due to a low memory situation
94 SCS_InvalidConfiguration, // Initialize() failed because the debugger settings were not configured or
95 // have become corrupt
96 SCS_UnknownTarget, // Connect() failed because the remote machine at the given address could not
97 // be found
98 SCS_NoListener, // Connect() failed because the remote machine was not listening for requests
99 // on the given port (most likely the remote machine is not configured for
100 // debugging)
101 SCS_NetworkFailure, // Connect() failed due to miscellaneous network error
102 SCS_MismatchedCerts, // Connect()/Accept() failed because the remote party was using a different
103 // cert
104};
105
106
107// Multiple clients can use a single DbgTransportSession, but only one can act as the debugger.
108// A valid DebugTicket is given to the client who is acting as the debugger.
109struct DebugTicket
110{
111friend class DbgTransportSession;
112
113public:
114 DebugTicket() { m_fValid = false; };
115
116 bool IsValid() { return m_fValid; };
117
118protected:
119 void SetValid() { m_fValid = true; };
120 void SetInvalid() { m_fValid = false; };
121
122private:
123 // Tickets can't be copied around. Hide these definitions so as to enforce that.
124 // We still need the Copy ctor so that it can be passed in as a parameter.
125 void operator=(DebugTicket & other);
126
127 bool m_fValid;
128};
129
130#ifdef RIGHT_SIDE_COMPILE
131#define DBG_TRANSPORT_LOG_THIS_SIDE LE_RightSide
132#define DBG_TRANSPORT_LOG_PREFIX "RS"
133#else // RIGHT_SIDE_COMPILE
134#define DBG_TRANSPORT_LOG_THIS_SIDE LE_LeftSide
135#define DBG_TRANSPORT_LOG_PREFIX "LS"
136#endif // RIGHT_SIDE_COMPILE
137
138// Method used to log an interesting event (of the given class). The message given will have any additional
139// arguments inserted following 'printf' formatiing conventions and will be automatically prepended with a
140// LS/RS indicator and suffixed with a newline.
141inline void DbgTransportLog(DbgTransportLogClass eClass, const char *szFormat, ...)
142{
143#ifdef _DEBUG
144 static DWORD s_dwLoggingEnabled = LE_Unknown;
145 static DWORD s_dwLoggingClass = LC_All;
146
147 if (s_dwLoggingEnabled == LE_Unknown)
148 {
149 s_dwLoggingEnabled = REGUTIL::GetConfigDWORD_DontUse_(CLRConfig::INTERNAL_DbgTransportLog, LE_None);
150 s_dwLoggingClass = REGUTIL::GetConfigDWORD_DontUse_(CLRConfig::INTERNAL_DbgTransportLogClass, LC_All);
151 }
152
153 if ((s_dwLoggingEnabled & DBG_TRANSPORT_LOG_THIS_SIDE) &&
154 ((s_dwLoggingClass & eClass) || eClass == LC_Always))
155 {
156 char szOutput[256];
157 va_list args;
158
159 va_start(args, szFormat);
160 vsprintf_s(szOutput, sizeof(szOutput), szFormat, args);
161 va_end(args);
162
163 printf("%s %04x: %s\n", DBG_TRANSPORT_LOG_PREFIX, GetCurrentThreadId(), szOutput);
164 fflush(stdout);
165
166 char szDebugOutput[512];
167 sprintf_s(szDebugOutput, sizeof(szDebugOutput), "%s: %s\n", DBG_TRANSPORT_LOG_PREFIX, szOutput);
168 OutputDebugStringA(szDebugOutput);
169 }
170#endif // _DEBUG
171}
172
173#ifdef _DEBUG
174//
175// Debug-only network fault injection (in order to help test the robust session code). Control is via a single
176// DWORD read from the environment (COMPlus_DbgTransportFaultInject). This DWORD is treated as a set of bit
177// fields as follows:
178//
179// +-------+-------+-------+----------------+-----------+
180// | Side | Op | State | Reserved | Frequency |
181// +-------+-------+-------+----------------+-----------+
182// 31<->28 27<->24 23<->20 19<----------->8 7<------->0
183//
184// The 'Side' field indicates whether the left or right side (or both) should have faults injected. See
185// DbgTransportFaultSide below for values.
186//
187// The 'Op' field indicates which connection methods should simulate failures. See DbgTransportFaultOp.
188//
189// The 'State' field indicates the session states in which faults will be injected. See
190// DbgTransportFaultState. Note that introducing too many failures into the Opening and Opening_NC states will
191// cause the debugger to timeout and fail.
192//
193// The 'Reserved' field has no current meaning and should be left as zero.
194//
195// The 'Frequency' field indicates a percentage failure rate. Valid values are between 0 and 99, values beyond
196// this range will be clamped to 99.
197//
198// For example:
199//
200// export COMPlus_DbgTransportFaultInject=1ff00001
201// --> Fail all network operations on the left side 1% of the time
202//
203// export COMPlus_DbgTransportFaultInject=34200063
204// --> Fail Send() calls on both sides while the session is Open 99% of the time
205//
206
207#define DBG_TRANSPORT_FAULT_RATE_MASK 0x000000ff
208
209// Whether to inject faults to the left, right or both sides.
210enum DbgTransportFaultSide
211{
212 FS_Left = 0x10000000,
213 FS_Right = 0x20000000,
214};
215
216// Network operations which are candiates for fault injection.
217enum DbgTransportFaultOp
218{
219 FO_Connect = 0x01000000,
220 FO_Accept = 0x02000000,
221 FO_Send = 0x04000000,
222 FO_Receive = 0x08000000,
223};
224
225// Session states into which faults should be injected.
226enum DbgTransportFaultState
227{
228 FS_Opening = 0x00100000, // Opening and Opening_NC
229 FS_Open = 0x00200000,
230 FS_Resync = 0x00400000, // Resync and Resync_NC
231};
232
233#ifdef RIGHT_SIDE_COMPILE
234#define DBG_TRANSPORT_FAULT_THIS_SIDE FS_Right
235#else // RIGHT_SIDE_COMPILE
236#define DBG_TRANSPORT_FAULT_THIS_SIDE FS_Left
237#endif // RIGHT_SIDE_COMPILE
238
239// Macro to determine whether a fault should be injected for the given operation.
240#define DBG_TRANSPORT_SHOULD_INJECT_FAULT(_op) DbgTransportShouldInjectFault(FO_##_op, #_op)
241
242#else // _DEBUG
243#define DBG_TRANSPORT_SHOULD_INJECT_FAULT(_op) false
244#endif // _DEBUG
245
246// The PAL doesn't define htons (host-to-network-short) and friends. So provide our own versions here.
247// winsock2.h defines BIGENDIAN to 0x0000 and LITTLEENDIAN to 0x0001, so we need to be careful with the
248// #ifdef.
249#if BIGENDIAN > 0
250#define DBGIPC_HTONS(x) (x)
251#define DBGIPC_NTOHS(x) (x)
252#define DBGIPC_HTONL(x) (x)
253#define DBGIPC_NTOHL(x) (x)
254#else
255inline UINT16 DBGIPC_HTONS(UINT16 x)
256{
257 return (x >> 8) | (x << 8);
258}
259#define DBGIPC_NTOHS(x) DBGIPC_HTONS(x)
260inline UINT32 DBGIPC_HTONL(UINT32 x)
261{
262 return (x >> 24) |
263 ((x >> 8) & 0x0000FF00L) |
264 ((x & 0x0000FF00L) << 8) |
265 (x << 24);
266}
267#define DBGIPC_NTOHL(x) DBGIPC_HTONL(x)
268
269#endif
270
271// Lock abstraction (we can't use the same lock implementation on LS and RS since we really want a Crst on the
272// LS and this isn't available in the RS environment).
273class DbgTransportLock
274{
275public:
276 void Init();
277 void Destroy();
278 void Enter();
279 void Leave();
280
281private:
282#ifdef RIGHT_SIDE_COMPILE
283 CRITICAL_SECTION m_sLock;
284#else // RIGHT_SIDE_COMPILE
285 CrstExplicitInit m_sLock;
286#endif // RIGHT_SIDE_COMPILE
287};
288
289// The transport has only one queue for IPC events, but each IPC event can be marked as one of two types.
290// The transport will signal the handle corresponding to the type of each IPC event. (See
291// code:DbgTransportSession::GetIPCEventReadyEvent and code:DbgTransportSession::GetDebugEventReadyEvent.)
292// This is effectively a basic multiplexing scheme. The old-style IPC event are for all RS-to-LS IPC events
293// and for all LS-to-RS replies. The other type is for LS-to-RS IPC events transported over the native
294// pipeline. For more information, see the comments for the interface code:IEventChannel.
295enum IPCEventType
296{
297 IPCET_OldStyle,
298 IPCET_DebugEvent,
299 IPCET_Max,
300};
301
302// The class that encapsulates all the state for a single session on either the right or left side. The left
303// side supports only one instance of this class for a given runtime. The right side can support several (all
304// connected to different LS instances of course).
305class DbgTransportSession
306{
307public:
308 // No real work done in the constructor. Use Init() instead.
309 DbgTransportSession();
310
311 // Cleanup what is allocated/created in Init()
312 ~DbgTransportSession();
313
314 // Allocates initial resources (including starting the transport thread). The session will start in the
315 // SS_Opening state. That is, the RS will immediately start trying to Connect() a connection while the
316 // LS will perform an Accept() to wait for a connection request. The RS needs an IP address and port
317 // number to initiate connections. These should be given in host byte order. The LS, on the other hand,
318 // requires the addresses of a couple of runtime data structures to service certain debugger requests that
319 // may be delivered once the session is established.
320#ifdef RIGHT_SIDE_COMPILE
321 HRESULT Init(const ProcessDescriptor& pd, HANDLE hProcessExited);
322#else
323 HRESULT Init(DebuggerIPCControlBlock * pDCB, AppDomainEnumerationIPCBlock * pADB);
324#endif // RIGHT_SIDE_COMPILE
325
326 // Drive the session to the SS_Closed state, which will deallocate all remaining transport resources
327 // (including terminating the transport thread). If this is the RS and the session state is SS_Open at the
328 // time of this call a graceful disconnect will be attempted (which tells the LS to go back to SS_Opening
329 // to look for a new RS rather than interpreting the disconnection as a temporary error and going into
330 // SS_Resync). On either side the session will no longer be functional after this call returns (though
331 // Init() may be called again to start over from the beginning).
332 void Shutdown();
333
334#ifdef RIGHT_SIDE_COMPILE
335 // Used by debugger side (RS) to cleanup the target (LS) named pipes
336 // and semaphores when the debugger detects the debuggee process exited.
337 void CleanupTargetProcess();
338#else
339 // Cleans up the named pipe connection so no tmp files are left behind. Does only
340 // the minimum and must be safe to call at any time. Called during PAL ExitProcess,
341 // TerminateProcess and for unhandled native exceptions and asserts.
342 void AbortConnection();
343#endif // RIGHT_SIDE_COMPILE
344
345 LONG AddRef()
346 {
347 LONG ref = InterlockedIncrement(&m_ref);
348 return ref;
349 }
350
351 LONG Release()
352 {
353 _ASSERTE(m_ref > 0);
354 LONG ref = InterlockedDecrement(&m_ref);
355 if (ref == 0)
356 {
357 delete this;
358 }
359 return ref;
360 }
361
362#ifndef RIGHT_SIDE_COMPILE
363 // API used only by the LS to drive the transport into a state where it won't accept connections. This is
364 // used when no proxy is detected at startup but it's too late to shutdown all of the debugging system
365 // easily. It's mainly paranoia to increase the protection of your system when the proxy isn't started.
366 void Neuter();
367#endif // !RIGHT_SIDE_COMPILE
368
369#ifdef RIGHT_SIDE_COMPILE
370 // On the RS it may be useful to wait and see if the session can reach the SS_Open state. If the target
371 // runtime has terminated for some reason then we'll never reach the open state. So the method below gives
372 // the RS a way to try and establish a connection for a reasonable amount of time and to time out
373 // otherwise. They could then call Shutdown on the session and report an error back to the rest of the
374 // debugger. The method returns true if the session opened within the time given (in milliseconds) and
375 // false otherwise.
376 bool WaitForSessionToOpen(DWORD dwTimeout);
377
378 // A valid ticket is returned if no other client is currently acting as the debugger.
379 bool UseAsDebugger(DebugTicket * pTicket);
380
381 // A valid ticket is required in order for this function to succeed. After this function succeeds,
382 // another client can request to be the debugger.
383 bool StopUsingAsDebugger(DebugTicket * pTicket);
384#endif // RIGHT_SIDE_COMPILE
385
386 // Sends a pre-initialized event to the other side.
387 HRESULT SendEvent(DebuggerIPCEvent * pEvent);
388 HRESULT SendDebugEvent(DebuggerIPCEvent * pEvent);
389
390 // Retrieves the auto-reset handle which is signalled by the session each time a new event is received
391 // from the other side.
392 HANDLE GetIPCEventReadyEvent();
393 HANDLE GetDebugEventReadyEvent();
394
395 // Copies the last event received from the other side into the provided buffer. This should only be called
396 // (once) after the event returned from GetIPCEventReadyEvent()/GetDebugEventReadyEvent() has been signalled.
397 void GetNextEvent(DebuggerIPCEvent *pEvent, DWORD cbEvent);
398
399#ifdef RIGHT_SIDE_COMPILE
400 // Read and write memory on the LS from the RS.
401 HRESULT ReadMemory(PBYTE pbRemoteAddress, PBYTE pbBuffer, SIZE_T cbBuffer);
402 HRESULT WriteMemory(PBYTE pbRemoteAddress, PBYTE pbBuffer, SIZE_T cbBuffer);
403 HRESULT VirtualUnwind(DWORD threadId, ULONG32 contextSize, PBYTE context);
404
405 // Read and write the debugger control block on the LS from the RS.
406 HRESULT GetDCB(DebuggerIPCControlBlock *pDCB);
407 HRESULT SetDCB(DebuggerIPCControlBlock *pDCB);
408
409 // Read the AppDomain control block on the LS from the RS.
410 HRESULT GetAppDomainCB(AppDomainEnumerationIPCBlock *pADB);
411
412#endif // RIGHT_SIDE_COMPILE
413
414private:
415
416 // Highest protocol version supported by this side of the session. See the
417 // m_dwMajorVersion/m_dwMinorVersion fields for a detailed explanation and the actual version being used
418 // by the session (if it is formed).
419 static const DWORD kCurrentMajorVersion = 2;
420 static const DWORD kCurrentMinorVersion = 0;
421
422 // Session states. These determine which action is taken on a SendMessage (message is sent, queued or an
423 // error is raised) and which incoming messages are valid.
424 enum SessionState
425 {
426 SS_Closed, // No session and no attempt is being made to form one
427 SS_Opening_NC, // Session is being formed but no connection is established yet
428 SS_Opening, // Session is being formed, the low level connection is in place
429 SS_Open, // Session is fully formed and normal transport messages can be sent and received
430 SS_Resync_NC, // A low level connection error is occurred and we're attempting to re-form the link
431 SS_Resync, // We're trying to resynchronize high level state over the new connection
432 };
433
434 // Types of messages that can be sent over the transport connection.
435 enum MessageType
436 {
437 // Session management operations. These must come first and MT_SessionClose must be last in the group.
438 MT_SessionRequest, // RS -> LS : Request a new session be formed (optionally pass encrypted data key)
439 MT_SessionAccept, // LS -> RS : Accept new session
440 MT_SessionReject, // LS -> RS : Reject new session, give reason
441 MT_SessionResync, // RS <-> LS : Resync broken connection by informing other side which messages must be resent
442 MT_SessionClose, // RS -> LS : Gracefully terminate a session
443
444 // Debugger events.
445 MT_Event, // RS <-> LS : A debugger event is being sent as the data block of the message
446
447 // Misc management operations.
448 MT_ReadMemory, // RS <-> LS : RS wants to read LS memory block (or LS is replying to such a request)
449 MT_WriteMemory, // RS <-> LS : RS wants to write LS memory block (or LS is replying to such a request)
450 MT_VirtualUnwind, // RS <-> LS : RS wants to LS unwind a stack frame (or LS is replying to such a request)
451 MT_GetDCB, // RS <-> LS : RS wants to read LS DCB (or LS is replying to such a request)
452 MT_SetDCB, // RS <-> LS : RS wants to write LS DCB (or LS is replying to such a request)
453 MT_GetAppDomainCB, // RS <-> LS : RS wants to read LS AppDomainCB (or LS is replying to such a request)
454 };
455
456 // Reasons the LS can give for rejecting a session. These codes should *not* be changed other than by
457 // adding reasons to keep versioning possible.
458 enum RejectReason
459 {
460 RR_IncompatibleVersion, // LS doesn't support the major version asked for in the request.
461 RR_AlreadyAttached, // LS already has another session open (LS only supports one session at a time)
462 };
463
464 // Struct that defines the format of a message header block sent on the connection. Note that the size of
465 // this structure and the location/size of the m_eType field must *never* change to allow our versioning
466 // protocol to work properly (in particular any LS must be able to interpret at least the type and version
467 // number of an MT_SessionRequest and reply with a MT_SessionReject that any RS can interpret the type and
468 // version of). To help with this there is a padding field at the end for future expansion (this should be
469 // initialized to zero and not accessed in any other manner).
470 struct MessageHeader
471 {
472 Portable<MessageType> m_eType; // Type of message this is
473 Portable<DWORD> m_cbDataBlock; // Size of data block that immediately follows this header (can be zero)
474 Portable<DWORD> m_dwId; // Message ID assigned by the sender of this message
475 Portable<DWORD> m_dwReplyId; // Message ID that this is a reply to (used by messages such as MT_GetDCB)
476 Portable<DWORD> m_dwLastSeenId; // Message ID last seen by sender (receiver can discard up to here from send queue)
477 Portable<DWORD> m_dwReserved; // Reserved for future expansion (must be initialized to zero and
478 // never read)
479
480 // The rest of the header varies depending on the message type (keep the maximum size of this union
481 // small since all messages will pay the overhead, large message type specific data should go in the
482 // following data block).
483 union
484 {
485 // Used by MT_SessionRequest / MT_SessionAccept.
486 struct
487 {
488 Portable<DWORD> m_dwMajorVersion; // Protocol version requested/accepted
489 Portable<DWORD> m_dwMinorVersion;
490 } VersionInfo;
491
492 // Used by MT_SessionReject.
493 struct
494 {
495 Portable<RejectReason> m_eReason; // Reason for rejection.
496 Portable<DWORD> m_dwMajorVersion; // Highest protocol version the LS supports
497 Portable<DWORD> m_dwMinorVersion;
498 } SessionReject;
499
500 // Used by MT_ReadMemory and MT_WriteMemory.
501 struct
502 {
503 Portable<PBYTE> m_pbLeftSideBuffer; // Address of memory to read/write on the LS
504 Portable<DWORD> m_cbLeftSideBuffer; // Size in bytes of memory to read/write
505 Portable<HRESULT> m_hrResult; // Result from LS (access can fail due to unmapped memory etc.)
506 } MemoryAccess;
507
508 // Used by MT_Event.
509 struct
510 {
511 Portable<IPCEventType> m_eIPCEventType; // multiplexing type of this IPC event
512 Portable<DWORD> m_eType; // Event type (useful for debugging)
513 } Event;
514
515 } TypeSpecificData;
516
517 BYTE m_sMustBeZero[8]; // Set this to zero when initializing and never read the contents
518 };
519
520 // Struct defining the format of the data block sent with a SessionRequest.
521 struct SessionRequestData
522 {
523 GUID m_sSessionID; // Unique session ID. Treated as byte blob so no endian-ness
524 };
525
526 // Struct used to track a message that is being (or will soon be) sent but has not yet been acknowledged.
527 // These are usually found queued on the send queue.
528 struct Message
529 {
530 Message *m_pNext; // Next message in the queue
531 MessageHeader m_sHeader; // Inline message header
532 PBYTE m_pbDataBlock; // Pointer to optional message data block (or NULL)
533 DWORD m_cbDataBlock; // Count of bytes in above block if it's non-NULL
534 HANDLE m_hReplyEvent; // Optional event to signal if this message is replied to (or NULL)
535 PBYTE m_pbReplyBlock; // Optional buffer to place data block from reply into (or NULL)
536 DWORD m_cbReplyBlock; // Size in bytes of the above buffer if it is non-NULL
537 Message *m_pOrigMessage; // Used when we need to find the original message from a copy
538 bool m_fAborted; // True if this send was aborted due to session shutdown
539
540 // Common initialization for messages.
541 void Init(MessageType eType,
542 PBYTE pbBufferIn = NULL,
543 DWORD cbBufferIn = 0,
544 PBYTE pbBufferOut = NULL,
545 DWORD cbBufferOut = 0)
546 {
547 memset(this, 0, sizeof(*this));
548 m_sHeader.m_eType = eType;
549 m_sHeader.m_cbDataBlock = cbBufferIn;
550 m_pbDataBlock = pbBufferIn;
551 m_cbDataBlock = cbBufferIn;
552 m_pbReplyBlock = pbBufferOut;
553 m_cbReplyBlock = cbBufferOut;
554 }
555 };
556
557 // Holder class used to take a transport lock in a given scope and automatically release it once that
558 // scope is exited.
559 class TransportLockHolder
560 {
561 public:
562 TransportLockHolder(DbgTransportLock *pLock)
563 {
564 m_pLock = pLock;
565 m_pLock->Enter();
566 }
567
568 ~TransportLockHolder()
569 {
570 m_pLock->Leave();
571 }
572
573 private:
574 DbgTransportLock *m_pLock;
575 };
576
577#ifdef _DEBUG
578 // Store statistics for various session activities that will be useful for performance analysis and tracking
579 // down bugs.
580 struct DbgStats
581 {
582 // Message type counts for sends.
583 LONG m_cSentSessionRequest;
584 LONG m_cSentSessionAccept;
585 LONG m_cSentSessionReject;
586 LONG m_cSentSessionResync;
587 LONG m_cSentSessionClose;
588 LONG m_cSentEvent;
589 LONG m_cSentReadMemory;
590 LONG m_cSentWriteMemory;
591 LONG m_cSentVirtualUnwind;
592 LONG m_cSentGetDCB;
593 LONG m_cSentSetDCB;
594 LONG m_cSentGetAppDomainCB;
595 LONG m_cSentDDMessage;
596
597 // Message type counts for receives.
598 LONG m_cReceivedSessionRequest;
599 LONG m_cReceivedSessionAccept;
600 LONG m_cReceivedSessionReject;
601 LONG m_cReceivedSessionResync;
602 LONG m_cReceivedSessionClose;
603 LONG m_cReceivedEvent;
604 LONG m_cReceivedReadMemory;
605 LONG m_cReceivedWriteMemory;
606 LONG m_cReceivedVirtualUnwind;
607 LONG m_cReceivedGetDCB;
608 LONG m_cReceivedSetDCB;
609 LONG m_cReceivedGetAppDomainCB;
610 LONG m_cReceivedDDMessage;
611
612 // Low level block counts.
613 LONG m_cSentBlocks;
614 LONG m_cReceivedBlocks;
615
616 // Byte count summaries.
617 LONGLONG m_cbSentBytes;
618 LONGLONG m_cbReceivedBytes;
619
620 // Errors and recovery
621 LONG m_cSendErrors;
622 LONG m_cReceiveErrors;
623 LONG m_cMiscErrors;
624 LONG m_cConnections;
625 LONG m_cResends;
626
627 // Session counts.
628 LONG m_cSessions;
629 };
630
631 DbgStats m_sStats;
632
633 // Macros to update the statistics. The increment version is thread safe, but the add version is assumed to be
634 // externally serialized since the 64-bit Interlocked operations are not available on all platforms and these
635 // stats are used for send and receive byte counts which are updated at locations that are serialized anyway.
636#define DBG_TRANSPORT_INC_STAT(_name) InterlockedIncrement(&m_sStats.m_c##_name)
637#define DBG_TRANSPORT_ADD_STAT(_name, _amount) m_sStats.m_cb##_name += (_amount)
638
639#else // _DEBUG
640
641#define DBG_TRANSPORT_INC_STAT(_name)
642#define DBG_TRANSPORT_ADD_STAT(_name, _amount)
643
644#endif // _DEBUG
645
646 // Reference count
647 LONG m_ref;
648
649 // Some flags used to record how far we got in Init() (used for cleanup in Shutdown()).
650 bool m_fInitStateLock;
651#ifndef RIGHT_SIDE_COMPILE
652 bool m_fInitWSA;
653#endif // !RIGHT_SIDE_COMPILE
654
655 // Protocol version. This consists of two parts. The major version is incremented on incompatible protocol
656 // updates. That is, a session between left and right sides that cannot use a protocol with the exact same
657 // major version cannot be formed. The minor version number is incremented on compatible protocol updates.
658 // These are usually associated with optional extensions to the protocol (e.g. a V1.2 endpoint might set
659 // previously unused fields in a message header to indicate some optional hint about the message that a
660 // V1.1 client won't notice at all).
661 //
662 // The right side has a hard-coded version number it sends in the SessionRequest message. The left side
663 // must support the same major version or reply with a SessionReject message containing the highest
664 // version it does support. For this reason the format of a SessionReject message can never change at all.
665 // On a SessionAccept the left side sends back the version number and can choose to lower the minor
666 // version to the highest it knows about. This gives the right side a hint as to the capabilities of the
667 // left side (though it must be prepared to interact with a left side with any minor version number).
668 //
669 // If necessary (and the SessionReject message sent by an incompatible left side indicates a major version
670 // the right side can also support), the right side can re-attempt a SessionRequest with a lower major
671 // version.
672 DWORD m_dwMajorVersion;
673 DWORD m_dwMinorVersion;
674
675 // Session ID randomly allocated by the right side and sent over in the SessionRequest message. This
676 // serves to disambiguate a re-send of the SessionRequest due to a network error versus a SessionRequest
677 // from a different debugger.
678 GUID m_sSessionID;
679
680 // Lock used to synchronize sending messages and updating the session state. This ensures message bytes
681 // don't become interleaved on the transport connection, the send queue is updated consistently across
682 // multiple threads and that we never attempt to use a connection that is being deallocated on another
683 // thread due to a state change. Receives don't need this since they're performed only on the transport
684 // thread (which is also the only thread allowed to deallocate the connection).
685 DbgTransportLock m_sStateLock;
686
687 // Queue of messages that have been sent over the connection but not acknowledged yet or are waiting to be
688 // sent (because another message is using the connection or we're in a SessionResync state). You must hold
689 // m_sStateLock in order to access this queue.
690 Message *m_pSendQueueFirst;
691 Message *m_pSendQueueLast;
692
693 // Message IDs. These are monotonically increasing numbers starting from 0 that are used to stamp each
694 // non-session management message sent on this session. If a low-level network error occurs and we must
695 // abandon and re-form the underlying transport connection the left and right sides send SessionResync
696 // messages with the ID of the last message they received (and processed). This allows us to determine
697 // which messages we still have in our send queue must be re-sent over the new transport connection.
698 // Allocate a new message ID by post incrementing m_dwNextMessageId under the state lock.
699 DWORD m_dwNextMessageId; // Next ID we'll give to a message we're sending
700 DWORD m_dwLastMessageIdSeen; // Last ID we saw in an incoming, fully received message
701
702 // The current session state. This is updated atomically under m_sStateLock.
703 SessionState m_eState;
704
705#ifdef RIGHT_SIDE_COMPILE
706 // Manual reset event that is signalled whenever the session state is SS_Open or SS_Closed (after waiting
707 // on this event the caller should check to see which state it was).
708 HANDLE m_hSessionOpenEvent;
709#endif // RIGHT_SIDE_COMPILE
710
711 // Thread responsible for initial Connect()/Accept() on a low level transport connection and
712 // subsequently for all message reception on that connection. Any error will cause the thread to reset
713 // back into the Connect()/Accept() phase (along with the resulting session state change).
714 HANDLE m_hTransportThread;
715
716 TwoWayPipe m_pipe;
717
718#ifdef RIGHT_SIDE_COMPILE
719 // On the RS the transport thread needs to know the IP address and port number to Connect() to.
720 ProcessDescriptor m_pd; // Descriptor of a process we're talking to.
721
722 HANDLE m_hProcessExited; // event which will be signaled when the debuggee is terminated
723
724 bool m_fDebuggerAttached;
725#endif
726
727 // Debugger event handling. To improve performance we allow the debugger to send as many events as it
728 // likes without acknowledgement from its peer. While not strictly adhering to the semantic provided by
729 // the shared memory buffer transport (where the buffer could not be written again until the receiver had
730 // explicitly released it) it turns out that no debugging code relies on this. In particular, the most
731 // common scenario where this makes sense is the left side sending large scale update events (such as the
732 // groups of appdomain create, module load etc. events sent during an attach). Here the right hand side
733 // queues the events for later processing and releases the buffers right away.
734 // We gain performance since its no longer necessary to send (or wait on) event acknowledgment messages.
735 // This lowers both network bandwidth and latency (especially when one side is trying to send a continuous
736 // stream of events).
737 // From the transport standpoint this design mainly impacts event receipt. We maintain a dynamically sized
738 // pool of event receipt buffers (the size is determined by the maximum number of unread events we've seen
739 // at any one time). The buffer is a circular array: clients read from the buffer at head index which is
740 // followed by some number of valid buffers (wrapping around to the start of the array if necessary). New
741 // events are added after these (and grow the array if the tail would touch the head otherwise).
742 DbgEventBufferEntry * m_pEventBuffers; // Pointer to array of incoming debugger events
743 DWORD m_cEventBuffers; // Size of the array above (in events)
744 DWORD m_cValidEventBuffers; // Number of events that actually contain data
745 DWORD m_idxEventBufferHead; // Index of the first valid event
746 DWORD m_idxEventBufferTail; // Index of the first invalid event
747 HANDLE m_rghEventReadyEvent[IPCET_Max]; // The event signalled when a new event arrives
748
749#ifndef RIGHT_SIDE_COMPILE
750 // The LS requires the addresses of a couple of runtime data structures in order to service MT_GetDCB etc.
751 // These are provided by the runtime at intialization time.
752 DebuggerIPCControlBlock *m_pDCB;
753 AppDomainEnumerationIPCBlock *m_pADB;
754#endif // !RIGHT_SIDE_COMPILE
755
756 HRESULT SendEventWorker(DebuggerIPCEvent * pEvent, IPCEventType type);
757
758 // Sends a pre-formatted message (including the data block, if any). The fWaitsForReply indicates whether
759 // the caller is going to block until some sort of reply message is received (for instance an event that
760 // must be ack'd or a request such as MT_GetDCB that needs a reply). SendMessage() uses this to determine
761 // whether it needs to buffer the message before placing it on the send queue (since it may need to resend
762 // the message after a transitory network failure).
763 HRESULT SendMessage(Message *pMessage, bool fWaitsForReply);
764
765 // Helper method for sending messages requiring a reply (such as MT_GetDCB) and waiting on the result.
766 HRESULT SendRequestMessageAndWait(Message *pMessage);
767
768 // Sends a single contiguous buffer of host memory over the connection. The caller is responsible for
769 // holding the state lock and ensuring the session state is SS_Open. Returns false if the send failed (the
770 // error will have already caused the recovery logic to kick in, so handling it is not required, the
771 // boolean is just returned so that any further blocks in the message are not sent).
772 bool SendBlock(PBYTE pbBuffer, DWORD cbBuffer);
773
774 // Receives a single contiguous buffer of host memory over the connection. No state lock needs to be
775 // held (receives are serialized by the fact they're only performed on the transport thread). Returns
776 // false if a network error is encountered (which will automatically transition the session into the
777 // correct retry state).
778 bool ReceiveBlock(PBYTE pbBuffer, DWORD cbBuffer);
779
780 // Called upon encountering a network error (e.g. an error from Send() or Receive()). This handles pushing
781 // the session state into SS_Resync_NC in order to start the recovery process.
782 void HandleNetworkError(bool fCallerHoldsStateLock);
783
784 // Scan the send queue and discard any messages which have been processed by the other side according to
785 // the specified ID). Messages waiting on a reply message (e.g. MT_GetDCB) will be retained until that
786 // reply is processed. FlushSendQueue will take the state lock.
787 void FlushSendQueue(DWORD dwLastProcessedId);
788
789#ifdef RIGHT_SIDE_COMPILE
790 // Perform processing required to complete a request (such as MT_GetDCB) once a reply comes in. This
791 // includes reading data from the connection into the output buffer, removing the original message from
792 // the send queue and signalling the completion event. Returns true if no network error was encountered.
793 bool ProcessReply(MessageHeader *pHeader);
794
795 // Upon receiving a reply message, signal the event on the message to wake up the thread waiting for
796 // the reply message and close the handle to the event.
797 void SignalReplyEvent(Message * pMesssage);
798
799 // Given a message ID, find the matching message in the send queue. If there is no match, return NULL.
800 // If there is a match, remove the message from the send queue and return it.
801 Message * RemoveMessageFromSendQueue(DWORD dwMessageId);
802#endif
803
804#ifndef RIGHT_SIDE_COMPILE
805 // Check read and optionally write memory access to the specified range of bytes. Used to check
806 // ReadProcessMemory and WriteProcessMemory requests.
807 HRESULT CheckBufferAccess(PBYTE pbBuffer, DWORD cbBuffer, bool fWriteAccess);
808#endif // !RIGHT_SIDE_COMPILE
809
810 // Initialize all session state to correct starting values. Used during Init() and on the LS when we
811 // gracefully close one session and prepare for another.
812 void InitSessionState();
813
814 // The entry point of the transport worker thread. This one's static, so we immediately dispatch to an
815 // instance method version defined below for convenience in the implementation.
816 static DWORD WINAPI TransportWorkerStatic(LPVOID pvContext);
817 void TransportWorker();
818
819 // Given a fully initialized debugger event structure, return the size of the structure in bytes (this is
820 // not trivial since DebuggerIPCEvent contains a large union member which can cause the portion containing
821 // significant data to vary wildy from event to event).
822 DWORD GetEventSize(DebuggerIPCEvent *pEvent);
823
824#ifdef _DEBUG
825 // Debug helper which returns the name associated with a MessageType.
826 const char *MessageName(MessageType eType);
827
828 // Debug logging helper which logs an incoming message of any type (as long as logging for that message
829 // class is currently enabled).
830 void DbgTransportLogMessageReceived(MessageHeader *pHeader);
831
832 // Helper method used by the DBG_TRANSPORT_SHOULD_INJECT_FAULT macro.
833 bool DbgTransportShouldInjectFault(DbgTransportFaultOp eOp, const char *szOpName);
834#else // _DEBUG
835#define DbgTransportLogMessageReceived(x)
836#endif // _DEBUG
837};
838
839#ifndef RIGHT_SIDE_COMPILE
840// The one and only transport instance for the left side. Allocated and initialized during EE startup (from
841// Debugger::Startup() in debugger.cpp).
842extern DbgTransportSession *g_pDbgTransport;
843#endif // !RIGHT_SIDE_COMPILE
844
845#define DBG_GET_LAST_WSA_ERROR() WSAGetLastError()
846
847#endif // defined(FEATURE_DBGIPC_TRANSPORT_VM) || defined(FEATURE_DBGIPC_TRANSPORT_DI)
848
849#endif // __DBG_TRANSPORT_SESSION_INCLUDED
850