From 5a836ef0a6a9bb1b601a50a6a46920273b2070fe Mon Sep 17 00:00:00 2001 From: Steve Sanderson Date: Wed, 7 Nov 2018 11:36:15 +0000 Subject: [PATCH] Update to ws-proxy code matching Mono repo 5e318afd --- .../MonoDebugProxyAppBuilderExtensions.cs | 3 +- .../MonoDebugProxy/ws-proxy/DebugStore.cs | 11 +- .../MonoDebugProxy/ws-proxy/MonoProxy.cs | 100 +++++++++++++----- .../MonoDebugProxy/ws-proxy/README.md | 13 --- .../MonoDebugProxy/ws-proxy/Startup.cs | 72 ------------- .../MonoDebugProxy/ws-proxy/WsProxy.cs | 20 ++-- 6 files changed, 93 insertions(+), 126 deletions(-) delete mode 100644 src/Microsoft.AspNetCore.Blazor.Server/MonoDebugProxy/ws-proxy/README.md delete mode 100644 src/Microsoft.AspNetCore.Blazor.Server/MonoDebugProxy/ws-proxy/Startup.cs diff --git a/src/Microsoft.AspNetCore.Blazor.Server/MonoDebugProxy/MonoDebugProxyAppBuilderExtensions.cs b/src/Microsoft.AspNetCore.Blazor.Server/MonoDebugProxy/MonoDebugProxyAppBuilderExtensions.cs index e67bf174d7..7cb83ba9a3 100644 --- a/src/Microsoft.AspNetCore.Blazor.Server/MonoDebugProxy/MonoDebugProxyAppBuilderExtensions.cs +++ b/src/Microsoft.AspNetCore.Blazor.Server/MonoDebugProxy/MonoDebugProxyAppBuilderExtensions.cs @@ -52,7 +52,8 @@ namespace Microsoft.AspNetCore.Builder } 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) diff --git a/src/Microsoft.AspNetCore.Blazor.Server/MonoDebugProxy/ws-proxy/DebugStore.cs b/src/Microsoft.AspNetCore.Blazor.Server/MonoDebugProxy/ws-proxy/DebugStore.cs index bb274946e9..9d29cc494a 100644 --- a/src/Microsoft.AspNetCore.Blazor.Server/MonoDebugProxy/ws-proxy/DebugStore.cs +++ b/src/Microsoft.AspNetCore.Blazor.Server/MonoDebugProxy/ws-proxy/DebugStore.cs @@ -14,6 +14,10 @@ namespace WsProxy { public int Line { 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) { if (args == null) @@ -457,13 +461,18 @@ namespace WsProxy { var doc = GetFileById (src_id); var res = new List (); + 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 sp in m.methodDef.DebugInformation.SequencePoints) { if (Match (sp, start, end)) res.Add (new SourceLocation (m, sp)); } } - return res; } diff --git a/src/Microsoft.AspNetCore.Blazor.Server/MonoDebugProxy/ws-proxy/MonoProxy.cs b/src/Microsoft.AspNetCore.Blazor.Server/MonoDebugProxy/ws-proxy/MonoProxy.cs index 273227dd22..ae6343bca4 100644 --- a/src/Microsoft.AspNetCore.Blazor.Server/MonoDebugProxy/ws-proxy/MonoProxy.cs +++ b/src/Microsoft.AspNetCore.Blazor.Server/MonoDebugProxy/ws-proxy/MonoProxy.cs @@ -1,7 +1,6 @@ using System; using System.Linq; using System.Threading.Tasks; -using Microsoft.AspNetCore.Http; using Newtonsoft.Json.Linq; 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 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 REMOVE_BREAK_POINT = "MONO.mono_wasm_remove_breakpoint({0})"; public const string GET_LOADED_FILES = "MONO.mono_wasm_get_loaded_files()"; public const string CLEAR_ALL_BREAKPOINTS = "MONO.mono_wasm_clear_all_breakpoints()"; } + internal enum MonoErrorCodes { + BpNotFound = 100000, + } + + internal class MonoConstants { public const string RUNTIME_IS_READY = "mono_wasm_runtime_ready"; } @@ -156,6 +161,9 @@ namespace WsProxy { } break; } + case "Debugger.removeBreakpoint": { + return await RemoveBreakpoint (id, args, token); + } case "Debugger.resume": { await OnResume (token); @@ -265,14 +273,14 @@ namespace WsProxy { var method = asm.GetMethodByToken (method_token); var location = method.GetLocationByIl (il_pos); - // When hitting a breakpoint on the "IncrementCount" method in the standard - // Blazor project template, one of the stack frames is inside mscorlib.dll - // and we get location==null for it. It will trigger a NullReferenceException - // if we don't skip over that stack frame. - if (location == null) - { - continue; - } + // When hitting a breakpoint on the "IncrementCount" method in the standard + // Blazor project template, one of the stack frames is inside mscorlib.dll + // and we get location==null for it. It will trigger a NullReferenceException + // if we don't skip over that stack frame. + if (location == null) + { + continue; + } Info ($"frame il offset: {il_pos} method token: {method_token} assembly name: {assembly_name}"); Info ($"\tmethod {method.Name} location: {location}"); @@ -413,9 +421,9 @@ namespace WsProxy { var var_list = new List (); - // 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. + // 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; values != null && i < vars.Length; ++i) { var_list.Add (JObject.FromObject (new { name = vars [i].Name, @@ -432,7 +440,7 @@ namespace WsProxy { async Task 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 il_offset = bp.Location.CliLocation.Offset; @@ -514,10 +522,61 @@ namespace WsProxy { } } + async Task RemoveBreakpoint(int msg_id, JObject args, CancellationToken token) { + var bpid = args? ["breakpointId"]?.Value (); + 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 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 (); + + if (ret_code.HasValue) { + bp.RemoteId = -1; + bp.State = BreakPointState.Disabled; + } + + return res; + } + async Task SetBreakPoint (int msg_id, BreakPointRequest req, CancellationToken token) { 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; if (!runtime_ready) { @@ -589,17 +648,8 @@ namespace WsProxy { var res = new StringWriter (); res.WriteLine ($"//dotnet:{id}"); - try - { - 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}"); + using (var f = new StreamReader (File.Open (src_file.LocalPath, FileMode.Open))) { + res.Write (f.ReadToEnd ()); } var o = JObject.FromObject (new { diff --git a/src/Microsoft.AspNetCore.Blazor.Server/MonoDebugProxy/ws-proxy/README.md b/src/Microsoft.AspNetCore.Blazor.Server/MonoDebugProxy/ws-proxy/README.md deleted file mode 100644 index 66bd02c512..0000000000 --- a/src/Microsoft.AspNetCore.Blazor.Server/MonoDebugProxy/ws-proxy/README.md +++ /dev/null @@ -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 - diff --git a/src/Microsoft.AspNetCore.Blazor.Server/MonoDebugProxy/ws-proxy/Startup.cs b/src/Microsoft.AspNetCore.Blazor.Server/MonoDebugProxy/ws-proxy/Startup.cs deleted file mode 100644 index 8bf23134fa..0000000000 --- a/src/Microsoft.AspNetCore.Blazor.Server/MonoDebugProxy/ws-proxy/Startup.cs +++ /dev/null @@ -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 (buff), CancellationToken.None); - if (result.MessageType == WebSocketMessageType.Close) { - await to.SendAsync (new ArraySegment (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 (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); - } - }); - }); - } - */ - } -} diff --git a/src/Microsoft.AspNetCore.Blazor.Server/MonoDebugProxy/ws-proxy/WsProxy.cs b/src/Microsoft.AspNetCore.Blazor.Server/MonoDebugProxy/ws-proxy/WsProxy.cs index 522d54e6ac..0629491680 100644 --- a/src/Microsoft.AspNetCore.Blazor.Server/MonoDebugProxy/ws-proxy/WsProxy.cs +++ b/src/Microsoft.AspNetCore.Blazor.Server/MonoDebugProxy/ws-proxy/WsProxy.cs @@ -1,7 +1,6 @@ using System; using System.Linq; using System.Threading.Tasks; -using Microsoft.AspNetCore.Http; using Newtonsoft.Json.Linq; using System.Net.WebSockets; @@ -115,11 +114,6 @@ namespace WsProxy { return Task.FromResult (false); } - Uri GetBrowserUri (string path) - { - return new Uri ("ws://localhost:9222" + path); - } - async Task ReadOne (WebSocket socket, CancellationToken token) { byte [] buff = new byte [4000]; @@ -139,8 +133,6 @@ namespace WsProxy { } } - - WsQueue GetQueueForSocket (WebSocket ws) { return queues.FirstOrDefault (q => q.Ws == ws); @@ -212,8 +204,8 @@ namespace WsProxy { pending_ops.Add (OnCommand (res ["id"].Value (), res ["method"].Value (), res ["params"] as JObject, token)); } - public async Task SendCommand (string method, JObject args, CancellationToken token) { - Debug ($"sending command {method}: {args}"); + internal async Task SendCommand (string method, JObject args, CancellationToken token) { + // Debug ($"sending command {method}: {args}"); return await SendCommandInternal (method, args, token); } @@ -250,7 +242,7 @@ namespace WsProxy { 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)}"); SendResponseInternal (id, result, token); @@ -263,11 +255,11 @@ namespace WsProxy { 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"); - using (this.ide = await context.WebSockets.AcceptWebSocketAsync ()) { + using (this.ide = ideSocket) { Debug ("ide connected"); queues.Add (new WsQueue (this.ide)); using (this.browser = new ClientWebSocket ()) {