Implement new System.Net.Sockets debugger

This commit is contained in:
Yoshi Askharoun
2023-06-01 20:18:53 -05:00
parent 5401f988b9
commit 3539ed4826
8 changed files with 293 additions and 11 deletions
@@ -0,0 +1,57 @@
using System;
using System.Text;
namespace Microsoft.Iris.Debug.Data;
public class DebuggerMessageFrame
{
public DebuggerMessageFrame() { }
public DebuggerMessageFrame(long transactionId, DebuggerMessageType type, byte[] data)
{
TransactionId = transactionId;
Type = type;
Data = data;
}
public DebuggerMessageFrame(long transactionId, DebuggerMessageType type, string message, Encoding encoding = null)
: this(transactionId, type, (encoding ?? Encoding.UTF8).GetBytes(message))
{
}
public DebuggerMessageFrame(byte[] bytes)
{
TransactionId = BitConverter.ToInt64(bytes, 0);
Type = (DebuggerMessageType)BitConverter.ToInt32(bytes, sizeof(long));
Data = bytes.AsSpan(sizeof(long) + sizeof(DebuggerMessageType)).ToArray();
}
public long TransactionId { get; set; }
public DebuggerMessageType Type { get; set; }
public byte[] Data { get; set; }
public byte[] ToBytes()
{
byte[] bytes = new byte[Data.Length + sizeof(DebuggerMessageType) + sizeof(long)];
#if NET5_0_OR_GREATER
var span = bytes.AsSpan();
BitConverter.TryWriteBytes(span, TransactionId);
BitConverter.TryWriteBytes(span[sizeof(long)..], (int)Type);
#else
var idBytes = BitConverter.GetBytes(TransactionId);
idBytes.CopyTo(bytes, 0);
var typeBytes = BitConverter.GetBytes((int)Type);
typeBytes.CopyTo(bytes, sizeof(long));
#endif
Data.CopyTo(bytes, sizeof(long) + sizeof(DebuggerMessageType));
return bytes;
}
public string GetDataAsString(Encoding encoding = null) => (encoding ?? Encoding.UTF8).GetString(Data);
}
+32 -3
View File
@@ -1,4 +1,8 @@
using System.Runtime.Serialization;
using System;
using System.Collections.Generic;
using System.IO;
using System.Net.Sockets;
using System.Runtime.Serialization;
namespace Microsoft.Iris.Debug;
@@ -7,9 +11,34 @@ namespace Microsoft.Iris.Debug;
/// </summary>
public static class DebugRemoting
{
public const string DEFAULT_TCP_CLIENT_URI = ">tcp://127.0.0.1:5555,@tcp://127.0.0.1:55556";
public const string DEFAULT_TCP_CLIENT_URI = ">tcp://127.0.0.1:5555,@tcp://127.0.0.1:5556";
public const string DEFAULT_TCP_SERVER_URI = "@tcp://127.0.0.1:5555,>tcp://127.0.0.1:5556";
public const string DEFAULT_TCP_SERVER_URI = "@tcp://127.0.0.1:5555,>tcp://127.0.0.1:55556";
public static readonly Uri DEFAULT_TCP_URI = new("tcp://127.0.0.1:5555");
internal static IFormatter CreateBsonFormatter() => new BsonFormatter(new StreamingContext(StreamingContextStates.Remoting));
internal static Data.DebuggerMessageFrame ReceiveDebuggerMessage(Socket socket)
{
try
{
byte[] sizeBuffer = new byte[sizeof(int)];
int bytesReceived = socket.Receive(sizeBuffer);
// Reached end of stream, no bytes to recieve
if (bytesReceived == 0)
return null;
int frameLength = BitConverter.ToInt32(sizeBuffer, 0);
byte[] frameBytes = new byte[frameLength];
socket.Receive(frameBytes, frameLength, SocketFlags.None);
return new(frameBytes);
}
catch (SocketException)
{
return null;
}
}
}
+1 -1
View File
@@ -8,7 +8,7 @@ public interface IDebuggerClient
/// <summary>
/// The URI the client is connected to.
/// </summary>
string ConnectionUri { get; }
Uri ConnectionUri { get; }
/// <summary>
/// Fired when the UIX interpreter steps forward.
@@ -1,4 +1,3 @@
using Microsoft.Iris.Debug;
using Microsoft.Iris.Debug.Data;
using NetMQ;
using NetMQ.Sockets;
@@ -16,14 +15,14 @@ public class ZmqDebuggerClient : IDebuggerClient, IDisposable
private readonly PairSocket _socket;
private readonly IFormatter _formatter;
public string ConnectionUri { get; }
public Uri ConnectionUri { get; }
public event EventHandler<InterpreterEntry> InterpreterStep;
public event Action<string> DispatcherStep;
public ZmqDebuggerClient(string connectionUri)
{
ConnectionUri = connectionUri ?? DebugRemoting.DEFAULT_TCP_CLIENT_URI;
ConnectionUri = new(connectionUri ?? DebugRemoting.DEFAULT_TCP_CLIENT_URI);
_socket = new(connectionUri);
_formatter = DebugRemoting.CreateBsonFormatter();
@@ -0,0 +1,89 @@
using Microsoft.Iris.Debug.Data;
using System;
using System.Collections.Generic;
using System.IO;
using System.Net;
using System.Net.Sockets;
using System.Runtime.Serialization;
using System.Text;
namespace Microsoft.Iris.Debug.SystemNet;
public class NetDebuggerClient : IDebuggerClient, IDisposable
{
public Uri ConnectionUri { get; }
private readonly Socket _socket;
private readonly Queue<byte[]> _queue;
private readonly IFormatter _formatter;
public event EventHandler<InterpreterEntry> InterpreterStep;
public event Action<string> DispatcherStep;
public NetDebuggerClient(string connectionUri) : this(new Uri(connectionUri))
{
}
public NetDebuggerClient(Uri connectionUri)
{
ConnectionUri = connectionUri ?? DebugRemoting.DEFAULT_TCP_URI;
_socket = new(SocketType.Stream, ProtocolType.Tcp);
_formatter = DebugRemoting.CreateBsonFormatter();
System.Threading.Thread connectThread = new(ConnectLoop);
connectThread.Start();
}
public void Dispose() => _socket.Dispose();
private void QueueDebuggerMessage(DebuggerMessageFrame frame)
{
_queue.Enqueue(frame.ToBytes());
}
private void MessageRecieveLoop()
{
while (_socket.Connected)
{
DebuggerMessageFrame frame;
while ((frame = DebugRemoting.ReceiveDebuggerMessage(_socket)) == null)
if (!_socket.Connected) return;
switch (frame.Type)
{
case DebuggerMessageType.InterpreterOpCode:
{
using MemoryStream stream = new(frame.Data);
var entry = (InterpreterEntry)_formatter.Deserialize(stream);
InterpreterStep?.Invoke(this, entry);
}
break;
case DebuggerMessageType.DispatcherStep:
string message = frame.GetDataAsString();
DispatcherStep?.Invoke(message);
break;
default:
Trace.WriteLine(TraceCategory.MarkupDebug, "Recieved unknown debugger message of type '{0}'.", frame.Type);
break;
}
}
}
private void ConnectLoop()
{
while (!_socket.Connected)
{
try
{
var endpoint = new IPEndPoint(IPAddress.Parse(ConnectionUri.Host), ConnectionUri.Port);
_socket.Connect(endpoint);
}
catch { }
}
System.Threading.Thread receiveThread = new(MessageRecieveLoop);
receiveThread.Start();
}
}
@@ -0,0 +1,106 @@
using Microsoft.Iris.Debug.Data;
using System;
using System.Collections.Generic;
using System.IO;
using System.Net;
using System.Net.Sockets;
using System.Runtime.Serialization;
using System.Text;
namespace Microsoft.Iris.Debug.SystemNet;
internal class NetDebuggerServer : IDebuggerServer, IDisposable
{
public static IDebuggerServer Current { get; private set; }
public Uri ConnectionUri { get; }
private readonly Queue<byte[]> _outQueue = new();
private readonly TcpListener _listener;
private readonly IFormatter _formatter;
private Socket _socket;
public NetDebuggerServer(string connectionUri) : this(new Uri(connectionUri))
{
}
public NetDebuggerServer(Uri connectionUri)
{
ConnectionUri = connectionUri ?? DebugRemoting.DEFAULT_TCP_URI;
_listener = TcpListener.Create(connectionUri.Port);
_listener.Start();
_formatter = DebugRemoting.CreateBsonFormatter();
System.Threading.Thread connectThread = new(ConnectLoop);
connectThread.Start();
Current = this;
}
public void LogInterpreterOpCode(object context, InterpreterEntry entry)
{
using MemoryStream entryStream = new();
_formatter.Serialize(entryStream, entry);
QueueDebuggerMessage(new(0, DebuggerMessageType.InterpreterOpCode, entryStream.ToArray()));
}
public void LogDispatcher(string message)
{
QueueDebuggerMessage(new(0, DebuggerMessageType.DispatcherStep, message));
}
public void Dispose()
{
_listener.Stop();
_socket?.Dispose();
}
private void QueueDebuggerMessage(DebuggerMessageFrame frame)
{
_outQueue.Enqueue(frame.ToBytes());
}
private void MessageRecieveLoop()
{
while (_socket.Connected)
{
DebuggerMessageFrame frame;
while ((frame = DebugRemoting.ReceiveDebuggerMessage(_socket)) == null)
;
switch (frame.Type)
{
default:
Trace.WriteLine(TraceCategory.MarkupDebug, "Recieved unknown debugger message of type '{0}'.", frame.Type);
break;
}
}
}
private void MessageSendLoop()
{
while (_socket.Connected)
{
while (_outQueue.Count == 0) ;
var frameBytes = _outQueue.Dequeue();
_socket.Send(BitConverter.GetBytes(frameBytes.Length));
_socket.Send(frameBytes);
}
}
private void ConnectLoop()
{
while (!_listener.Pending()) ;
_socket = _listener.AcceptSocket();
System.Threading.Thread receiveThread = new(MessageRecieveLoop);
System.Threading.Thread sendThread = new(MessageSendLoop);
receiveThread.Start();
sendThread.Start();
}
}