Update to ws-proxy code matching Mono repo 5e318afd
This commit is contained in:
parent
edff13f25a
commit
5a836ef0a6
|
|
@ -52,7 +52,8 @@ namespace Microsoft.AspNetCore.Builder
|
||||||
}
|
}
|
||||||
|
|
||||||
var browserUri = new Uri(context.Request.Query["browser"]);
|
var browserUri = new Uri(context.Request.Query["browser"]);
|
||||||
await new MonoProxy().Run(context, browserUri);
|
var ideSocket = await context.WebSockets.AcceptWebSocketAsync();
|
||||||
|
await new MonoProxy().Run(browserUri, ideSocket);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static async Task DebugHome(HttpContext context)
|
private static async Task DebugHome(HttpContext context)
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,10 @@ namespace WsProxy {
|
||||||
public int Line { get; private set; }
|
public int Line { get; private set; }
|
||||||
public int Column { get; private set; }
|
public int Column { get; private set; }
|
||||||
|
|
||||||
|
public override string ToString () {
|
||||||
|
return $"BreakPointRequest Assembly: {Assembly} File: {File} Line: {Line} Column: {Column}";
|
||||||
|
}
|
||||||
|
|
||||||
public static BreakPointRequest Parse (JObject args)
|
public static BreakPointRequest Parse (JObject args)
|
||||||
{
|
{
|
||||||
if (args == null)
|
if (args == null)
|
||||||
|
|
@ -457,13 +461,18 @@ namespace WsProxy {
|
||||||
var doc = GetFileById (src_id);
|
var doc = GetFileById (src_id);
|
||||||
|
|
||||||
var res = new List<SourceLocation> ();
|
var res = new List<SourceLocation> ();
|
||||||
|
if (doc == null) {
|
||||||
|
//FIXME we need to write up logging here
|
||||||
|
Console.WriteLine ($"Could not find document {src_id}");
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
foreach (var m in doc.Methods) {
|
foreach (var m in doc.Methods) {
|
||||||
foreach (var sp in m.methodDef.DebugInformation.SequencePoints) {
|
foreach (var sp in m.methodDef.DebugInformation.SequencePoints) {
|
||||||
if (Match (sp, start, end))
|
if (Match (sp, start, end))
|
||||||
res.Add (new SourceLocation (m, sp));
|
res.Add (new SourceLocation (m, sp));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,6 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.AspNetCore.Http;
|
|
||||||
using Newtonsoft.Json.Linq;
|
using Newtonsoft.Json.Linq;
|
||||||
|
|
||||||
using System.Net.WebSockets;
|
using System.Net.WebSockets;
|
||||||
|
|
@ -18,10 +17,16 @@ namespace WsProxy {
|
||||||
public const string START_SINGLE_STEPPING = "MONO.mono_wasm_start_single_stepping({0})";
|
public const string START_SINGLE_STEPPING = "MONO.mono_wasm_start_single_stepping({0})";
|
||||||
public const string GET_SCOPE_VARIABLES = "MONO.mono_wasm_get_variables({0}, [ {1} ])";
|
public const string GET_SCOPE_VARIABLES = "MONO.mono_wasm_get_variables({0}, [ {1} ])";
|
||||||
public const string SET_BREAK_POINT = "MONO.mono_wasm_set_breakpoint(\"{0}\", {1}, {2})";
|
public const string SET_BREAK_POINT = "MONO.mono_wasm_set_breakpoint(\"{0}\", {1}, {2})";
|
||||||
|
public const string REMOVE_BREAK_POINT = "MONO.mono_wasm_remove_breakpoint({0})";
|
||||||
public const string GET_LOADED_FILES = "MONO.mono_wasm_get_loaded_files()";
|
public const string GET_LOADED_FILES = "MONO.mono_wasm_get_loaded_files()";
|
||||||
public const string CLEAR_ALL_BREAKPOINTS = "MONO.mono_wasm_clear_all_breakpoints()";
|
public const string CLEAR_ALL_BREAKPOINTS = "MONO.mono_wasm_clear_all_breakpoints()";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal enum MonoErrorCodes {
|
||||||
|
BpNotFound = 100000,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
internal class MonoConstants {
|
internal class MonoConstants {
|
||||||
public const string RUNTIME_IS_READY = "mono_wasm_runtime_ready";
|
public const string RUNTIME_IS_READY = "mono_wasm_runtime_ready";
|
||||||
}
|
}
|
||||||
|
|
@ -156,6 +161,9 @@ namespace WsProxy {
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case "Debugger.removeBreakpoint": {
|
||||||
|
return await RemoveBreakpoint (id, args, token);
|
||||||
|
}
|
||||||
|
|
||||||
case "Debugger.resume": {
|
case "Debugger.resume": {
|
||||||
await OnResume (token);
|
await OnResume (token);
|
||||||
|
|
@ -265,14 +273,14 @@ namespace WsProxy {
|
||||||
var method = asm.GetMethodByToken (method_token);
|
var method = asm.GetMethodByToken (method_token);
|
||||||
var location = method.GetLocationByIl (il_pos);
|
var location = method.GetLocationByIl (il_pos);
|
||||||
|
|
||||||
// When hitting a breakpoint on the "IncrementCount" method in the standard
|
// When hitting a breakpoint on the "IncrementCount" method in the standard
|
||||||
// Blazor project template, one of the stack frames is inside mscorlib.dll
|
// Blazor project template, one of the stack frames is inside mscorlib.dll
|
||||||
// and we get location==null for it. It will trigger a NullReferenceException
|
// and we get location==null for it. It will trigger a NullReferenceException
|
||||||
// if we don't skip over that stack frame.
|
// if we don't skip over that stack frame.
|
||||||
if (location == null)
|
if (location == null)
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
Info ($"frame il offset: {il_pos} method token: {method_token} assembly name: {assembly_name}");
|
Info ($"frame il offset: {il_pos} method token: {method_token} assembly name: {assembly_name}");
|
||||||
Info ($"\tmethod {method.Name} location: {location}");
|
Info ($"\tmethod {method.Name} location: {location}");
|
||||||
|
|
@ -413,9 +421,9 @@ namespace WsProxy {
|
||||||
|
|
||||||
var var_list = new List<JObject> ();
|
var var_list = new List<JObject> ();
|
||||||
|
|
||||||
// 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.
|
||||||
for (int i = 0; values != null && i < vars.Length; ++i) {
|
for (int i = 0; values != null && i < vars.Length; ++i) {
|
||||||
var_list.Add (JObject.FromObject (new {
|
var_list.Add (JObject.FromObject (new {
|
||||||
name = vars [i].Name,
|
name = vars [i].Name,
|
||||||
|
|
@ -432,7 +440,7 @@ namespace WsProxy {
|
||||||
|
|
||||||
async Task<Result> EnableBreakPoint (Breakpoint bp, CancellationToken token)
|
async Task<Result> EnableBreakPoint (Breakpoint bp, CancellationToken token)
|
||||||
{
|
{
|
||||||
var asm_name = bp.Location.CliLocation.Method.Assembly.Name.ToLower ();
|
var asm_name = bp.Location.CliLocation.Method.Assembly.Name;
|
||||||
var method_token = bp.Location.CliLocation.Method.Token;
|
var method_token = bp.Location.CliLocation.Method.Token;
|
||||||
var il_offset = bp.Location.CliLocation.Offset;
|
var il_offset = bp.Location.CliLocation.Offset;
|
||||||
|
|
||||||
|
|
@ -514,10 +522,61 @@ namespace WsProxy {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async Task<bool> RemoveBreakpoint(int msg_id, JObject args, CancellationToken token) {
|
||||||
|
var bpid = args? ["breakpointId"]?.Value<string> ();
|
||||||
|
if (bpid?.StartsWith ("dotnet:") != true)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
var the_id = int.Parse (bpid.Substring ("dotnet:".Length));
|
||||||
|
|
||||||
|
var bp = breakpoints.FirstOrDefault (b => b.LocalId == the_id);
|
||||||
|
if (bp == null) {
|
||||||
|
Info ($"Could not find dotnet bp with id {the_id}");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
breakpoints.Remove (bp);
|
||||||
|
//FIXME verify result (and log?)
|
||||||
|
var res = await RemoveBreakPoint (bp, token);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
async Task<Result> RemoveBreakPoint (Breakpoint bp, CancellationToken token)
|
||||||
|
{
|
||||||
|
var o = JObject.FromObject (new {
|
||||||
|
expression = string.Format (MonoCommands.REMOVE_BREAK_POINT, bp.RemoteId),
|
||||||
|
objectGroup = "mono_debugger",
|
||||||
|
includeCommandLineAPI = false,
|
||||||
|
silent = false,
|
||||||
|
returnByValue = true,
|
||||||
|
});
|
||||||
|
|
||||||
|
var res = await SendCommand ("Runtime.evaluate", o, token);
|
||||||
|
var ret_code = res.Value? ["result"]? ["value"]?.Value<int> ();
|
||||||
|
|
||||||
|
if (ret_code.HasValue) {
|
||||||
|
bp.RemoteId = -1;
|
||||||
|
bp.State = BreakPointState.Disabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
async Task SetBreakPoint (int msg_id, BreakPointRequest req, CancellationToken token)
|
async Task SetBreakPoint (int msg_id, BreakPointRequest req, CancellationToken token)
|
||||||
{
|
{
|
||||||
var bp_loc = store.FindBestBreakpoint (req);
|
var bp_loc = store.FindBestBreakpoint (req);
|
||||||
Info ($"BP request for '{req}' runtime ready {runtime_ready}");
|
Info ($"BP request for '{req}' runtime ready {runtime_ready} location '{bp_loc}'");
|
||||||
|
if (bp_loc == null) {
|
||||||
|
|
||||||
|
Info ($"Could not resolve breakpoint request: {req}");
|
||||||
|
SendResponse (msg_id, Result.Err(JObject.FromObject (new {
|
||||||
|
code = (int)MonoErrorCodes.BpNotFound,
|
||||||
|
message = $"C# Breakpoint at {req} not found."
|
||||||
|
})), token);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
Breakpoint bp = null;
|
Breakpoint bp = null;
|
||||||
if (!runtime_ready) {
|
if (!runtime_ready) {
|
||||||
|
|
@ -589,17 +648,8 @@ namespace WsProxy {
|
||||||
var res = new StringWriter ();
|
var res = new StringWriter ();
|
||||||
res.WriteLine ($"//dotnet:{id}");
|
res.WriteLine ($"//dotnet:{id}");
|
||||||
|
|
||||||
try
|
using (var f = new StreamReader (File.Open (src_file.LocalPath, FileMode.Open))) {
|
||||||
{
|
res.Write (f.ReadToEnd ());
|
||||||
using (var f = new StreamReader(File.Open(src_file.LocalPath, FileMode.Open)))
|
|
||||||
{
|
|
||||||
res.Write(f.ReadToEnd());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
res.WriteLine($"Unable to open file {src_file.LocalPath}");
|
|
||||||
res.WriteLine($"\nException:\n{ex}");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var o = JObject.FromObject (new {
|
var o = JObject.FromObject (new {
|
||||||
|
|
|
||||||
|
|
@ -1,13 +0,0 @@
|
||||||
# Running the proxy
|
|
||||||
|
|
||||||
Hack your way around, good luck
|
|
||||||
|
|
||||||
# TODO
|
|
||||||
|
|
||||||
Reconnects - reconsile with all existing breakpoints in the runtime
|
|
||||||
F5 - recognize it and reset debuger state
|
|
||||||
release object groups - we don't send for the groups we create nor do we intercept the fake ones we generate for the runtime
|
|
||||||
Deal with the browser caching stuff across execution - cache invalidation and proper bundling of resources
|
|
||||||
Figure out how to enable/disable debugging
|
|
||||||
Do some target validation - right now we simply assume the debugee has a mono runtime in debug mode
|
|
||||||
|
|
||||||
|
|
@ -1,72 +0,0 @@
|
||||||
using System;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using Microsoft.AspNetCore.Builder;
|
|
||||||
using Microsoft.AspNetCore.Hosting;
|
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
|
||||||
using Microsoft.AspNetCore.Routing;
|
|
||||||
using System.Net.WebSockets;
|
|
||||||
using System.Threading;
|
|
||||||
using System.IO;
|
|
||||||
using System.Text;
|
|
||||||
|
|
||||||
namespace WsProxy
|
|
||||||
{
|
|
||||||
|
|
||||||
internal class Startup {
|
|
||||||
// This method gets called by the runtime. Use this method to add services to the container.
|
|
||||||
// For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940
|
|
||||||
public void ConfigureServices (IServiceCollection services)
|
|
||||||
{
|
|
||||||
services.AddRouting ();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static async Task ProxyMsg (string desc, WebSocket from, WebSocket to)
|
|
||||||
{
|
|
||||||
byte [] buff = new byte [4000];
|
|
||||||
var mem = new MemoryStream ();
|
|
||||||
while (true) {
|
|
||||||
var result = await from.ReceiveAsync (new ArraySegment<byte> (buff), CancellationToken.None);
|
|
||||||
if (result.MessageType == WebSocketMessageType.Close) {
|
|
||||||
await to.SendAsync (new ArraySegment<byte> (mem.GetBuffer (), 0, (int)mem.Length), WebSocketMessageType.Close, true, CancellationToken.None);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (result.EndOfMessage) {
|
|
||||||
mem.Write (buff, 0, result.Count);
|
|
||||||
|
|
||||||
var str = Encoding.UTF8.GetString (mem.GetBuffer (), 0, (int)mem.Length);
|
|
||||||
|
|
||||||
await to.SendAsync (new ArraySegment<byte> (mem.GetBuffer (), 0, (int)mem.Length), WebSocketMessageType.Text, true, CancellationToken.None);
|
|
||||||
mem.SetLength (0);
|
|
||||||
} else {
|
|
||||||
mem.Write (buff, 0, result.Count);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
|
|
||||||
/*
|
|
||||||
public void Configure (IApplicationBuilder app, IHostingEnvironment env)
|
|
||||||
{
|
|
||||||
//loggerFactory.AddConsole();
|
|
||||||
//loggerFactory.AddDebug();
|
|
||||||
app.UseDeveloperExceptionPage ();
|
|
||||||
|
|
||||||
app.UseWebSockets (); app.UseRouter (router => {
|
|
||||||
router.MapGet ("devtools/page/{pageId}", async context => {
|
|
||||||
if (!context.WebSockets.IsWebSocketRequest) {
|
|
||||||
context.Response.StatusCode = 400;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
var proxy = new MonoProxy ();
|
|
||||||
await proxy.Run (context);
|
|
||||||
} catch (Exception e) {
|
|
||||||
Console.WriteLine ("got exception {0}", e);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,7 +1,6 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.AspNetCore.Http;
|
|
||||||
using Newtonsoft.Json.Linq;
|
using Newtonsoft.Json.Linq;
|
||||||
|
|
||||||
using System.Net.WebSockets;
|
using System.Net.WebSockets;
|
||||||
|
|
@ -115,11 +114,6 @@ namespace WsProxy {
|
||||||
return Task.FromResult (false);
|
return Task.FromResult (false);
|
||||||
}
|
}
|
||||||
|
|
||||||
Uri GetBrowserUri (string path)
|
|
||||||
{
|
|
||||||
return new Uri ("ws://localhost:9222" + path);
|
|
||||||
}
|
|
||||||
|
|
||||||
async Task<string> ReadOne (WebSocket socket, CancellationToken token)
|
async Task<string> ReadOne (WebSocket socket, CancellationToken token)
|
||||||
{
|
{
|
||||||
byte [] buff = new byte [4000];
|
byte [] buff = new byte [4000];
|
||||||
|
|
@ -139,8 +133,6 @@ namespace WsProxy {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
WsQueue GetQueueForSocket (WebSocket ws)
|
WsQueue GetQueueForSocket (WebSocket ws)
|
||||||
{
|
{
|
||||||
return queues.FirstOrDefault (q => q.Ws == ws);
|
return queues.FirstOrDefault (q => q.Ws == ws);
|
||||||
|
|
@ -212,8 +204,8 @@ namespace WsProxy {
|
||||||
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));
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<Result> SendCommand (string method, JObject args, CancellationToken token) {
|
internal async Task<Result> SendCommand (string method, JObject args, CancellationToken token) {
|
||||||
Debug ($"sending command {method}: {args}");
|
// Debug ($"sending command {method}: {args}");
|
||||||
return await SendCommandInternal (method, args, token);
|
return await SendCommandInternal (method, args, token);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -250,7 +242,7 @@ namespace WsProxy {
|
||||||
Send (this.ide, o, token);
|
Send (this.ide, o, token);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SendResponse (int id, Result result, CancellationToken token)
|
internal void SendResponse (int id, Result result, CancellationToken token)
|
||||||
{
|
{
|
||||||
//Debug ($"sending response: {id}: {result.ToJObject (id)}");
|
//Debug ($"sending response: {id}: {result.ToJObject (id)}");
|
||||||
SendResponseInternal (id, result, token);
|
SendResponseInternal (id, result, token);
|
||||||
|
|
@ -263,11 +255,11 @@ namespace WsProxy {
|
||||||
Send (this.ide, o, token);
|
Send (this.ide, o, token);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task Run (HttpContext context, Uri browserUri)
|
// , HttpContext context)
|
||||||
|
public async Task Run (Uri browserUri, WebSocket ideSocket)
|
||||||
{
|
{
|
||||||
//var browserUri = GetBrowserUri (context.Request.Path.ToString ());
|
|
||||||
Debug ("wsproxy start");
|
Debug ("wsproxy start");
|
||||||
using (this.ide = await context.WebSockets.AcceptWebSocketAsync ()) {
|
using (this.ide = ideSocket) {
|
||||||
Debug ("ide connected");
|
Debug ("ide connected");
|
||||||
queues.Add (new WsQueue (this.ide));
|
queues.Add (new WsQueue (this.ide));
|
||||||
using (this.browser = new ClientWebSocket ()) {
|
using (this.browser = new ClientWebSocket ()) {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue