mirror of
https://github.com/ZuneDev/MicrosoftIris.git
synced 2026-07-27 13:13:29 -07:00
Merge branch 'master' of https://github.com/ZuneDev/MicrosoftIris
This commit is contained in:
@@ -1,6 +1,8 @@
|
||||
using Microsoft.Iris.Debug;
|
||||
using Microsoft.Iris.Debug.Data;
|
||||
using Microsoft.Iris.Debug.SystemNet;
|
||||
using System;
|
||||
using System.Threading;
|
||||
|
||||
namespace SimpleDebugClient;
|
||||
|
||||
@@ -10,25 +12,44 @@ internal class Program
|
||||
|
||||
static int Main(string[] args)
|
||||
{
|
||||
string connectionString = args.Length >= 2
|
||||
? args[1] : "tcp://127.0.0.1:5556";
|
||||
var connectionString = args.Length >= 2
|
||||
? new Uri(args[1]) : DebugRemoting.DEFAULT_TCP_URI;
|
||||
|
||||
Console.CancelKeyPress += Console_CancelKeyPress;
|
||||
|
||||
Debugger = new ZmqDebuggerClient(connectionString);
|
||||
Debugger.DispatcherStep += Debugger_DispatcherStep;
|
||||
Debugger = new NetDebuggerClient(connectionString);
|
||||
//Debugger.DispatcherStep += Debugger_DispatcherStep;
|
||||
Debugger.InterpreterStep += Debugger_InterpreterStep;
|
||||
Debugger.InterpreterStateChanged += Debugger_InterpreterStateChanged;
|
||||
|
||||
Console.WriteLine("Listening for debug messages. Press Ctrl-C to exit.");
|
||||
Console.ReadLine();
|
||||
Console.WriteLine($"Listening for debug messages at '{Debugger.ConnectionUri}'. Press Ctrl-C or 'x' to exit.");
|
||||
|
||||
while (true)
|
||||
{
|
||||
var cmd = Console.ReadLine();
|
||||
if (cmd[0] == 'x')
|
||||
{
|
||||
if (Debugger is IDisposable debugger)
|
||||
debugger.Dispose();
|
||||
break;
|
||||
}
|
||||
|
||||
switch (cmd[0])
|
||||
{
|
||||
case 's':
|
||||
Debugger.DebuggerCommand = InterpreterCommand.Step;
|
||||
break;
|
||||
|
||||
case 'c':
|
||||
Debugger.DebuggerCommand = InterpreterCommand.Continue;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
private static void Console_CancelKeyPress(object sender, ConsoleCancelEventArgs e)
|
||||
private static void Debugger_InterpreterStateChanged(InterpreterCommand state)
|
||||
{
|
||||
if (Debugger is IDisposable debugger)
|
||||
debugger.Dispose();
|
||||
Console.WriteLine($"Interpreter is in {state} mode");
|
||||
}
|
||||
|
||||
private static void Debugger_DispatcherStep(string obj)
|
||||
@@ -38,6 +59,8 @@ internal class Program
|
||||
|
||||
private static void Debugger_InterpreterStep(object? sender, InterpreterEntry e)
|
||||
{
|
||||
if (e.LoadUri.EndsWith("TopToolbarSignIn.uix"))
|
||||
return;
|
||||
Console.WriteLine($"[Interpreter] {e}");
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
using Microsoft.Iris;
|
||||
using Microsoft.Iris.Debug;
|
||||
using System;
|
||||
|
||||
namespace SimpleIrisApp;
|
||||
@@ -19,7 +20,7 @@ internal class Program
|
||||
};
|
||||
|
||||
Application.DebugSettings.DebugConnectionUri = args.Length >= 2
|
||||
? args[1] : "tcp://127.0.0.1:5556";
|
||||
? args[1] : DebugRemoting.DEFAULT_TCP_URI.OriginalString;
|
||||
|
||||
Application.DebugSettings.Breakpoints.Add(new("clr-res://SimpleIrisApp!MainPage.uix", 3, 22));
|
||||
#endif
|
||||
@@ -32,6 +33,7 @@ internal class Program
|
||||
Application.Window.RequestLoad("clr-res://SimpleIrisApp!MainPage.uix#Frame");
|
||||
|
||||
Application.Run(OnInitialLoadComplete);
|
||||
Application.Shutdown();
|
||||
}
|
||||
|
||||
static void OnInitialLoadComplete(object arg)
|
||||
|
||||
@@ -195,7 +195,7 @@ namespace Microsoft.Iris
|
||||
|
||||
if (DebugSettings.DebugConnectionUri != null)
|
||||
{
|
||||
Debugger = new Debug.NetMQ.ZmqDebuggerServer(DebugSettings.DebugConnectionUri);
|
||||
Debugger = new Debug.SystemNet.NetDebuggerServer(DebugSettings.DebugConnectionUri);
|
||||
DebuggerServerReady?.Invoke(Debugger, EventArgs.Empty);
|
||||
}
|
||||
|
||||
@@ -309,7 +309,11 @@ namespace Microsoft.Iris
|
||||
if (s_initializationState == InitializationState.InitializedWithoutUI)
|
||||
RenderApi.ShutdownForToolOnly();
|
||||
StaticServices.Uninitialize();
|
||||
|
||||
Debug.Trace.Shutdown();
|
||||
if (Debugger is IDisposable disposable)
|
||||
disposable.Dispose();
|
||||
|
||||
ErrorManager.OnErrors -= new NotifyErrorBatch(NotifyErrorBatchHandler);
|
||||
s_initializationState = InitializationState.NotInitialized;
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ using System.Text;
|
||||
|
||||
namespace Microsoft.Iris.Debug.Data;
|
||||
|
||||
[Serializable]
|
||||
public struct Breakpoint : IEquatable<Breakpoint>
|
||||
{
|
||||
public Breakpoint(string uri, int line, int column, bool enabled = true) : this(uri, enabled)
|
||||
@@ -32,7 +33,16 @@ public struct Breakpoint : IEquatable<Breakpoint>
|
||||
|
||||
public bool Enabled { get; set; }
|
||||
|
||||
public bool Equals(string uri, int line, int column) => uri == Uri && line == Line && column == Column;
|
||||
public bool Equals(string uri, int line, int column, uint offset = uint.MaxValue)
|
||||
{
|
||||
if (!Uri.Equals(uri, StringComparison.OrdinalIgnoreCase))
|
||||
return false;
|
||||
|
||||
if (Offset != uint.MaxValue && offset != uint.MaxValue)
|
||||
return Offset == offset;
|
||||
else
|
||||
return line == Line && column == Column;
|
||||
}
|
||||
|
||||
public bool Equals(Breakpoint other) => Equals(other.Uri, other.Line, other.Column);
|
||||
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Runtime.Serialization;
|
||||
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);
|
||||
|
||||
public T DeserializeData<T>(IFormatter formatter)
|
||||
{
|
||||
using MemoryStream stream = new(Data);
|
||||
return (T)formatter.Deserialize(stream);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
namespace Microsoft.Iris.Debug.Data;
|
||||
|
||||
public enum InterpreterCommand : byte
|
||||
{
|
||||
Continue,
|
||||
Break,
|
||||
Step,
|
||||
}
|
||||
@@ -1,4 +1,7 @@
|
||||
using System.Runtime.Serialization;
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Net.Sockets;
|
||||
using System.Runtime.Serialization;
|
||||
|
||||
namespace Microsoft.Iris.Debug;
|
||||
|
||||
@@ -7,9 +10,41 @@ 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;
|
||||
}
|
||||
}
|
||||
|
||||
internal static byte[] Serialize(this object obj, IFormatter formatter)
|
||||
{
|
||||
using MemoryStream dataStream = new();
|
||||
formatter.Serialize(dataStream, obj);
|
||||
return dataStream.ToArray();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,4 +6,7 @@ public enum DebuggerMessageType : int
|
||||
|
||||
InterpreterOpCode,
|
||||
DispatcherStep,
|
||||
|
||||
UpdateBreakpoint,
|
||||
InterpreterCommand,
|
||||
}
|
||||
|
||||
@@ -8,7 +8,11 @@ public interface IDebuggerClient
|
||||
/// <summary>
|
||||
/// The URI the client is connected to.
|
||||
/// </summary>
|
||||
string ConnectionUri { get; }
|
||||
Uri ConnectionUri { get; }
|
||||
|
||||
InterpreterCommand DebuggerCommand { get; set; }
|
||||
|
||||
event Action<InterpreterCommand> InterpreterStateChanged;
|
||||
|
||||
/// <summary>
|
||||
/// Fired when the UIX interpreter steps forward.
|
||||
@@ -19,4 +23,6 @@ public interface IDebuggerClient
|
||||
/// Fired when the UIX dispatcher executes another call from the queue.
|
||||
/// </summary>
|
||||
event Action<string> DispatcherStep;
|
||||
|
||||
void UpdateBreakpoint(Breakpoint breakpoint);
|
||||
}
|
||||
|
||||
@@ -4,15 +4,17 @@ namespace Microsoft.Iris.Debug;
|
||||
|
||||
internal interface IDebuggerServer
|
||||
{
|
||||
InterpreterCommand DebuggerCommand { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Logs the context, opcode, and arguments of an instruction
|
||||
/// executed by <c>Microsoft.Iris.Markup.Interpreter</c>.
|
||||
/// </summary>
|
||||
public void LogInterpreterOpCode(object context, InterpreterEntry entry);
|
||||
void LogInterpreterOpCode(object context, InterpreterEntry entry);
|
||||
|
||||
/// <summary>
|
||||
/// Logs the string representation of a dispatcher step.
|
||||
/// </summary>
|
||||
/// <param name="message"></param>
|
||||
public void LogDispatcher(string message);
|
||||
void LogDispatcher(string message);
|
||||
}
|
||||
|
||||
@@ -1,79 +0,0 @@
|
||||
using Microsoft.Iris.Debug;
|
||||
using Microsoft.Iris.Debug.Data;
|
||||
using NetMQ;
|
||||
using NetMQ.Sockets;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Runtime.Serialization;
|
||||
using System.Text;
|
||||
|
||||
namespace Microsoft.Iris.Debug.NetMQ;
|
||||
|
||||
public class ZmqDebuggerClient : IDebuggerClient, IDisposable
|
||||
{
|
||||
private List<byte[]> _frames = new(2);
|
||||
private readonly PairSocket _socket;
|
||||
private readonly IFormatter _formatter;
|
||||
|
||||
public string ConnectionUri { get; }
|
||||
|
||||
public event EventHandler<InterpreterEntry> InterpreterStep;
|
||||
public event Action<string> DispatcherStep;
|
||||
|
||||
public ZmqDebuggerClient(string connectionUri)
|
||||
{
|
||||
ConnectionUri = connectionUri ?? DebugRemoting.DEFAULT_TCP_CLIENT_URI;
|
||||
|
||||
_socket = new(connectionUri);
|
||||
_formatter = DebugRemoting.CreateBsonFormatter();
|
||||
|
||||
System.Threading.Thread th = new(MessageRecieveLoop);
|
||||
th.Start();
|
||||
}
|
||||
|
||||
public void Dispose() => _socket.Dispose();
|
||||
|
||||
private void MessageRecieveLoop()
|
||||
{
|
||||
while (!_socket.IsDisposed)
|
||||
{
|
||||
DebuggerMessageType type;
|
||||
byte[] bytes;
|
||||
while (!TryRecieveDebuggerMessage(out type, out bytes))
|
||||
;
|
||||
|
||||
switch (type)
|
||||
{
|
||||
case DebuggerMessageType.InterpreterOpCode:
|
||||
{
|
||||
using MemoryStream stream = new(bytes);
|
||||
var entry = (InterpreterEntry)_formatter.Deserialize(stream);
|
||||
InterpreterStep?.Invoke(this, entry);
|
||||
}
|
||||
break;
|
||||
|
||||
case DebuggerMessageType.DispatcherStep:
|
||||
string message = Encoding.Unicode.GetString(bytes);
|
||||
DispatcherStep?.Invoke(message);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private bool TryRecieveDebuggerMessage(out DebuggerMessageType type, out byte[] bytes)
|
||||
{
|
||||
if (!_socket.IsDisposed && _socket.TryReceiveMultipartBytes(ref _frames, 2))
|
||||
{
|
||||
type = (DebuggerMessageType)BitConverter.ToInt32(_frames[0], 0);
|
||||
bytes = _frames[1];
|
||||
return true;
|
||||
}
|
||||
|
||||
type = default;
|
||||
bytes = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
internal static IFormatter CreateFormatter() => new BsonFormatter(new StreamingContext(StreamingContextStates.Remoting));
|
||||
}
|
||||
@@ -1,55 +0,0 @@
|
||||
using NetMQ;
|
||||
using NetMQ.Sockets;
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Runtime.Serialization;
|
||||
using System.Text;
|
||||
|
||||
namespace Microsoft.Iris.Debug.NetMQ;
|
||||
|
||||
internal class ZmqDebuggerServer : IDebuggerServer, IDisposable
|
||||
{
|
||||
public static IDebuggerServer Current { get; private set; }
|
||||
|
||||
private readonly PairSocket _socket;
|
||||
private readonly byte[][] _messageFrame = new byte[2][];
|
||||
private readonly IFormatter _formatter;
|
||||
|
||||
public ZmqDebuggerServer(string connectionUri)
|
||||
{
|
||||
_socket = new(connectionUri ?? DebugRemoting.DEFAULT_TCP_SERVER_URI);
|
||||
|
||||
_formatter = DebugRemoting.CreateBsonFormatter();
|
||||
|
||||
Current = this;
|
||||
}
|
||||
|
||||
public void LogInterpreterOpCode(object context, Data.InterpreterEntry entry)
|
||||
{
|
||||
using MemoryStream entryStream = new();
|
||||
_formatter.Serialize(entryStream, entry);
|
||||
|
||||
SendDebuggerMessage(DebuggerMessageType.InterpreterOpCode, entryStream.ToArray());
|
||||
}
|
||||
|
||||
public void LogDispatcher(string message)
|
||||
{
|
||||
SendDebuggerMessage(DebuggerMessageType.DispatcherStep, message);
|
||||
}
|
||||
|
||||
public void Dispose() => _socket.Dispose();
|
||||
|
||||
private void SendDebuggerMessage(DebuggerMessageType type, string message, Encoding encoding = null)
|
||||
{
|
||||
var messageBytes = (encoding ?? Encoding.Unicode).GetBytes(message);
|
||||
SendDebuggerMessage(type, messageBytes);
|
||||
}
|
||||
|
||||
private void SendDebuggerMessage(DebuggerMessageType type, byte[] bytes)
|
||||
{
|
||||
_messageFrame[0] = BitConverter.GetBytes((int)type);
|
||||
_messageFrame[1] = bytes;
|
||||
|
||||
_socket.SendMultipartBytes(_messageFrame);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,129 @@
|
||||
using Microsoft.Iris.Debug.Data;
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Net;
|
||||
using System.Net.Sockets;
|
||||
using System.Runtime.Serialization;
|
||||
|
||||
namespace Microsoft.Iris.Debug.SystemNet;
|
||||
|
||||
public class NetDebuggerClient : IDebuggerClient, IDisposable
|
||||
{
|
||||
private readonly Socket _socket;
|
||||
private readonly ConcurrentQueue<byte[]> _outQueue = new();
|
||||
private readonly IFormatter _formatter;
|
||||
private InterpreterCommand _uibCommand = InterpreterCommand.Continue;
|
||||
|
||||
public Uri ConnectionUri { get; }
|
||||
|
||||
public InterpreterCommand DebuggerCommand
|
||||
{
|
||||
get => _uibCommand;
|
||||
set
|
||||
{
|
||||
var data = new[] { (byte)value };
|
||||
QueueDebuggerMessage(new(0, DebuggerMessageType.InterpreterCommand, data));
|
||||
_uibCommand = value;
|
||||
}
|
||||
}
|
||||
|
||||
public event EventHandler<InterpreterEntry> InterpreterStep;
|
||||
public event Action<string> DispatcherStep;
|
||||
public event Action<InterpreterCommand> InterpreterStateChanged;
|
||||
|
||||
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) { IsBackground = true };
|
||||
connectThread.Start();
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (_socket != null)
|
||||
{
|
||||
if (_socket.Connected)
|
||||
_socket.Disconnect(false);
|
||||
_socket.Close();
|
||||
}
|
||||
}
|
||||
|
||||
public void UpdateBreakpoint(Breakpoint breakpoint)
|
||||
{
|
||||
QueueDebuggerMessage(new(0, DebuggerMessageType.UpdateBreakpoint, breakpoint.Serialize(_formatter)));
|
||||
}
|
||||
|
||||
private void QueueDebuggerMessage(DebuggerMessageFrame frame)
|
||||
{
|
||||
_outQueue.Enqueue(frame.ToBytes());
|
||||
}
|
||||
|
||||
private void MessageReceiveLoop()
|
||||
{
|
||||
while (_socket.Connected)
|
||||
{
|
||||
DebuggerMessageFrame frame;
|
||||
while ((frame = DebugRemoting.ReceiveDebuggerMessage(_socket)) == null)
|
||||
if (!_socket.Connected) return;
|
||||
|
||||
switch (frame.Type)
|
||||
{
|
||||
case DebuggerMessageType.InterpreterOpCode:
|
||||
var entry = frame.DeserializeData<InterpreterEntry>(_formatter);
|
||||
InterpreterStep?.Invoke(this, entry);
|
||||
break;
|
||||
|
||||
case DebuggerMessageType.DispatcherStep:
|
||||
string message = frame.GetDataAsString();
|
||||
DispatcherStep?.Invoke(message);
|
||||
break;
|
||||
|
||||
case DebuggerMessageType.InterpreterCommand:
|
||||
_uibCommand = (InterpreterCommand)frame.Data[0];
|
||||
InterpreterStateChanged?.Invoke(_uibCommand);
|
||||
break;
|
||||
|
||||
default:
|
||||
Trace.WriteLine(TraceCategory.MarkupDebug, "Recieved unknown debugger message of type '{0}'.", frame.Type);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void MessageSendLoop()
|
||||
{
|
||||
while (_socket.Connected)
|
||||
{
|
||||
byte[] frameBytes;
|
||||
while (!_outQueue.TryDequeue(out frameBytes)) ;
|
||||
|
||||
_socket.Send(BitConverter.GetBytes(frameBytes.Length));
|
||||
_socket.Send(frameBytes);
|
||||
}
|
||||
}
|
||||
|
||||
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(MessageReceiveLoop) { IsBackground = true };
|
||||
System.Threading.Thread sendThread = new(MessageSendLoop) { IsBackground = true };
|
||||
receiveThread.Start();
|
||||
sendThread.Start();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,134 @@
|
||||
using Microsoft.Iris.Debug.Data;
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Net.Sockets;
|
||||
using System.Runtime.Serialization;
|
||||
|
||||
namespace Microsoft.Iris.Debug.SystemNet;
|
||||
|
||||
internal class NetDebuggerServer : IDebuggerServer, IDisposable
|
||||
{
|
||||
public static IDebuggerServer Current { get; private set; }
|
||||
|
||||
private readonly ConcurrentQueue<byte[]> _outQueue = new();
|
||||
private readonly TcpListener _listener;
|
||||
private readonly IFormatter _formatter;
|
||||
private Socket _socket;
|
||||
private bool _disposed = false;
|
||||
private InterpreterCommand _uibCommand = InterpreterCommand.Continue;
|
||||
|
||||
public Uri ConnectionUri { get; }
|
||||
|
||||
public InterpreterCommand DebuggerCommand
|
||||
{
|
||||
get => _uibCommand;
|
||||
set
|
||||
{
|
||||
var data = new[] { (byte)value };
|
||||
QueueDebuggerMessage(new(0, DebuggerMessageType.InterpreterCommand, data));
|
||||
_uibCommand = value;
|
||||
}
|
||||
}
|
||||
|
||||
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) { IsBackground = true };
|
||||
connectThread.Start();
|
||||
|
||||
Current = this;
|
||||
}
|
||||
|
||||
public void LogInterpreterOpCode(object context, InterpreterEntry entry)
|
||||
{
|
||||
QueueDebuggerMessage(new(0, DebuggerMessageType.InterpreterOpCode, entry.Serialize(_formatter)));
|
||||
}
|
||||
|
||||
public void LogDispatcher(string message)
|
||||
{
|
||||
QueueDebuggerMessage(new(0, DebuggerMessageType.DispatcherStep, message));
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (_disposed)
|
||||
return;
|
||||
|
||||
_disposed = true;
|
||||
_listener.Stop();
|
||||
_socket?.Dispose();
|
||||
}
|
||||
|
||||
private void QueueDebuggerMessage(DebuggerMessageFrame frame)
|
||||
{
|
||||
_outQueue.Enqueue(frame.ToBytes());
|
||||
}
|
||||
|
||||
private void MessageReceiveLoop()
|
||||
{
|
||||
while (_socket.Connected)
|
||||
{
|
||||
DebuggerMessageFrame frame;
|
||||
while ((frame = DebugRemoting.ReceiveDebuggerMessage(_socket)) == null)
|
||||
if (!_socket.Connected) return;
|
||||
|
||||
switch (frame.Type)
|
||||
{
|
||||
case DebuggerMessageType.UpdateBreakpoint:
|
||||
var breakpoint = frame.DeserializeData<Breakpoint>(_formatter);
|
||||
if (breakpoint.Enabled)
|
||||
Application.DebugSettings.Breakpoints.Add(breakpoint);
|
||||
else
|
||||
Application.DebugSettings.Breakpoints.Remove(breakpoint);
|
||||
break;
|
||||
|
||||
case DebuggerMessageType.InterpreterCommand:
|
||||
_uibCommand = (InterpreterCommand)frame.Data[0];
|
||||
break;
|
||||
|
||||
default:
|
||||
Trace.WriteLine(TraceCategory.MarkupDebug, "Recieved unknown debugger message of type '{0}'.", frame.Type);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void MessageSendLoop()
|
||||
{
|
||||
while (_socket.Connected)
|
||||
{
|
||||
byte[] frameBytes;
|
||||
while (!_outQueue.TryDequeue(out frameBytes)) ;
|
||||
|
||||
try
|
||||
{
|
||||
_socket.Send(BitConverter.GetBytes(frameBytes.Length));
|
||||
_socket.Send(frameBytes);
|
||||
}
|
||||
catch (SocketException) { }
|
||||
}
|
||||
}
|
||||
|
||||
private void ConnectLoop()
|
||||
{
|
||||
while (!_listener.Pending())
|
||||
if (_disposed) return;
|
||||
|
||||
_socket = _listener.AcceptSocket();
|
||||
|
||||
System.Threading.Thread receiveThread = new(MessageReceiveLoop) { IsBackground = true };
|
||||
System.Threading.Thread sendThread = new(MessageSendLoop) { IsBackground = true };
|
||||
receiveThread.Start();
|
||||
sendThread.Start();
|
||||
}
|
||||
}
|
||||
@@ -78,18 +78,21 @@ namespace Microsoft.Iris.Markup
|
||||
if (debugging && context.LoadResult.LineNumberTable.TryLookup(reader.CurrentOffset, out int line, out int column))
|
||||
{
|
||||
bool ShouldBreak(Breakpoint b)
|
||||
=> b.Enabled
|
||||
&& b.Uri.Equals(loadResult.Uri, StringComparison.OrdinalIgnoreCase)
|
||||
&& (b.Offset == reader.CurrentOffset || (b.Line == line && b.Column == column));
|
||||
=> b.Enabled && b.Equals(loadResult.Uri, line, column, reader.CurrentOffset);
|
||||
|
||||
// Check if a breakpoint has been set at this location
|
||||
bool shouldBreakHere = Application.DebugSettings.Breakpoints.Any(ShouldBreak);
|
||||
if (shouldBreakHere)
|
||||
{
|
||||
System.Diagnostics.Debugger.Break();
|
||||
Application.Debugger.DebuggerCommand = InterpreterCommand.Break;
|
||||
//System.Diagnostics.Debugger.Break();
|
||||
}
|
||||
}
|
||||
|
||||
while (Application.Debugger.DebuggerCommand == InterpreterCommand.Break) ;
|
||||
if (Application.Debugger.DebuggerCommand == InterpreterCommand.Step)
|
||||
Application.Debugger.DebuggerCommand = InterpreterCommand.Break;
|
||||
|
||||
switch (opCode)
|
||||
{
|
||||
case OpCode.ConstructObject:
|
||||
|
||||
@@ -11,7 +11,6 @@
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\UIX.RenderApi\UIX.RenderApi.csproj" />
|
||||
<PackageReference Include="NetMQ" Version="4.0.1.12" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
|
||||
<PackageReference Include="Newtonsoft.Json.Bson" Version="1.0.2" />
|
||||
</ItemGroup>
|
||||
|
||||
Reference in New Issue
Block a user