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); var package = (JObject)serializer.Deserialize(reader);
TypeName = package["0"].Value<string>(); TypeName = package["0"].Value<string>();
SerializedObject = (JObject)package["1"]; SerializedObject = package["1"];
// If the type is accessible from the current domain, // If the type is accessible from the current domain,
// create an instance of it. // create an instance of it.
@@ -72,7 +72,7 @@ internal class BsonFormatter : IFormatter
public string TypeName { get; set; } public string TypeName { get; set; }
public JObject SerializedObject { get; set; } public JToken SerializedObject { get; set; }
public object Object { get; set; } public object Object { get; set; }
} }
+8 -2
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 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(); StringBuilder sb = new();
if (includeEnabled)
{
sb.Append(Enabled ? '+' : '-'); sb.Append(Enabled ? '+' : '-');
sb.Append(' '); sb.Append(' ');
}
sb.Append(Uri); sb.Append(Uri);
sb.Append(' '); sb.Append(' ');
@@ -69,5 +75,5 @@ public struct Breakpoint : IEquatable<Breakpoint>
return sb.ToString(); return sb.ToString();
} }
public override int GetHashCode() => ToString().GetHashCode(); public override int GetHashCode() => ToString(false).GetHashCode();
} }
@@ -1,28 +1,38 @@
using System; using System;
using System.IO; using System.IO;
using System.Runtime.Serialization; using System.Runtime.Serialization;
using System.Text;
namespace Microsoft.Iris.Debug.Data; namespace Microsoft.Iris.Debug.Data;
public class DebuggerMessageFrame 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; TransactionId = transactionId;
Type = type; 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; Data = data;
} }
public DebuggerMessageFrame(long transactionId, DebuggerMessageType type, string message, Encoding encoding = null) public DebuggerMessageFrame(byte[] bytes, IFormatter formatter)
: this(transactionId, type, (encoding ?? Encoding.UTF8).GetBytes(message))
{ {
} _formatter = formatter;
public DebuggerMessageFrame(byte[] bytes)
{
TransactionId = BitConverter.ToInt64(bytes, 0); TransactionId = BitConverter.ToInt64(bytes, 0);
Type = (DebuggerMessageType)BitConverter.ToInt32(bytes, sizeof(long)); Type = (DebuggerMessageType)BitConverter.ToInt32(bytes, sizeof(long));
@@ -39,7 +49,46 @@ public class DebuggerMessageFrame
public DebuggerMessageType Type { get; set; } 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() public byte[] ToBytes()
{ {
@@ -62,11 +111,33 @@ public class DebuggerMessageFrame
return bytes; return bytes;
} }
public string GetDataAsString(Encoding encoding = null) => (encoding ?? Encoding.UTF8).GetString(Data); public virtual T GetValue<T>(IFormatter formatter = null)
public T DeserializeData<T>(IFormatter formatter)
{ {
using MemoryStream stream = new(Data); 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); 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 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 try
{ {
@@ -33,7 +33,7 @@ public static class DebugRemoting
byte[] frameBytes = new byte[frameLength]; byte[] frameBytes = new byte[frameLength];
socket.Receive(frameBytes, frameLength, SocketFlags.None); socket.Receive(frameBytes, frameLength, SocketFlags.None);
return new(frameBytes); return new(frameBytes, formatter);
} }
catch (SocketException) catch (SocketException)
{ {
+1 -1
View File
@@ -14,7 +14,7 @@ public class DebugSettings
public bool GenerateDataMappingModels { get; set; } = false; public bool GenerateDataMappingModels { get; set; } = false;
public ObservableCollection<DataMappingModel> DataMappingModels { get; } = new(); public ObservableCollection<DataMappingModel> DataMappingModels { get; } = new();
public List<Breakpoint> Breakpoints { get; } = new(); public HashSet<Breakpoint> Breakpoints { get; } = new();
public string DebugConnectionUri { get; set; } public string DebugConnectionUri { get; set; }
} }
@@ -10,4 +10,6 @@ public enum DebuggerMessageType : int
UpdateBreakpoint, UpdateBreakpoint,
InterpreterCommand, InterpreterCommand,
LineNumberTable,
} }
@@ -23,4 +23,10 @@ public interface IDebuggerClient : IDebuggerState
event Action<string> DispatcherStep; event Action<string> DispatcherStep;
void UpdateBreakpoint(Breakpoint breakpoint); 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 internal interface IDebuggerServer : IDebuggerState
{ {
/// <summary>
/// Sends the requested line number table.
/// </summary>
MarkupLineNumberEntry[] OnLineNumberTableRequested(string uri);
/// <summary> /// <summary>
/// Logs the context, opcode, and operands of an instruction /// Logs the context, opcode, and operands of an instruction
/// decoded by <c>Microsoft.Iris.Markup.Interpreter</c>. /// decoded by <c>Microsoft.Iris.Markup.Interpreter</c>.
@@ -11,8 +11,11 @@ public class NetDebuggerClient : IDebuggerClient, IDisposable
{ {
private readonly Socket _socket; private readonly Socket _socket;
private readonly ConcurrentQueue<byte[]> _outQueue = new(); private readonly ConcurrentQueue<byte[]> _outQueue = new();
private readonly ConcurrentDictionary<long, Action<object>> _requests = new();
private readonly IFormatter _formatter; private readonly IFormatter _formatter;
private InterpreterCommand _uibCommand; private InterpreterCommand _uibCommand;
private long _nextFreeTransactionId = 1;
public Uri ConnectionUri { get; } public Uri ConnectionUri { get; }
@@ -21,8 +24,7 @@ public class NetDebuggerClient : IDebuggerClient, IDisposable
get => _uibCommand; get => _uibCommand;
set set
{ {
var data = new[] { (byte)value }; QueueDebuggerMessage(new(0, DebuggerMessageType.InterpreterCommand, value, _formatter));
QueueDebuggerMessage(new(0, DebuggerMessageType.InterpreterCommand, data));
_uibCommand = value; _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) 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) private void QueueDebuggerMessage(DebuggerMessageFrame frame)
@@ -67,36 +76,55 @@ public class NetDebuggerClient : IDebuggerClient, IDisposable
_outQueue.Enqueue(frame.ToBytes()); _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() private void MessageReceiveLoop()
{ {
while (_socket.Connected) while (_socket.Connected)
{ {
DebuggerMessageFrame frame; DebuggerMessageFrame frame;
while ((frame = DebugRemoting.ReceiveDebuggerMessage(_socket)) == null) while ((frame = DebugRemoting.ReceiveDebuggerMessage(_socket, _formatter)) == null)
if (!_socket.Connected) return; if (!_socket.Connected) return;
switch (frame.Type) switch (frame.Type)
{ {
case DebuggerMessageType.InterpreterDecode: case DebuggerMessageType.InterpreterDecode:
var decEntry = frame.DeserializeData<InterpreterInstruction>(_formatter); var decEntry = frame.GetValue<InterpreterInstruction>();
InterpreterDecode?.Invoke(this, decEntry); InterpreterDecode?.Invoke(this, decEntry);
break; break;
case DebuggerMessageType.InterpreterExecute: case DebuggerMessageType.InterpreterExecute:
var execEntry = frame.DeserializeData<InterpreterEntry>(_formatter); var execEntry = frame.GetValue<InterpreterEntry>();
InterpreterExecute?.Invoke(this, execEntry); InterpreterExecute?.Invoke(this, execEntry);
break; break;
case DebuggerMessageType.DispatcherStep: case DebuggerMessageType.DispatcherStep:
string message = frame.GetDataAsString(); string message = frame.GetValue<string>();
DispatcherStep?.Invoke(message); DispatcherStep?.Invoke(message);
break; break;
case DebuggerMessageType.InterpreterCommand: case DebuggerMessageType.InterpreterCommand:
_uibCommand = (InterpreterCommand)frame.Data[0]; _uibCommand = frame.GetValue<InterpreterCommand>();
InterpreterStateChanged?.Invoke(_uibCommand); InterpreterStateChanged?.Invoke(_uibCommand);
break; break;
case DebuggerMessageType.LineNumberTable:
if (_requests.TryRemove(frame.TransactionId, out var callback))
callback(frame.GetValue<MarkupLineNumberEntry[]>());
break;
default: default:
Trace.WriteLine(TraceCategory.MarkupDebug, "Received unknown debugger message of type '{0}'.", frame.Type); Trace.WriteLine(TraceCategory.MarkupDebug, "Received unknown debugger message of type '{0}'.", frame.Type);
break; break;
@@ -1,4 +1,5 @@
using Microsoft.Iris.Debug.Data; using Microsoft.Iris.Debug.Data;
using Microsoft.Iris.Markup;
using System; using System;
using System.Collections.Concurrent; using System.Collections.Concurrent;
using System.Net.Sockets; using System.Net.Sockets;
@@ -26,8 +27,7 @@ internal class NetDebuggerServer : IDebuggerServer, IDisposable
get => _uibCommand; get => _uibCommand;
set set
{ {
var data = new[] { (byte)value }; QueueDebuggerMessage(new(0, DebuggerMessageType.InterpreterCommand, value, _formatter));
QueueDebuggerMessage(new(0, DebuggerMessageType.InterpreterCommand, data));
_uibCommand = value; _uibCommand = value;
} }
} }
@@ -51,19 +51,27 @@ internal class NetDebuggerServer : IDebuggerServer, IDisposable
Current = this; 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) 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) 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) public void LogDispatcher(string message)
{ {
QueueDebuggerMessage(new(0, DebuggerMessageType.DispatcherStep, message)); QueueDebuggerMessage(new(0, DebuggerMessageType.DispatcherStep, message, _formatter));
} }
public void Dispose() public void Dispose()
@@ -86,13 +94,13 @@ internal class NetDebuggerServer : IDebuggerServer, IDisposable
while (_socket.Connected) while (_socket.Connected)
{ {
DebuggerMessageFrame frame; DebuggerMessageFrame frame;
while ((frame = DebugRemoting.ReceiveDebuggerMessage(_socket)) == null) while ((frame = DebugRemoting.ReceiveDebuggerMessage(_socket, _formatter)) == null)
if (!_socket.Connected) return; if (!_socket.Connected) return;
switch (frame.Type) switch (frame.Type)
{ {
case DebuggerMessageType.UpdateBreakpoint: case DebuggerMessageType.UpdateBreakpoint:
var breakpoint = frame.DeserializeData<Breakpoint>(_formatter); var breakpoint = frame.GetValue<Breakpoint>();
if (breakpoint.Enabled) if (breakpoint.Enabled)
Application.DebugSettings.Breakpoints.Add(breakpoint); Application.DebugSettings.Breakpoints.Add(breakpoint);
else else
@@ -100,7 +108,16 @@ internal class NetDebuggerServer : IDebuggerServer, IDisposable
break; break;
case DebuggerMessageType.InterpreterCommand: 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; break;
default: default:
@@ -62,14 +62,14 @@ namespace Microsoft.Iris.Markup
internal ulong[] PersistList => _runtimeList; 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) for (int index = 0; index < _runtimeList.Length; ++index)
{ {
var value = _runtimeList[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; return knownLines;