Update Mono debug proxy code (#18478)

This commit is contained in:
Steve Sanderson 2020-01-21 21:33:27 +00:00 committed by Artak
parent 0f2e88a334
commit 05f2430bbd
3 changed files with 114 additions and 73 deletions

View File

@ -274,12 +274,18 @@ namespace WsProxy {
var frames = new List<Frame> (); var frames = new List<Frame> ();
int frame_id = 0; int frame_id = 0;
var the_mono_frames = res.Value? ["result"]? ["value"]? ["frames"]?.Values<JObject> (); var the_mono_frames = res.Value? ["result"]? ["value"]? ["frames"]?.Values<JObject> ();
foreach (var mono_frame in the_mono_frames) { foreach (var mono_frame in the_mono_frames) {
var il_pos = mono_frame ["il_pos"].Value<int> (); var il_pos = mono_frame ["il_pos"].Value<int> ();
var method_token = mono_frame ["method_token"].Value<int> (); var method_token = mono_frame ["method_token"].Value<int> ();
var assembly_name = mono_frame ["assembly_name"].Value<string> (); var assembly_name = mono_frame ["assembly_name"].Value<string> ();
var asm = store.GetAssemblyByName (assembly_name); var asm = store.GetAssemblyByName (assembly_name);
if (asm == null) {
Info ($"Unable to find assembly: {assembly_name}");
continue;
}
var method = asm.GetMethodByToken (method_token); var method = asm.GetMethodByToken (method_token);
if (method == null) { if (method == null) {
@ -374,7 +380,7 @@ namespace WsProxy {
//Debug ($"\t{is_ready}"); //Debug ($"\t{is_ready}");
if (is_ready.HasValue && is_ready.Value == true) { if (is_ready.HasValue && is_ready.Value == true) {
Debug ("RUNTIME LOOK READY. GO TIME!"); Debug ("RUNTIME LOOK READY. GO TIME!");
await RuntimeReady (token); await OnRuntimeReady (token);
} }
} }
@ -426,33 +432,38 @@ namespace WsProxy {
return; return;
} }
var values = res.Value?["result"]?["value"]?.Values<JObject>().ToArray(); try {
var values = res.Value?["result"]?["value"]?.Values<JObject>().ToArray() ?? Array.Empty<JObject>();
var var_list = new List<JObject>();
var var_list = new List<JObject>(); // Trying to inspect the stack frame for DotNetDispatcher::InvokeSynchronously
// results in a "Memory access out of bounds", causing 'values' to be null,
// so skip returning variable values in that case.
for (int i = 0; i < values.Length; i+=2)
{
string fieldName = (string)values[i]["name"];
if (fieldName.Contains("k__BackingField")){
fieldName = fieldName.Replace("k__BackingField", "");
fieldName = fieldName.Replace("<", "");
fieldName = fieldName.Replace(">", "");
}
var value = values [i + 1]? ["value"];
if (((string)value ["description"]) == null)
value ["description"] = value ["value"]?.ToString ();
// Trying to inspect the stack frame for DotNetDispatcher::InvokeSynchronously var_list.Add(JObject.FromObject(new {
// results in a "Memory access out of bounds", causing 'values' to be null, name = fieldName,
// so skip returning variable values in that case. value
for (int i = 0; i < values.Length; i+=2) }));
{
string fieldName = (string)values[i]["name"]; }
if (fieldName.Contains("k__BackingField")){ o = JObject.FromObject(new
fieldName = fieldName.Replace("k__BackingField", ""); {
fieldName = fieldName.Replace("<", ""); result = var_list
fieldName = fieldName.Replace(">", ""); });
} catch (Exception e) {
Debug ($"failed to parse {res.Value}");
} }
var_list.Add(JObject.FromObject(new
{
name = fieldName,
value = values[i+1]["value"]
}));
}
o = JObject.FromObject(new
{
result = var_list
});
SendResponse(msg_id, Result.Ok(o), token); SendResponse(msg_id, Result.Ok(o), token);
} }
@ -481,41 +492,51 @@ namespace WsProxy {
return; return;
} }
var values = res.Value? ["result"]? ["value"]?.Values<JObject> ().ToArray (); try {
var values = res.Value? ["result"]? ["value"]?.Values<JObject> ().ToArray ();
var var_list = new List<JObject> (); var var_list = new List<JObject> ();
int i = 0; int i = 0;
// Trying to inspect the stack frame for DotNetDispatcher::InvokeSynchronously // Trying to inspect the stack frame for DotNetDispatcher::InvokeSynchronously
// results in a "Memory access out of bounds", causing 'values' to be null, // results in a "Memory access out of bounds", causing 'values' to be null,
// so skip returning variable values in that case. // so skip returning variable values in that case.
while (values != null && i < vars.Length && i < values.Length) { while (values != null && i < vars.Length && i < values.Length) {
var value = values [i] ["value"]; var value = values [i] ["value"];
if (((string)value ["description"]) == null) if (((string)value ["description"]) == null)
value ["description"] = value ["value"]?.ToString(); value ["description"] = value ["value"]?.ToString ();
var_list.Add (JObject.FromObject (new { var_list.Add (JObject.FromObject (new {
name = vars [i].Name, name = vars [i].Name,
value = values [i] ["value"] value
})); }));
i++; i++;
}
//Async methods are special in the way that local variables can be lifted to generated class fields
//value of "this" comes here either
while (i < values.Length) {
String name = values [i] ["name"].ToString ();
if (name.IndexOf (">", StringComparison.Ordinal) > 0)
name = name.Substring (1, name.IndexOf (">", StringComparison.Ordinal) - 1);
var value = values [i + 1] ["value"];
if (((string)value ["description"]) == null)
value ["description"] = value ["value"]?.ToString ();
var_list.Add (JObject.FromObject (new {
name,
value
}));
i = i + 2;
}
o = JObject.FromObject (new {
result = var_list
});
SendResponse (msg_id, Result.Ok (o), token);
} }
//Async methods are special in the way that local variables can be lifted to generated class fields catch (Exception exc) {
//value of "this" comes here either SendResponse (msg_id, res, token);
while (i < values.Length) {
String name = values [i] ["name"].ToString ();
if (name.IndexOf (">", StringComparison.Ordinal) > 0)
name = name.Substring (1, name.IndexOf (">", StringComparison.Ordinal) - 1);
var_list.Add (JObject.FromObject (new {
name = name,
value = values [i+1] ["value"]
}));
i = i + 2;
} }
o = JObject.FromObject (new {
result = var_list
});
SendResponse (msg_id, Result.Ok (o), token);
} }
async Task<Result> EnableBreakPoint (Breakpoint bp, CancellationToken token) async Task<Result> EnableBreakPoint (Breakpoint bp, CancellationToken token)

View File

@ -97,6 +97,7 @@ namespace WsProxy {
internal class WsProxy { internal class WsProxy {
TaskCompletionSource<bool> side_exception = new TaskCompletionSource<bool> (); TaskCompletionSource<bool> side_exception = new TaskCompletionSource<bool> ();
TaskCompletionSource<bool> client_initiated_close = new TaskCompletionSource<bool> ();
List<(int, TaskCompletionSource<Result>)> pending_cmds = new List<(int, TaskCompletionSource<Result>)> (); List<(int, TaskCompletionSource<Result>)> pending_cmds = new List<(int, TaskCompletionSource<Result>)> ();
ClientWebSocket browser; ClientWebSocket browser;
WebSocket ide; WebSocket ide;
@ -119,8 +120,16 @@ namespace WsProxy {
byte [] buff = new byte [4000]; byte [] buff = new byte [4000];
var mem = new MemoryStream (); var mem = new MemoryStream ();
while (true) { while (true) {
if (socket.State != WebSocketState.Open) {
Console.WriteLine ($"WSProxy: Socket is no longer open.");
client_initiated_close.TrySetResult (true);
return null;
}
var result = await socket.ReceiveAsync (new ArraySegment<byte> (buff), token); var result = await socket.ReceiveAsync (new ArraySegment<byte> (buff), token);
if (result.MessageType == WebSocketMessageType.Close) { if (result.MessageType == WebSocketMessageType.Close) {
client_initiated_close.TrySetResult (true);
return null; return null;
} }
@ -199,9 +208,10 @@ namespace WsProxy {
void ProcessIdeMessage (string msg, CancellationToken token) void ProcessIdeMessage (string msg, CancellationToken token)
{ {
var res = JObject.Parse (msg); if (!string.IsNullOrEmpty (msg)) {
var res = JObject.Parse (msg);
pending_ops.Add (OnCommand (res ["id"].Value<int> (), res ["method"].Value<string> (), res ["params"] as JObject, token)); pending_ops.Add (OnCommand (res ["id"].Value<int> (), res ["method"].Value<string> (), res ["params"] as JObject, token));
}
} }
internal async Task<Result> SendCommand (string method, JObject args, CancellationToken token) { internal async Task<Result> SendCommand (string method, JObject args, CancellationToken token) {
@ -255,24 +265,25 @@ namespace WsProxy {
Send (this.ide, o, token); Send (this.ide, o, token);
} }
// , HttpContext context) // , HttpContext context)
public async Task Run (Uri browserUri, WebSocket ideSocket) public async Task Run (Uri browserUri, WebSocket ideSocket)
{ {
Debug ("wsproxy start"); Debug ($"WsProxy Starting on {browserUri}");
using (this.ide = ideSocket) { using (this.ide = ideSocket) {
Debug ("ide connected"); Debug ($"WsProxy: IDE waiting for connection on {browserUri}");
queues.Add (new WsQueue (this.ide)); queues.Add (new WsQueue (this.ide));
using (this.browser = new ClientWebSocket ()) { using (this.browser = new ClientWebSocket ()) {
this.browser.Options.KeepAliveInterval = Timeout.InfiniteTimeSpan; this.browser.Options.KeepAliveInterval = Timeout.InfiniteTimeSpan;
await this.browser.ConnectAsync (browserUri, CancellationToken.None); await this.browser.ConnectAsync (browserUri, CancellationToken.None);
queues.Add (new WsQueue (this.browser)); queues.Add (new WsQueue (this.browser));
Debug ("client connected"); Debug ($"WsProxy: Client connected on {browserUri}");
var x = new CancellationTokenSource (); var x = new CancellationTokenSource ();
pending_ops.Add (ReadOne (browser, x.Token)); pending_ops.Add (ReadOne (browser, x.Token));
pending_ops.Add (ReadOne (ide, x.Token)); pending_ops.Add (ReadOne (ide, x.Token));
pending_ops.Add (side_exception.Task); pending_ops.Add (side_exception.Task);
pending_ops.Add (client_initiated_close.Task);
try { try {
while (!x.IsCancellationRequested) { while (!x.IsCancellationRequested) {
@ -280,15 +291,23 @@ namespace WsProxy {
//Console.WriteLine ("pump {0} {1}", task, pending_ops.IndexOf (task)); //Console.WriteLine ("pump {0} {1}", task, pending_ops.IndexOf (task));
if (task == pending_ops [0]) { if (task == pending_ops [0]) {
var msg = ((Task<string>)task).Result; var msg = ((Task<string>)task).Result;
pending_ops [0] = ReadOne (browser, x.Token); //queue next read if (msg != null) {
ProcessBrowserMessage (msg, x.Token); pending_ops [0] = ReadOne (browser, x.Token); //queue next read
ProcessBrowserMessage (msg, x.Token);
}
} else if (task == pending_ops [1]) { } else if (task == pending_ops [1]) {
var msg = ((Task<string>)task).Result; var msg = ((Task<string>)task).Result;
pending_ops [1] = ReadOne (ide, x.Token); //queue next read if (msg != null) {
ProcessIdeMessage (msg, x.Token); pending_ops [1] = ReadOne (ide, x.Token); //queue next read
ProcessIdeMessage (msg, x.Token);
}
} else if (task == pending_ops [2]) { } else if (task == pending_ops [2]) {
var res = ((Task<bool>)task).Result; var res = ((Task<bool>)task).Result;
throw new Exception ("side task must always complete with an exception, what's going on???"); throw new Exception ("side task must always complete with an exception, what's going on???");
} else if (task == pending_ops [3]) {
var res = ((Task<bool>)task).Result;
Debug ($"WsProxy: Client initiated close from {browserUri}");
x.Cancel ();
} else { } else {
//must be a background task //must be a background task
pending_ops.Remove (task); pending_ops.Remove (task);
@ -301,10 +320,11 @@ namespace WsProxy {
} }
} }
} catch (Exception e) { } catch (Exception e) {
Debug ($"got exception {e}"); Debug ($"WsProxy::Run: Exception {e}");
//throw; //throw;
} finally { } finally {
x.Cancel (); if (!x.IsCancellationRequested)
x.Cancel ();
} }
} }
} }