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

@ -545,7 +545,7 @@ namespace WsProxy {
return assemblies.FirstOrDefault (a => a.Name.Equals (name, StringComparison.InvariantCultureIgnoreCase));
}
/*
/*
V8 uses zero based indexing for both line and column.
PPDBs uses one based indexing for both line and column.
*/
@ -598,7 +598,7 @@ namespace WsProxy {
PPDBs uses one based indexing for both line and column.
*/
static bool Match (SequencePoint sp, int line, int column)
{
{
var bp = (line: line + 1, column: column + 1);
if (sp.StartLine > bp.line || sp.EndLine < bp.line)

View File

@ -223,7 +223,7 @@ namespace WsProxy {
Info ("RUNTIME READY, PARTY TIME");
await RuntimeReady (token);
await SendCommand ("Debugger.resume", new JObject (), token);
SendEvent ("Mono.runtimeReady", new JObject (), token);
SendEvent ("Mono.runtimeReady", new JObject (), token);
}
async Task OnBreakPointHit (JObject args, CancellationToken token)
@ -274,12 +274,18 @@ namespace WsProxy {
var frames = new List<Frame> ();
int frame_id = 0;
var the_mono_frames = res.Value? ["result"]? ["value"]? ["frames"]?.Values<JObject> ();
foreach (var mono_frame in the_mono_frames) {
var il_pos = mono_frame ["il_pos"].Value<int> ();
var method_token = mono_frame ["method_token"].Value<int> ();
var assembly_name = mono_frame ["assembly_name"].Value<string> ();
var asm = store.GetAssemblyByName (assembly_name);
if (asm == null) {
Info ($"Unable to find assembly: {assembly_name}");
continue;
}
var method = asm.GetMethodByToken (method_token);
if (method == null) {
@ -374,7 +380,7 @@ namespace WsProxy {
//Debug ($"\t{is_ready}");
if (is_ready.HasValue && is_ready.Value == true) {
Debug ("RUNTIME LOOK READY. GO TIME!");
await RuntimeReady (token);
await OnRuntimeReady (token);
}
}
@ -426,33 +432,38 @@ namespace WsProxy {
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
// 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_list.Add(JObject.FromObject(new {
name = fieldName,
value
}));
}
o = JObject.FromObject(new
{
result = var_list
});
} 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);
}
@ -481,41 +492,51 @@ namespace WsProxy {
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> ();
int i = 0;
// 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.
while (values != null && i < vars.Length && i < values.Length) {
var value = values [i] ["value"];
if (((string)value ["description"]) == null)
value ["description"] = value ["value"]?.ToString();
var var_list = new List<JObject> ();
int i = 0;
// 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.
while (values != null && i < vars.Length && i < values.Length) {
var value = values [i] ["value"];
if (((string)value ["description"]) == null)
value ["description"] = value ["value"]?.ToString ();
var_list.Add (JObject.FromObject (new {
name = vars [i].Name,
value = values [i] ["value"]
}));
i++;
var_list.Add (JObject.FromObject (new {
name = vars [i].Name,
value
}));
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
//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_list.Add (JObject.FromObject (new {
name = name,
value = values [i+1] ["value"]
}));
i = i + 2;
catch (Exception exc) {
SendResponse (msg_id, res, token);
}
o = JObject.FromObject (new {
result = var_list
});
SendResponse (msg_id, Result.Ok (o), token);
}
async Task<Result> EnableBreakPoint (Breakpoint bp, CancellationToken token)

View File

@ -97,6 +97,7 @@ namespace WsProxy {
internal class WsProxy {
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>)> ();
ClientWebSocket browser;
WebSocket ide;
@ -119,8 +120,16 @@ namespace WsProxy {
byte [] buff = new byte [4000];
var mem = new MemoryStream ();
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);
if (result.MessageType == WebSocketMessageType.Close) {
client_initiated_close.TrySetResult (true);
return null;
}
@ -144,7 +153,7 @@ namespace WsProxy {
void Send (WebSocket to, JObject o, CancellationToken token)
{
var bytes = Encoding.UTF8.GetBytes (o.ToString ());
var bytes = Encoding.UTF8.GetBytes (o.ToString ());
var queue = GetQueueForSocket (to);
var task = queue.Send (bytes, token);
@ -199,9 +208,10 @@ namespace WsProxy {
void ProcessIdeMessage (string msg, CancellationToken token)
{
var res = JObject.Parse (msg);
pending_ops.Add (OnCommand (res ["id"].Value<int> (), res ["method"].Value<string> (), res ["params"] as JObject, token));
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));
}
}
internal async Task<Result> SendCommand (string method, JObject args, CancellationToken token) {
@ -255,24 +265,25 @@ namespace WsProxy {
Send (this.ide, o, token);
}
// , HttpContext context)
// , HttpContext context)
public async Task Run (Uri browserUri, WebSocket ideSocket)
{
Debug ("wsproxy start");
Debug ($"WsProxy Starting on {browserUri}");
using (this.ide = ideSocket) {
Debug ("ide connected");
Debug ($"WsProxy: IDE waiting for connection on {browserUri}");
queues.Add (new WsQueue (this.ide));
using (this.browser = new ClientWebSocket ()) {
this.browser.Options.KeepAliveInterval = Timeout.InfiniteTimeSpan;
await this.browser.ConnectAsync (browserUri, CancellationToken.None);
queues.Add (new WsQueue (this.browser));
Debug ("client connected");
Debug ($"WsProxy: Client connected on {browserUri}");
var x = new CancellationTokenSource ();
pending_ops.Add (ReadOne (browser, x.Token));
pending_ops.Add (ReadOne (ide, x.Token));
pending_ops.Add (side_exception.Task);
pending_ops.Add (client_initiated_close.Task);
try {
while (!x.IsCancellationRequested) {
@ -280,15 +291,23 @@ namespace WsProxy {
//Console.WriteLine ("pump {0} {1}", task, pending_ops.IndexOf (task));
if (task == pending_ops [0]) {
var msg = ((Task<string>)task).Result;
pending_ops [0] = ReadOne (browser, x.Token); //queue next read
ProcessBrowserMessage (msg, x.Token);
if (msg != null) {
pending_ops [0] = ReadOne (browser, x.Token); //queue next read
ProcessBrowserMessage (msg, x.Token);
}
} else if (task == pending_ops [1]) {
var msg = ((Task<string>)task).Result;
pending_ops [1] = ReadOne (ide, x.Token); //queue next read
ProcessIdeMessage (msg, x.Token);
if (msg != null) {
pending_ops [1] = ReadOne (ide, x.Token); //queue next read
ProcessIdeMessage (msg, x.Token);
}
} else if (task == pending_ops [2]) {
var res = ((Task<bool>)task).Result;
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 {
//must be a background task
pending_ops.Remove (task);
@ -301,10 +320,11 @@ namespace WsProxy {
}
}
} catch (Exception e) {
Debug ($"got exception {e}");
Debug ($"WsProxy::Run: Exception {e}");
//throw;
} finally {
x.Cancel ();
if (!x.IsCancellationRequested)
x.Cancel ();
}
}
}