Update Mono debug proxy code (#18478)
This commit is contained in:
parent
0f2e88a334
commit
05f2430bbd
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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 ();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue