Implement basic interpreter execution controls

This commit is contained in:
Yoshi Askharoun
2023-06-02 00:09:51 -05:00
parent 1dea74d1e5
commit f1a8627057
9 changed files with 153 additions and 45 deletions
+30 -8
View File
@@ -2,6 +2,7 @@
using Microsoft.Iris.Debug.Data;
using Microsoft.Iris.Debug.SystemNet;
using System;
using System.Threading;
namespace SimpleDebugClient;
@@ -14,22 +15,41 @@ internal class Program
var connectionString = args.Length >= 2
? new Uri(args[1]) : DebugRemoting.DEFAULT_TCP_URI;
Console.CancelKeyPress += Console_CancelKeyPress;
Debugger = new NetDebuggerClient(connectionString);
Debugger.DispatcherStep += Debugger_DispatcherStep;
//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)
@@ -39,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 -1
View File
@@ -8,7 +8,7 @@
<ItemGroup>
<ProjectReference Include="..\..\UIX\UIX.csproj" />
<EmbeddedResource Include="Assets\*.*" />
<EmbeddedResource Include="Assets\*.*" />
<EmbeddedResource Include="Pages\*.*" />
</ItemGroup>
@@ -0,0 +1,8 @@
namespace Microsoft.Iris.Debug.Data;
public enum InterpreterCommand : byte
{
Continue,
Break,
Step,
}
@@ -6,4 +6,7 @@ public enum DebuggerMessageType : int
InterpreterOpCode,
DispatcherStep,
UpdateBreakpoint,
InterpreterCommand,
}
@@ -10,6 +10,10 @@ public interface IDebuggerClient
/// </summary>
Uri ConnectionUri { get; }
InterpreterCommand DebuggerCommand { get; set; }
event Action<InterpreterCommand> InterpreterStateChanged;
/// <summary>
/// Fired when the UIX interpreter steps forward.
/// </summary>
@@ -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 -2
View File
@@ -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,24 +1,35 @@
using Microsoft.Iris.Debug.Data;
using System;
using System.Collections.Generic;
using System.IO;
using System.Collections.Concurrent;
using System.Net;
using System.Net.Sockets;
using System.Runtime.Serialization;
using System.Text;
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; }
private readonly Socket _socket;
private readonly Queue<byte[]> _queue;
private readonly IFormatter _formatter;
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))
{
@@ -30,18 +41,31 @@ public class NetDebuggerClient : IDebuggerClient, IDisposable
_socket = new(SocketType.Stream, ProtocolType.Tcp);
_formatter = DebugRemoting.CreateBsonFormatter();
System.Threading.Thread connectThread = new(ConnectLoop);
System.Threading.Thread connectThread = new(ConnectLoop) { IsBackground = true };
connectThread.Start();
}
public void Dispose() => _socket.Dispose();
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)
{
_queue.Enqueue(frame.ToBytes());
_outQueue.Enqueue(frame.ToBytes());
}
private void MessageRecieveLoop()
private void MessageReceiveLoop()
{
while (_socket.Connected)
{
@@ -52,11 +76,8 @@ public class NetDebuggerClient : IDebuggerClient, IDisposable
switch (frame.Type)
{
case DebuggerMessageType.InterpreterOpCode:
{
using MemoryStream stream = new(frame.Data);
var entry = (InterpreterEntry)_formatter.Deserialize(stream);
InterpreterStep?.Invoke(this, entry);
}
var entry = frame.DeserializeData<InterpreterEntry>(_formatter);
InterpreterStep?.Invoke(this, entry);
break;
case DebuggerMessageType.DispatcherStep:
@@ -64,6 +85,11 @@ public class NetDebuggerClient : IDebuggerClient, IDisposable
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;
@@ -71,6 +97,18 @@ public class NetDebuggerClient : IDebuggerClient, IDisposable
}
}
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)
@@ -83,7 +121,9 @@ public class NetDebuggerClient : IDebuggerClient, IDisposable
catch { }
}
System.Threading.Thread receiveThread = new(MessageRecieveLoop);
System.Threading.Thread receiveThread = new(MessageReceiveLoop) { IsBackground = true };
System.Threading.Thread sendThread = new(MessageSendLoop) { IsBackground = true };
receiveThread.Start();
sendThread.Start();
}
}
@@ -1,7 +1,6 @@
using Microsoft.Iris.Debug.Data;
using System;
using System.Collections.Concurrent;
using System.IO;
using System.Net.Sockets;
using System.Runtime.Serialization;
@@ -11,13 +10,25 @@ internal class NetDebuggerServer : IDebuggerServer, IDisposable
{
public static IDebuggerServer Current { get; private set; }
public Uri ConnectionUri { get; }
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))
{
@@ -32,7 +43,7 @@ internal class NetDebuggerServer : IDebuggerServer, IDisposable
_formatter = DebugRemoting.CreateBsonFormatter();
System.Threading.Thread connectThread = new(ConnectLoop);
System.Threading.Thread connectThread = new(ConnectLoop) { IsBackground = true };
connectThread.Start();
Current = this;
@@ -40,10 +51,7 @@ internal class NetDebuggerServer : IDebuggerServer, IDisposable
public void LogInterpreterOpCode(object context, InterpreterEntry entry)
{
using MemoryStream entryStream = new();
_formatter.Serialize(entryStream, entry);
QueueDebuggerMessage(new(0, DebuggerMessageType.InterpreterOpCode, entryStream.ToArray()));
QueueDebuggerMessage(new(0, DebuggerMessageType.InterpreterOpCode, entry.Serialize(_formatter)));
}
public void LogDispatcher(string message)
@@ -66,16 +74,28 @@ internal class NetDebuggerServer : IDebuggerServer, IDisposable
_outQueue.Enqueue(frame.ToBytes());
}
private void MessageRecieveLoop()
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;
@@ -90,8 +110,12 @@ internal class NetDebuggerServer : IDebuggerServer, IDisposable
byte[] frameBytes;
while (!_outQueue.TryDequeue(out frameBytes)) ;
_socket.Send(BitConverter.GetBytes(frameBytes.Length));
_socket.Send(frameBytes);
try
{
_socket.Send(BitConverter.GetBytes(frameBytes.Length));
_socket.Send(frameBytes);
}
catch (SocketException) { }
}
}
@@ -102,8 +126,8 @@ internal class NetDebuggerServer : IDebuggerServer, IDisposable
_socket = _listener.AcceptSocket();
System.Threading.Thread receiveThread = new(MessageRecieveLoop);
System.Threading.Thread sendThread = new(MessageSendLoop);
System.Threading.Thread receiveThread = new(MessageReceiveLoop) { IsBackground = true };
System.Threading.Thread sendThread = new(MessageSendLoop) { IsBackground = true };
receiveThread.Start();
sendThread.Start();
}
+7 -4
View File
@@ -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: