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));
|
return assemblies.FirstOrDefault (a => a.Name.Equals (name, StringComparison.InvariantCultureIgnoreCase));
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
V8 uses zero based indexing for both line and column.
|
V8 uses zero based indexing for both line and column.
|
||||||
PPDBs uses one 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.
|
PPDBs uses one based indexing for both line and column.
|
||||||
*/
|
*/
|
||||||
static bool Match (SequencePoint sp, int line, int column)
|
static bool Match (SequencePoint sp, int line, int column)
|
||||||
{
|
{
|
||||||
var bp = (line: line + 1, column: column + 1);
|
var bp = (line: line + 1, column: column + 1);
|
||||||
|
|
||||||
if (sp.StartLine > bp.line || sp.EndLine < bp.line)
|
if (sp.StartLine > bp.line || sp.EndLine < bp.line)
|
||||||
|
|
|
||||||
|
|
@ -223,7 +223,7 @@ namespace WsProxy {
|
||||||
Info ("RUNTIME READY, PARTY TIME");
|
Info ("RUNTIME READY, PARTY TIME");
|
||||||
await RuntimeReady (token);
|
await RuntimeReady (token);
|
||||||
await SendCommand ("Debugger.resume", new JObject (), 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)
|
async Task OnBreakPointHit (JObject args, CancellationToken token)
|
||||||
|
|
@ -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)
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -144,7 +153,7 @@ namespace WsProxy {
|
||||||
|
|
||||||
void Send (WebSocket to, JObject o, CancellationToken token)
|
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 queue = GetQueueForSocket (to);
|
||||||
var task = queue.Send (bytes, token);
|
var task = queue.Send (bytes, token);
|
||||||
|
|
@ -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 ();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue