Update to ws-proxy code matching Mono repo 5e318afd

This commit is contained in:
Steve Sanderson 2018-11-07 11:36:15 +00:00
parent edff13f25a
commit 5a836ef0a6
6 changed files with 93 additions and 126 deletions

View File

@ -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)

View File

@ -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<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 sp in m.methodDef.DebugInformation.SequencePoints) {
if (Match (sp, start, end))
res.Add (new SourceLocation (m, sp));
}
}
return res;
}

View File

@ -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<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.
// 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<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 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)
{
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 {

View File

@ -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

View File

@ -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);
}
});
});
}
*/
}
}

View File

@ -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<string> 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<int> (), res ["method"].Value<string> (), res ["params"] as JObject, token));
}
public async Task<Result> SendCommand (string method, JObject args, CancellationToken token) {
Debug ($"sending command {method}: {args}");
internal async Task<Result> 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 ()) {