Skip to content

Commit 24cfdb2

Browse files
authored
Add tooling TCP/IP support to enable diagnostics on IOS/Android runtime platforms. (#2050)
1 parent 38244ba commit 24cfdb2

18 files changed

+1829
-123
lines changed

diagnostics.sln

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -201,6 +201,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Diagnostics.Debug
201201
EndProject
202202
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ExitCodeTracee", "src\tests\ExitCodeTracee\ExitCodeTracee.csproj", "{61F73DD0-F346-4D7A-AB12-63415B4EEEE1}"
203203
EndProject
204+
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "dotnet-dsrouter", "src\Tools\dotnet-dsrouter\dotnet-dsrouter.csproj", "{2BD55143-B102-4FEC-84AD-DC3B330FA9AC}"
205+
EndProject
204206
Global
205207
GlobalSection(SolutionConfigurationPlatforms) = preSolution
206208
Checked|Any CPU = Checked|Any CPU
@@ -1618,6 +1620,46 @@ Global
16181620
{61F73DD0-F346-4D7A-AB12-63415B4EEEE1}.RelWithDebInfo|x64.Build.0 = Release|Any CPU
16191621
{61F73DD0-F346-4D7A-AB12-63415B4EEEE1}.RelWithDebInfo|x86.ActiveCfg = Release|Any CPU
16201622
{61F73DD0-F346-4D7A-AB12-63415B4EEEE1}.RelWithDebInfo|x86.Build.0 = Release|Any CPU
1623+
{2BD55143-B102-4FEC-84AD-DC3B330FA9AC}.Checked|Any CPU.ActiveCfg = Debug|Any CPU
1624+
{2BD55143-B102-4FEC-84AD-DC3B330FA9AC}.Checked|Any CPU.Build.0 = Debug|Any CPU
1625+
{2BD55143-B102-4FEC-84AD-DC3B330FA9AC}.Checked|ARM.ActiveCfg = Debug|Any CPU
1626+
{2BD55143-B102-4FEC-84AD-DC3B330FA9AC}.Checked|ARM.Build.0 = Debug|Any CPU
1627+
{2BD55143-B102-4FEC-84AD-DC3B330FA9AC}.Checked|ARM64.ActiveCfg = Debug|Any CPU
1628+
{2BD55143-B102-4FEC-84AD-DC3B330FA9AC}.Checked|ARM64.Build.0 = Debug|Any CPU
1629+
{2BD55143-B102-4FEC-84AD-DC3B330FA9AC}.Checked|x64.ActiveCfg = Debug|Any CPU
1630+
{2BD55143-B102-4FEC-84AD-DC3B330FA9AC}.Checked|x64.Build.0 = Debug|Any CPU
1631+
{2BD55143-B102-4FEC-84AD-DC3B330FA9AC}.Checked|x86.ActiveCfg = Debug|Any CPU
1632+
{2BD55143-B102-4FEC-84AD-DC3B330FA9AC}.Checked|x86.Build.0 = Debug|Any CPU
1633+
{2BD55143-B102-4FEC-84AD-DC3B330FA9AC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
1634+
{2BD55143-B102-4FEC-84AD-DC3B330FA9AC}.Debug|Any CPU.Build.0 = Debug|Any CPU
1635+
{2BD55143-B102-4FEC-84AD-DC3B330FA9AC}.Debug|ARM.ActiveCfg = Debug|Any CPU
1636+
{2BD55143-B102-4FEC-84AD-DC3B330FA9AC}.Debug|ARM.Build.0 = Debug|Any CPU
1637+
{2BD55143-B102-4FEC-84AD-DC3B330FA9AC}.Debug|ARM64.ActiveCfg = Debug|Any CPU
1638+
{2BD55143-B102-4FEC-84AD-DC3B330FA9AC}.Debug|ARM64.Build.0 = Debug|Any CPU
1639+
{2BD55143-B102-4FEC-84AD-DC3B330FA9AC}.Debug|x64.ActiveCfg = Debug|Any CPU
1640+
{2BD55143-B102-4FEC-84AD-DC3B330FA9AC}.Debug|x64.Build.0 = Debug|Any CPU
1641+
{2BD55143-B102-4FEC-84AD-DC3B330FA9AC}.Debug|x86.ActiveCfg = Debug|Any CPU
1642+
{2BD55143-B102-4FEC-84AD-DC3B330FA9AC}.Debug|x86.Build.0 = Debug|Any CPU
1643+
{2BD55143-B102-4FEC-84AD-DC3B330FA9AC}.Release|Any CPU.ActiveCfg = Release|Any CPU
1644+
{2BD55143-B102-4FEC-84AD-DC3B330FA9AC}.Release|Any CPU.Build.0 = Release|Any CPU
1645+
{2BD55143-B102-4FEC-84AD-DC3B330FA9AC}.Release|ARM.ActiveCfg = Release|Any CPU
1646+
{2BD55143-B102-4FEC-84AD-DC3B330FA9AC}.Release|ARM.Build.0 = Release|Any CPU
1647+
{2BD55143-B102-4FEC-84AD-DC3B330FA9AC}.Release|ARM64.ActiveCfg = Release|Any CPU
1648+
{2BD55143-B102-4FEC-84AD-DC3B330FA9AC}.Release|ARM64.Build.0 = Release|Any CPU
1649+
{2BD55143-B102-4FEC-84AD-DC3B330FA9AC}.Release|x64.ActiveCfg = Release|Any CPU
1650+
{2BD55143-B102-4FEC-84AD-DC3B330FA9AC}.Release|x64.Build.0 = Release|Any CPU
1651+
{2BD55143-B102-4FEC-84AD-DC3B330FA9AC}.Release|x86.ActiveCfg = Release|Any CPU
1652+
{2BD55143-B102-4FEC-84AD-DC3B330FA9AC}.Release|x86.Build.0 = Release|Any CPU
1653+
{2BD55143-B102-4FEC-84AD-DC3B330FA9AC}.RelWithDebInfo|Any CPU.ActiveCfg = Release|Any CPU
1654+
{2BD55143-B102-4FEC-84AD-DC3B330FA9AC}.RelWithDebInfo|Any CPU.Build.0 = Release|Any CPU
1655+
{2BD55143-B102-4FEC-84AD-DC3B330FA9AC}.RelWithDebInfo|ARM.ActiveCfg = Release|Any CPU
1656+
{2BD55143-B102-4FEC-84AD-DC3B330FA9AC}.RelWithDebInfo|ARM.Build.0 = Release|Any CPU
1657+
{2BD55143-B102-4FEC-84AD-DC3B330FA9AC}.RelWithDebInfo|ARM64.ActiveCfg = Release|Any CPU
1658+
{2BD55143-B102-4FEC-84AD-DC3B330FA9AC}.RelWithDebInfo|ARM64.Build.0 = Release|Any CPU
1659+
{2BD55143-B102-4FEC-84AD-DC3B330FA9AC}.RelWithDebInfo|x64.ActiveCfg = Release|Any CPU
1660+
{2BD55143-B102-4FEC-84AD-DC3B330FA9AC}.RelWithDebInfo|x64.Build.0 = Release|Any CPU
1661+
{2BD55143-B102-4FEC-84AD-DC3B330FA9AC}.RelWithDebInfo|x86.ActiveCfg = Release|Any CPU
1662+
{2BD55143-B102-4FEC-84AD-DC3B330FA9AC}.RelWithDebInfo|x86.Build.0 = Release|Any CPU
16211663
EndGlobalSection
16221664
GlobalSection(SolutionProperties) = preSolution
16231665
HideSolutionNode = FALSE
@@ -1669,6 +1711,7 @@ Global
16691711
{5FC66A16-41E9-4D22-A44C-FEBB7DCCAAF8} = {19FAB78C-3351-4911-8F0C-8C6056401740}
16701712
{064BC7DD-D44C-400E-9215-7546E092AB98} = {03479E19-3F18-49A6-910A-F5041E27E7C0}
16711713
{61F73DD0-F346-4D7A-AB12-63415B4EEEE1} = {03479E19-3F18-49A6-910A-F5041E27E7C0}
1714+
{2BD55143-B102-4FEC-84AD-DC3B330FA9AC} = {B62728C8-1267-4043-B46F-5537BBAEC692}
16721715
EndGlobalSection
16731716
GlobalSection(ExtensibilityGlobals) = postSolution
16741717
SolutionGuid = {46465737-C938-44FC-BE1A-4CE139EBB5E0}

src/Microsoft.Diagnostics.NETCore.Client/DiagnosticsIpc/IpcAdvertise.cs

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,8 @@ private IpcAdvertise(byte[] magic, Guid cookie, UInt64 pid, UInt16 future)
3737
RuntimeInstanceCookie = cookie;
3838
}
3939

40+
public static int V1SizeInBytes { get; } = IpcAdvertiseV1SizeInBytes;
41+
4042
public static async Task<IpcAdvertise> ParseAsync(Stream stream, CancellationToken token)
4143
{
4244
byte[] buffer = new byte[IpcAdvertiseV1SizeInBytes];
@@ -78,6 +80,28 @@ public static async Task<IpcAdvertise> ParseAsync(Stream stream, CancellationTok
7880
return new IpcAdvertise(magic, cookie, pid, future);
7981
}
8082

83+
public static async Task SerializeAsync(Stream stream, Guid runtimeInstanceCookie, ulong processId, CancellationToken token)
84+
{
85+
int index = 0;
86+
byte[] buffer = new byte[IpcAdvertiseV1SizeInBytes];
87+
88+
Array.Copy(Magic_V1, buffer, Magic_V1.Length);
89+
index += Magic_V1.Length;
90+
91+
byte[] cookieBuffer = runtimeInstanceCookie.ToByteArray();
92+
Array.Copy(cookieBuffer, 0, buffer, index, cookieBuffer.Length);
93+
index += cookieBuffer.Length;
94+
95+
Array.Copy(BitConverter.GetBytes(processId), 0, buffer, index, sizeof(ulong));
96+
index += sizeof(ulong);
97+
98+
short future = 0;
99+
Array.Copy(BitConverter.GetBytes(future), 0, buffer, index, sizeof(short));
100+
index += sizeof(short);
101+
102+
await stream.WriteAsync(buffer, 0, index).ConfigureAwait(false);
103+
}
104+
81105
public override string ToString()
82106
{
83107
return $"{{ Magic={Magic}; ClrInstanceId={RuntimeInstanceCookie}; ProcessId={ProcessId}; Future={Future} }}";

src/Microsoft.Diagnostics.NETCore.Client/DiagnosticsIpc/IpcServerTransport.cs

Lines changed: 125 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
using System;
66
using System.IO;
77
using System.IO.Pipes;
8+
using System.Net;
89
using System.Net.Sockets;
910
using System.Runtime.InteropServices;
1011
using System.Threading;
@@ -17,18 +18,30 @@ internal abstract class IpcServerTransport : IDisposable
1718
private IIpcServerTransportCallbackInternal _callback;
1819
private bool _disposed;
1920

20-
public static IpcServerTransport Create(string transportPath, int maxConnections)
21+
public static IpcServerTransport Create(string address, int maxConnections, bool enableTcpIpProtocol, IIpcServerTransportCallbackInternal transportCallback = null)
2122
{
22-
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
23+
if (!enableTcpIpProtocol || !IpcTcpSocketEndPoint.IsTcpIpEndPoint(address))
2324
{
24-
return new WindowsPipeServerTransport(transportPath, maxConnections);
25+
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
26+
{
27+
return new IpcWindowsNamedPipeServerTransport(address, maxConnections, transportCallback);
28+
}
29+
else
30+
{
31+
return new IpcUnixDomainSocketServerTransport(address, maxConnections, transportCallback);
32+
}
2533
}
2634
else
2735
{
28-
return new UnixDomainSocketServerTransport(transportPath, maxConnections);
36+
return new IpcTcpSocketServerTransport(address, maxConnections, transportCallback);
2937
}
3038
}
3139

40+
protected IpcServerTransport(IIpcServerTransportCallbackInternal transportCallback = null)
41+
{
42+
_callback = transportCallback;
43+
}
44+
3245
public void Dispose()
3346
{
3447
if (!_disposed)
@@ -45,18 +58,10 @@ protected virtual void Dispose(bool disposing)
4558

4659
public abstract Task<Stream> AcceptAsync(CancellationToken token);
4760

48-
public static int MaxAllowedConnections
49-
{
61+
public static int MaxAllowedConnections {
5062
get
5163
{
52-
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
53-
{
54-
return NamedPipeServerStream.MaxAllowedServerInstances;
55-
}
56-
else
57-
{
58-
return (int)SocketOptionName.MaxConnections;
59-
}
64+
return -1;
6065
}
6166
}
6267

@@ -73,13 +78,13 @@ internal void SetCallback(IIpcServerTransportCallbackInternal callback)
7378
_callback = callback;
7479
}
7580

76-
protected void OnCreateNewServer()
81+
protected void OnCreateNewServer(EndPoint localEP)
7782
{
78-
_callback?.CreatedNewServer();
83+
_callback?.CreatedNewServer(localEP);
7984
}
8085
}
8186

82-
internal sealed class WindowsPipeServerTransport : IpcServerTransport
87+
internal sealed class IpcWindowsNamedPipeServerTransport : IpcServerTransport
8388
{
8489
private const string PipePrefix = @"\\.\pipe\";
8590

@@ -89,11 +94,12 @@ internal sealed class WindowsPipeServerTransport : IpcServerTransport
8994
private readonly string _pipeName;
9095
private readonly int _maxInstances;
9196

92-
public WindowsPipeServerTransport(string pipeName, int maxInstances)
97+
public IpcWindowsNamedPipeServerTransport(string pipeName, int maxInstances, IIpcServerTransportCallbackInternal transportCallback = null)
98+
: base(transportCallback)
9399
{
94-
_maxInstances = maxInstances;
100+
_maxInstances = maxInstances != MaxAllowedConnections ? maxInstances : NamedPipeServerStream.MaxAllowedServerInstances;
95101
_pipeName = pipeName.StartsWith(PipePrefix) ? pipeName.Substring(PipePrefix.Length) : pipeName;
96-
CreateNewPipeServer();
102+
_stream = CreateNewNamedPipeServer(_pipeName, _maxInstances);
97103
}
98104

99105
protected override void Dispose(bool disposing)
@@ -113,50 +119,50 @@ public override async Task<Stream> AcceptAsync(CancellationToken token)
113119
VerifyNotDisposed();
114120

115121
using var linkedSource = CancellationTokenSource.CreateLinkedTokenSource(token, _cancellation.Token);
116-
117-
NamedPipeServerStream connectedStream;
118122
try
119123
{
124+
// Connect client to named pipe server stream.
120125
await _stream.WaitForConnectionAsync(linkedSource.Token).ConfigureAwait(false);
121126

122-
connectedStream = _stream;
127+
// Transfer ownership of connected named pipe.
128+
var connectedStream = _stream;
129+
130+
// Setup new named pipe server stream used in upcomming accept calls.
131+
_stream = CreateNewNamedPipeServer(_pipeName, _maxInstances);
132+
133+
return connectedStream;
123134
}
124-
finally
135+
catch (Exception)
125136
{
126-
if (!_cancellation.IsCancellationRequested)
137+
// Keep named pipe server stream when getting any kind of cancel request.
138+
// Cancel happens when complete transport is about to disposed or caller
139+
// cancels out specific accept call, no need to recycle named pipe server stream.
140+
// In all other exception scenarios named pipe server stream will be re-created.
141+
if (!linkedSource.IsCancellationRequested)
127142
{
128-
CreateNewPipeServer();
143+
_stream.Dispose();
144+
_stream = CreateNewNamedPipeServer(_pipeName, _maxInstances);
129145
}
146+
throw;
130147
}
131-
return connectedStream;
132148
}
133149

134-
private void CreateNewPipeServer()
150+
private NamedPipeServerStream CreateNewNamedPipeServer(string pipeName, int maxInstances)
135151
{
136-
_stream = new NamedPipeServerStream(
137-
_pipeName,
138-
PipeDirection.InOut,
139-
_maxInstances,
140-
PipeTransmissionMode.Byte,
141-
PipeOptions.Asynchronous);
142-
OnCreateNewServer();
152+
var stream = new NamedPipeServerStream(pipeName, PipeDirection.InOut, maxInstances, PipeTransmissionMode.Byte, PipeOptions.Asynchronous);
153+
OnCreateNewServer(null);
154+
return stream;
143155
}
144156
}
145157

146-
internal sealed class UnixDomainSocketServerTransport : IpcServerTransport
158+
internal abstract class IpcSocketServerTransport : IpcServerTransport
147159
{
148160
private readonly CancellationTokenSource _cancellation = new CancellationTokenSource();
149-
private readonly int _backlog;
150-
private readonly string _path;
161+
protected IpcSocket _socket;
151162

152-
private UnixDomainSocket _socket;
153-
154-
public UnixDomainSocketServerTransport(string path, int backlog)
163+
protected IpcSocketServerTransport(IIpcServerTransportCallbackInternal transportCallback = null)
164+
: base(transportCallback)
155165
{
156-
_backlog = backlog;
157-
_path = path;
158-
159-
CreateNewSocketServer();
160166
}
161167

162168
protected override void Dispose(bool disposing)
@@ -187,33 +193,96 @@ public override async Task<Stream> AcceptAsync(CancellationToken token)
187193
using var linkedSource = CancellationTokenSource.CreateLinkedTokenSource(token, _cancellation.Token);
188194
try
189195
{
190-
Socket socket = await _socket.AcceptAsync(linkedSource.Token).ConfigureAwait(false);
196+
// Accept next client socket.
197+
var socket = await _socket.AcceptAsync(linkedSource.Token).ConfigureAwait(false);
198+
199+
// Configure client socket based on transport type.
200+
OnAccept(socket);
191201

192202
return new ExposedSocketNetworkStream(socket, ownsSocket: true);
193203
}
194204
catch (Exception)
195205
{
196-
// Recreate socket if transport is not disposed.
197-
if (!_cancellation.IsCancellationRequested)
206+
// Keep server socket when getting any kind of cancel request.
207+
// Cancel happens when complete transport is about to disposed or caller
208+
// cancels out specific accept call, no need to recycle server socket.
209+
// In all other exception scenarios server socket will be re-created.
210+
if (!linkedSource.IsCancellationRequested)
198211
{
199-
CreateNewSocketServer();
212+
_socket = CreateNewSocketServer();
200213
}
201214
throw;
202215
}
203216
}
204217

205-
private void CreateNewSocketServer()
218+
internal abstract bool OnAccept(Socket socket);
219+
220+
internal abstract IpcSocket CreateNewSocketServer();
221+
}
222+
223+
internal sealed class IpcTcpSocketServerTransport : IpcSocketServerTransport
224+
{
225+
private readonly int _backlog;
226+
private readonly IpcTcpSocketEndPoint _endPoint;
227+
228+
public IpcTcpSocketServerTransport(string address, int backlog, IIpcServerTransportCallbackInternal transportCallback = null)
229+
: base(transportCallback)
230+
{
231+
_endPoint = new IpcTcpSocketEndPoint(address);
232+
_backlog = backlog != MaxAllowedConnections ? backlog : 100;
233+
_socket = CreateNewSocketServer();
234+
}
235+
236+
internal override bool OnAccept(Socket socket)
237+
{
238+
socket.NoDelay = true;
239+
return true;
240+
}
241+
242+
internal override IpcSocket CreateNewSocketServer()
243+
{
244+
var socket = new IpcSocket(SocketType.Stream, ProtocolType.Tcp);
245+
if (_endPoint.DualMode)
246+
socket.DualMode = _endPoint.DualMode;
247+
socket.Bind(_endPoint);
248+
socket.Listen(_backlog);
249+
socket.LingerState.Enabled = false;
250+
OnCreateNewServer(socket.LocalEndPoint);
251+
return socket;
252+
}
253+
}
254+
255+
internal sealed class IpcUnixDomainSocketServerTransport : IpcSocketServerTransport
256+
{
257+
private readonly int _backlog;
258+
private readonly IpcUnixDomainSocketEndPoint _endPoint;
259+
260+
public IpcUnixDomainSocketServerTransport(string path, int backlog, IIpcServerTransportCallbackInternal transportCallback = null)
261+
: base(transportCallback)
262+
{
263+
_backlog = backlog != MaxAllowedConnections ? backlog : (int)SocketOptionName.MaxConnections;
264+
_endPoint = new IpcUnixDomainSocketEndPoint(path);
265+
_socket = CreateNewSocketServer();
266+
}
267+
268+
internal override bool OnAccept(Socket socket)
269+
{
270+
return true;
271+
}
272+
273+
internal override IpcSocket CreateNewSocketServer()
206274
{
207-
_socket = new UnixDomainSocket();
208-
_socket.Bind(_path);
209-
_socket.Listen(_backlog);
210-
_socket.LingerState.Enabled = false;
211-
OnCreateNewServer();
275+
var socket = new IpcUnixDomainSocket();
276+
socket.Bind(_endPoint);
277+
socket.Listen(_backlog);
278+
socket.LingerState.Enabled = false;
279+
OnCreateNewServer(null);
280+
return socket;
212281
}
213282
}
214283

215284
internal interface IIpcServerTransportCallbackInternal
216285
{
217-
void CreatedNewServer();
286+
void CreatedNewServer(EndPoint localEP);
218287
}
219288
}

0 commit comments

Comments
 (0)