UIX debugger improvements

This commit is contained in:
Yoshi Askharoun
2024-01-20 20:32:30 -06:00
parent 3345ffe590
commit 46833d467f
12 changed files with 181 additions and 43 deletions
+2 -2
View File
@@ -61,7 +61,7 @@ internal class BsonFormatter : IFormatter
var package = (JObject)serializer.Deserialize(reader);
TypeName = package["0"].Value<string>();
SerializedObject = (JObject)package["1"];
SerializedObject = package["1"];
// If the type is accessible from the current domain,
// create an instance of it.
@@ -72,7 +72,7 @@ internal class BsonFormatter : IFormatter
public string TypeName { get; set; }
public JObject SerializedObject { get; set; }
public JToken SerializedObject { get; set; }
public object Object { get; set; }
}
+10 -4
View File
@@ -52,12 +52,18 @@ public struct Breakpoint : IEquatable<Breakpoint>
public override bool Equals(object obj) => obj is Breakpoint bp && Equals(bp);
public override string ToString()
public override string ToString() => ToString(true);
public string ToString(bool includeEnabled)
{
StringBuilder sb = new();
sb.Append(Enabled ? '+' : '-');
sb.Append(' ');
if (includeEnabled)
{
sb.Append(Enabled ? '+' : '-');
sb.Append(' ');
}
sb.Append(Uri);
sb.Append(' ');
@@ -69,5 +75,5 @@ public struct Breakpoint : IEquatable<Breakpoint>
return sb.ToString();
}
public override int GetHashCode() => ToString().GetHashCode();
public override int GetHashCode() => ToString(false).GetHashCode();
}
@@ -1,28 +1,38 @@
using System;
using System.IO;
using System.Runtime.Serialization;
using System.Text;
namespace Microsoft.Iris.Debug.Data;
public class DebuggerMessageFrame
{
public DebuggerMessageFrame() { }
private readonly IFormatter _formatter;
private object _value;
private byte[] _data;
public DebuggerMessageFrame(long transactionId, DebuggerMessageType type, byte[] data)
public DebuggerMessageFrame(long transactionId, DebuggerMessageType type, IFormatter formatter)
{
_formatter = formatter;
TransactionId = transactionId;
Type = type;
}
public DebuggerMessageFrame(long transactionId, DebuggerMessageType type, object value, IFormatter formatter)
: this(transactionId, type, formatter)
{
Value = value;
}
public DebuggerMessageFrame(long transactionId, DebuggerMessageType type, byte[] data, IFormatter formatter)
: this(transactionId, type, formatter)
{
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, IFormatter formatter)
{
}
_formatter = formatter;
public DebuggerMessageFrame(byte[] bytes)
{
TransactionId = BitConverter.ToInt64(bytes, 0);
Type = (DebuggerMessageType)BitConverter.ToInt32(bytes, sizeof(long));
@@ -39,7 +49,46 @@ public class DebuggerMessageFrame
public DebuggerMessageType Type { get; set; }
public byte[] Data { get; set; }
public object Value
{
get
{
if (_value is null)
{
if (_data is null) throw new ArgumentException("Either a value or data must be specified.");
using MemoryStream stream = new(_data);
_value = _formatter.Deserialize(stream);
}
return _value;
}
set
{
_value = value;
_data = null;
}
}
public byte[] Data
{
get
{
if (_data is null)
{
if (_value is null) throw new ArgumentException("Either a value or data must be specified.");
_data = _value.Serialize(_formatter);
}
return _data;
}
set
{
_data = value;
_value = default;
}
}
public byte[] ToBytes()
{
@@ -62,11 +111,33 @@ public class DebuggerMessageFrame
return bytes;
}
public string GetDataAsString(Encoding encoding = null) => (encoding ?? Encoding.UTF8).GetString(Data);
public T DeserializeData<T>(IFormatter formatter)
public virtual T GetValue<T>(IFormatter formatter = null)
{
using MemoryStream stream = new(Data);
return (T)formatter.Deserialize(stream);
return (T)(formatter ?? _formatter).Deserialize(stream);
}
public DebuggerMessageFrame<T> Deserialize<T>(IFormatter formatter = null)
=> new(TransactionId, Type, GetValue<T>(formatter), formatter ?? _formatter);
}
public class DebuggerMessageFrame<T> : DebuggerMessageFrame
{
public DebuggerMessageFrame(byte[] bytes, IFormatter formatter) : base(bytes, formatter)
{
}
public DebuggerMessageFrame(long transactionId, DebuggerMessageType type, IFormatter formatter) : base(transactionId, type, formatter)
{
}
public DebuggerMessageFrame(long transactionId, DebuggerMessageType type, T value, IFormatter formatter) : base(transactionId, type, value, formatter)
{
}
public DebuggerMessageFrame(long transactionId, DebuggerMessageType type, byte[] data, IFormatter formatter) : base(transactionId, type, data, formatter)
{
}
public T GetValue() => (T)Value;
}
@@ -1,3 +1,6 @@
namespace Microsoft.Iris.Debug.Data;
using System;
namespace Microsoft.Iris.Debug.Data;
[Serializable]
public record struct MarkupLineNumberEntry(uint Offset, int Line, int Column);
+2 -2
View File
@@ -17,7 +17,7 @@ public static class DebugRemoting
internal static IFormatter CreateBsonFormatter() => new BsonFormatter(new StreamingContext(StreamingContextStates.Remoting));
internal static Data.DebuggerMessageFrame ReceiveDebuggerMessage(Socket socket)
internal static Data.DebuggerMessageFrame ReceiveDebuggerMessage(Socket socket, IFormatter formatter)
{
try
{
@@ -33,7 +33,7 @@ public static class DebugRemoting
byte[] frameBytes = new byte[frameLength];
socket.Receive(frameBytes, frameLength, SocketFlags.None);
return new(frameBytes);
return new(frameBytes, formatter);
}
catch (SocketException)
{
+1 -1
View File
@@ -14,7 +14,7 @@ public class DebugSettings
public bool GenerateDataMappingModels { get; set; } = false;
public ObservableCollection<DataMappingModel> DataMappingModels { get; } = new();
public List<Breakpoint> Breakpoints { get; } = new();
public HashSet<Breakpoint> Breakpoints { get; } = new();
public string DebugConnectionUri { get; set; }
}
@@ -10,4 +10,6 @@ public enum DebuggerMessageType : int
UpdateBreakpoint,
InterpreterCommand,
LineNumberTable,
}
@@ -23,4 +23,10 @@ public interface IDebuggerClient : IDebuggerState
event Action<string> DispatcherStep;
void UpdateBreakpoint(Breakpoint breakpoint);
/// <summary>
/// Requests the line number table for the given UIX file.
/// </summary>
/// <param name="uri">The URI of the file to get information for.</param>
void RequestLineNumberTable(string uri, Action<MarkupLineNumberEntry[]> callback);
}
@@ -4,6 +4,11 @@ namespace Microsoft.Iris.Debug;
internal interface IDebuggerServer : IDebuggerState
{
/// <summary>
/// Sends the requested line number table.
/// </summary>
MarkupLineNumberEntry[] OnLineNumberTableRequested(string uri);
/// <summary>
/// Logs the context, opcode, and operands of an instruction
/// decoded by <c>Microsoft.Iris.Markup.Interpreter</c>.
@@ -11,8 +11,11 @@ public class NetDebuggerClient : IDebuggerClient, IDisposable
{
private readonly Socket _socket;
private readonly ConcurrentQueue<byte[]> _outQueue = new();
private readonly ConcurrentDictionary<long, Action<object>> _requests = new();
private readonly IFormatter _formatter;
private InterpreterCommand _uibCommand;
private long _nextFreeTransactionId = 1;
public Uri ConnectionUri { get; }
@@ -21,8 +24,7 @@ public class NetDebuggerClient : IDebuggerClient, IDisposable
get => _uibCommand;
set
{
var data = new[] { (byte)value };
QueueDebuggerMessage(new(0, DebuggerMessageType.InterpreterCommand, data));
QueueDebuggerMessage(new(0, DebuggerMessageType.InterpreterCommand, value, _formatter));
_uibCommand = value;
}
}
@@ -57,9 +59,16 @@ public class NetDebuggerClient : IDebuggerClient, IDisposable
}
}
public void RequestLineNumberTable(string uri, Action<MarkupLineNumberEntry[]> callback)
{
DebuggerMessageFrame frame = new(GetNextTransactionId(), DebuggerMessageType.LineNumberTable, uri, _formatter);
_requests.TryAdd(frame.TransactionId, o => callback((MarkupLineNumberEntry[])o));
QueueDebuggerMessage(frame);
}
public void UpdateBreakpoint(Breakpoint breakpoint)
{
QueueDebuggerMessage(new(0, DebuggerMessageType.UpdateBreakpoint, breakpoint.Serialize(_formatter)));
QueueDebuggerMessage(new(0, DebuggerMessageType.UpdateBreakpoint, breakpoint, _formatter));
}
private void QueueDebuggerMessage(DebuggerMessageFrame frame)
@@ -67,36 +76,55 @@ public class NetDebuggerClient : IDebuggerClient, IDisposable
_outQueue.Enqueue(frame.ToBytes());
}
private long GetNextTransactionId()
{
var currentId = _nextFreeTransactionId;
if (currentId == -1)
_nextFreeTransactionId += 2;
else if (currentId == long.MaxValue)
_nextFreeTransactionId = long.MinValue;
else
++_nextFreeTransactionId;
return currentId;
}
private void MessageReceiveLoop()
{
while (_socket.Connected)
{
DebuggerMessageFrame frame;
while ((frame = DebugRemoting.ReceiveDebuggerMessage(_socket)) == null)
while ((frame = DebugRemoting.ReceiveDebuggerMessage(_socket, _formatter)) == null)
if (!_socket.Connected) return;
switch (frame.Type)
{
case DebuggerMessageType.InterpreterDecode:
var decEntry = frame.DeserializeData<InterpreterInstruction>(_formatter);
var decEntry = frame.GetValue<InterpreterInstruction>();
InterpreterDecode?.Invoke(this, decEntry);
break;
case DebuggerMessageType.InterpreterExecute:
var execEntry = frame.DeserializeData<InterpreterEntry>(_formatter);
var execEntry = frame.GetValue<InterpreterEntry>();
InterpreterExecute?.Invoke(this, execEntry);
break;
case DebuggerMessageType.DispatcherStep:
string message = frame.GetDataAsString();
string message = frame.GetValue<string>();
DispatcherStep?.Invoke(message);
break;
case DebuggerMessageType.InterpreterCommand:
_uibCommand = (InterpreterCommand)frame.Data[0];
_uibCommand = frame.GetValue<InterpreterCommand>();
InterpreterStateChanged?.Invoke(_uibCommand);
break;
case DebuggerMessageType.LineNumberTable:
if (_requests.TryRemove(frame.TransactionId, out var callback))
callback(frame.GetValue<MarkupLineNumberEntry[]>());
break;
default:
Trace.WriteLine(TraceCategory.MarkupDebug, "Received unknown debugger message of type '{0}'.", frame.Type);
break;
@@ -1,4 +1,5 @@
using Microsoft.Iris.Debug.Data;
using Microsoft.Iris.Markup;
using System;
using System.Collections.Concurrent;
using System.Net.Sockets;
@@ -26,8 +27,7 @@ internal class NetDebuggerServer : IDebuggerServer, IDisposable
get => _uibCommand;
set
{
var data = new[] { (byte)value };
QueueDebuggerMessage(new(0, DebuggerMessageType.InterpreterCommand, data));
QueueDebuggerMessage(new(0, DebuggerMessageType.InterpreterCommand, value, _formatter));
_uibCommand = value;
}
}
@@ -51,19 +51,27 @@ internal class NetDebuggerServer : IDebuggerServer, IDisposable
Current = this;
}
public MarkupLineNumberEntry[] OnLineNumberTableRequested(string uri)
{
var loadResult = LoadResultCache.Read(uri) as MarkupLoadResult;
var lineNumberTable = loadResult.LineNumberTable.DumpTable();
return lineNumberTable;
}
public void LogInterpreterDecode(object context, InterpreterInstruction instruction)
{
QueueDebuggerMessage(new(0, DebuggerMessageType.InterpreterDecode, instruction.Serialize(_formatter)));
QueueDebuggerMessage(new(0, DebuggerMessageType.InterpreterDecode, instruction, _formatter));
}
public void LogInterpreterExecute(object context, InterpreterEntry entry)
{
QueueDebuggerMessage(new(0, DebuggerMessageType.InterpreterExecute, entry.Serialize(_formatter)));
QueueDebuggerMessage(new(0, DebuggerMessageType.InterpreterExecute, entry, _formatter));
}
public void LogDispatcher(string message)
{
QueueDebuggerMessage(new(0, DebuggerMessageType.DispatcherStep, message));
QueueDebuggerMessage(new(0, DebuggerMessageType.DispatcherStep, message, _formatter));
}
public void Dispose()
@@ -86,13 +94,13 @@ internal class NetDebuggerServer : IDebuggerServer, IDisposable
while (_socket.Connected)
{
DebuggerMessageFrame frame;
while ((frame = DebugRemoting.ReceiveDebuggerMessage(_socket)) == null)
while ((frame = DebugRemoting.ReceiveDebuggerMessage(_socket, _formatter)) == null)
if (!_socket.Connected) return;
switch (frame.Type)
{
case DebuggerMessageType.UpdateBreakpoint:
var breakpoint = frame.DeserializeData<Breakpoint>(_formatter);
var breakpoint = frame.GetValue<Breakpoint>();
if (breakpoint.Enabled)
Application.DebugSettings.Breakpoints.Add(breakpoint);
else
@@ -100,7 +108,16 @@ internal class NetDebuggerServer : IDebuggerServer, IDisposable
break;
case DebuggerMessageType.InterpreterCommand:
_uibCommand = (InterpreterCommand)frame.Data[0];
_uibCommand = frame.GetValue<InterpreterCommand>();
break;
case DebuggerMessageType.LineNumberTable:
var lineNumberTable = OnLineNumberTableRequested(frame.GetValue<string>());
DebuggerMessageFrame<MarkupLineNumberEntry[]> responseFrame =
new(frame.TransactionId, DebuggerMessageType.LineNumberTable, lineNumberTable, _formatter);
QueueDebuggerMessage(responseFrame);
break;
default:
@@ -62,14 +62,14 @@ namespace Microsoft.Iris.Markup
internal ulong[] PersistList => _runtimeList;
public Vector<Debug.Data.MarkupLineNumberEntry> DumpTable()
public Debug.Data.MarkupLineNumberEntry[] DumpTable()
{
Vector<Debug.Data.MarkupLineNumberEntry> knownLines = new();
var knownLines = new Debug.Data.MarkupLineNumberEntry[_runtimeList.Length];
for (int index = 0; index < _runtimeList.Length; ++index)
{
var value = _runtimeList[index];
knownLines.Add(new(UnpackOffset(value), UnpackLine(value), UnpackColumn(value)));
knownLines[index] = new(UnpackOffset(value), UnpackLine(value), UnpackColumn(value));
}
return knownLines;