From 68080fd066e55d0e6c9a980561e9e886b0768e7c Mon Sep 17 00:00:00 2001 From: Thays Date: Thu, 10 Jun 2021 22:13:11 -0300 Subject: [PATCH 1/8] Detect initial status of pause on exceptions. --- .../BrowserDebugProxy/DevToolsHelper.cs | 3 ++ .../debugger/BrowserDebugProxy/MonoProxy.cs | 41 ++++++++++++++++++- 2 files changed, 43 insertions(+), 1 deletion(-) diff --git a/src/mono/wasm/debugger/BrowserDebugProxy/DevToolsHelper.cs b/src/mono/wasm/debugger/BrowserDebugProxy/DevToolsHelper.cs index efb2a7b4a2a79..e6c77d79b25ea 100644 --- a/src/mono/wasm/debugger/BrowserDebugProxy/DevToolsHelper.cs +++ b/src/mono/wasm/debugger/BrowserDebugProxy/DevToolsHelper.cs @@ -295,6 +295,9 @@ internal class ExecutionContext public int Id { get; set; } public object AuxData { get; set; } + public bool PauseOnUncaught { get; set; } + public bool PauseOnCaught { get; set; } + public List CallStack { get; set; } public string[] LoadedFiles { get; set; } diff --git a/src/mono/wasm/debugger/BrowserDebugProxy/MonoProxy.cs b/src/mono/wasm/debugger/BrowserDebugProxy/MonoProxy.cs index b80a4483ca02d..15bd219620d51 100644 --- a/src/mono/wasm/debugger/BrowserDebugProxy/MonoProxy.cs +++ b/src/mono/wasm/debugger/BrowserDebugProxy/MonoProxy.cs @@ -120,8 +120,40 @@ protected override async Task AcceptEvent(SessionId sessionId, string meth return true; } + case "Runtime.exceptionThrown": + { + if (!GetContext(sessionId).IsRuntimeReady) + { + string exceptionError = args?["exceptionDetails"]?["exception"]?["value"]?.Value(); + if (exceptionError == "pause_on_uncaught") + { + GetContext(sessionId).PauseOnUncaught = true; + return true; + } + if (exceptionError == "pause_on_caught") + { + GetContext(sessionId).PauseOnCaught = true; + return true; + } + } + break; + } + case "Debugger.paused": { + if (!GetContext(sessionId).IsRuntimeReady) + { + string reason = args?["reason"]?.Value(); + if (reason == "exception") + { + string exceptionError = args?["data"]?["value"]?.Value(); + if (exceptionError == "pause_on_uncaught" || exceptionError == "pause_on_caught") + { + await SendCommand(sessionId, "Debugger.resume", new JObject(), token); + return true; + } + } + } //TODO figure out how to stich out more frames and, in particular what happens when real wasm is on the stack string top_func = args?["callFrames"]?[0]?["functionName"]?.Value(); switch (top_func) { @@ -1078,6 +1110,11 @@ private async Task RuntimeReady(SessionId sessionId, CancellationTok Log("verbose", $"Failed to clear breakpoints due to {clear_result}"); } + if (context.PauseOnCaught && context.PauseOnUncaught) + await SendMonoCommand(sessionId, MonoCommands.SetPauseOnExceptions("all"), token); + if (context.PauseOnUncaught) + await SendMonoCommand(sessionId, MonoCommands.SetPauseOnExceptions("uncaught"), token); + DebugStore store = await LoadStore(sessionId, token); context.ready.SetResult(store); @@ -1214,10 +1251,12 @@ private async Task AttachToTarget(SessionId sessionId, CancellationToken token) // see https://github.com/mono/mono/issues/19549 for background if (sessions.Add(sessionId)) { + string checkUncaughtExceptions = "throw \"pause_on_uncaught\";"; + string checkCaughtExceptions = "try {throw \"pause_on_caught\";} catch {}"; await SendMonoCommand(sessionId, new MonoCommands("globalThis.dotnetDebugger = true"), token); Result res = await SendCommand(sessionId, "Page.addScriptToEvaluateOnNewDocument", - JObject.FromObject(new { source = "globalThis.dotnetDebugger = true; delete navigator.constructor.prototype.webdriver" }), + JObject.FromObject(new { source = $"globalThis.dotnetDebugger = true; delete navigator.constructor.prototype.webdriver; {checkUncaughtExceptions} {checkCaughtExceptions}" }), token); if (sessionId != SessionId.Null && !res.IsOk) From de352a1bd82b1c2c584ee480e15ed2a81d539e32 Mon Sep 17 00:00:00 2001 From: Thays Date: Tue, 15 Jun 2021 19:06:37 -0300 Subject: [PATCH 2/8] Changing what @radical suggested. --- .../wasm/debugger/BrowserDebugProxy/MonoProxy.cs | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/mono/wasm/debugger/BrowserDebugProxy/MonoProxy.cs b/src/mono/wasm/debugger/BrowserDebugProxy/MonoProxy.cs index 15bd219620d51..b8ac750380aac 100644 --- a/src/mono/wasm/debugger/BrowserDebugProxy/MonoProxy.cs +++ b/src/mono/wasm/debugger/BrowserDebugProxy/MonoProxy.cs @@ -22,6 +22,8 @@ internal class MonoProxy : DevToolsProxy private static HttpClient client = new HttpClient(); private HashSet sessions = new HashSet(); private Dictionary contexts = new Dictionary(); + private const string sPauseOnUncaught = "pause_on_uncaught"; + private const string sPauseOnCaught = "pause_on_caught"; public MonoProxy(ILoggerFactory loggerFactory, IList urlSymbolServerList) : base(loggerFactory) { @@ -125,12 +127,12 @@ protected override async Task AcceptEvent(SessionId sessionId, string meth if (!GetContext(sessionId).IsRuntimeReady) { string exceptionError = args?["exceptionDetails"]?["exception"]?["value"]?.Value(); - if (exceptionError == "pause_on_uncaught") + if (exceptionError == sPauseOnUncaught) { GetContext(sessionId).PauseOnUncaught = true; return true; } - if (exceptionError == "pause_on_caught") + if (exceptionError == sPauseOnCaught) { GetContext(sessionId).PauseOnCaught = true; return true; @@ -147,7 +149,7 @@ protected override async Task AcceptEvent(SessionId sessionId, string meth if (reason == "exception") { string exceptionError = args?["data"]?["value"]?.Value(); - if (exceptionError == "pause_on_uncaught" || exceptionError == "pause_on_caught") + if (exceptionError == sPauseOnUncaught || exceptionError == sPauseOnCaught) { await SendCommand(sessionId, "Debugger.resume", new JObject(), token); return true; @@ -1112,7 +1114,7 @@ private async Task RuntimeReady(SessionId sessionId, CancellationTok if (context.PauseOnCaught && context.PauseOnUncaught) await SendMonoCommand(sessionId, MonoCommands.SetPauseOnExceptions("all"), token); - if (context.PauseOnUncaught) + else if (context.PauseOnUncaught) await SendMonoCommand(sessionId, MonoCommands.SetPauseOnExceptions("uncaught"), token); DebugStore store = await LoadStore(sessionId, token); @@ -1251,8 +1253,8 @@ private async Task AttachToTarget(SessionId sessionId, CancellationToken token) // see https://github.com/mono/mono/issues/19549 for background if (sessions.Add(sessionId)) { - string checkUncaughtExceptions = "throw \"pause_on_uncaught\";"; - string checkCaughtExceptions = "try {throw \"pause_on_caught\";} catch {}"; + string checkUncaughtExceptions = $"throw \"{sPauseOnUncaught}\";"; + string checkCaughtExceptions = $"try {{throw \"{sPauseOnCaught}\";}} catch {{}}"; await SendMonoCommand(sessionId, new MonoCommands("globalThis.dotnetDebugger = true"), token); Result res = await SendCommand(sessionId, "Page.addScriptToEvaluateOnNewDocument", From 232322e953be8abd4fe780acd2db9ec8f641dc94 Mon Sep 17 00:00:00 2001 From: Thays Date: Tue, 15 Jun 2021 19:48:21 -0300 Subject: [PATCH 3/8] Changing more things. --- .../debugger/BrowserDebugProxy/MonoProxy.cs | 37 ++++++++++++++----- 1 file changed, 27 insertions(+), 10 deletions(-) diff --git a/src/mono/wasm/debugger/BrowserDebugProxy/MonoProxy.cs b/src/mono/wasm/debugger/BrowserDebugProxy/MonoProxy.cs index b8ac750380aac..2cf987da59c37 100644 --- a/src/mono/wasm/debugger/BrowserDebugProxy/MonoProxy.cs +++ b/src/mono/wasm/debugger/BrowserDebugProxy/MonoProxy.cs @@ -127,14 +127,8 @@ protected override async Task AcceptEvent(SessionId sessionId, string meth if (!GetContext(sessionId).IsRuntimeReady) { string exceptionError = args?["exceptionDetails"]?["exception"]?["value"]?.Value(); - if (exceptionError == sPauseOnUncaught) + if (exceptionError == sPauseOnUncaught || exceptionError == sPauseOnCaught) { - GetContext(sessionId).PauseOnUncaught = true; - return true; - } - if (exceptionError == sPauseOnCaught) - { - GetContext(sessionId).PauseOnCaught = true; return true; } } @@ -149,9 +143,16 @@ protected override async Task AcceptEvent(SessionId sessionId, string meth if (reason == "exception") { string exceptionError = args?["data"]?["value"]?.Value(); - if (exceptionError == sPauseOnUncaught || exceptionError == sPauseOnCaught) + if (exceptionError == sPauseOnUncaught) { await SendCommand(sessionId, "Debugger.resume", new JObject(), token); + GetContext(sessionId).PauseOnUncaught = true; + return true; + } + if (exceptionError == sPauseOnCaught) + { + await SendCommand(sessionId, "Debugger.resume", new JObject(), token); + GetContext(sessionId).PauseOnCaught = true; return true; } } @@ -422,7 +423,23 @@ protected override async Task AcceptCommand(MessageId id, string method, J case "Debugger.setPauseOnExceptions": { string state = args["state"].Value(); - await SendMonoCommand(id, MonoCommands.SetPauseOnExceptions(state), token); + if (!context.IsRuntimeReady) + { + context.PauseOnCaught = false; + context.PauseOnUncaught = false; + switch (state) + { + case "all": + context.PauseOnCaught = true; + context.PauseOnUncaught = true; + break; + case "uncaught": + context.PauseOnUncaught = true; + break; + } + } + else + await SendMonoCommand(id, MonoCommands.SetPauseOnExceptions(state), token); // Pass this on to JS too return false; } @@ -1258,7 +1275,7 @@ private async Task AttachToTarget(SessionId sessionId, CancellationToken token) await SendMonoCommand(sessionId, new MonoCommands("globalThis.dotnetDebugger = true"), token); Result res = await SendCommand(sessionId, "Page.addScriptToEvaluateOnNewDocument", - JObject.FromObject(new { source = $"globalThis.dotnetDebugger = true; delete navigator.constructor.prototype.webdriver; {checkUncaughtExceptions} {checkCaughtExceptions}" }), + JObject.FromObject(new { source = $"globalThis.dotnetDebugger = true; delete navigator.constructor.prototype.webdriver; {checkCaughtExceptions} {checkUncaughtExceptions}" }), token); if (sessionId != SessionId.Null && !res.IsOk) From cb1bc7f2d4bd86d54a802e61c569d741b1125862 Mon Sep 17 00:00:00 2001 From: Thays Date: Thu, 17 Jun 2021 03:40:21 -0300 Subject: [PATCH 4/8] Test case created. I could not test the pause on "all" exceptions because if I enable the pause on caught exceptions and reload the page it will stop in a lot of exceptions other then the one that I inserted in AttachToTarget. --- .../DebuggerTestSuite/ExceptionTests.cs | 41 +++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/src/mono/wasm/debugger/DebuggerTestSuite/ExceptionTests.cs b/src/mono/wasm/debugger/DebuggerTestSuite/ExceptionTests.cs index 1be0b18f325b5..bcbd7e1fb3471 100644 --- a/src/mono/wasm/debugger/DebuggerTestSuite/ExceptionTests.cs +++ b/src/mono/wasm/debugger/DebuggerTestSuite/ExceptionTests.cs @@ -6,6 +6,7 @@ using System.Threading.Tasks; using Microsoft.WebAssembly.Diagnostics; using Newtonsoft.Json.Linq; +using System.Threading; using Xunit; namespace DebuggerTests @@ -191,6 +192,46 @@ await CheckValue(pause_location["data"], JObject.FromObject(new CheckString(exception_members, "message", exception_message); } + [Fact] + public async Task ExceptionTestUncaughtWithReload() + { + string entry_method_name = "[debugger-test] DebuggerTests.ExceptionTestsClass:TestExceptions"; + var debugger_test_loc = "dotnet://debugger-test.dll/debugger-exception-test.cs"; + + await SetPauseOnException("uncaught"); + + await SendCommand("Page.enable", null); + await SendCommand("Page.reload", JObject.FromObject(new + { + ignoreCache = true + })); + Thread.Sleep(1000); + + var eval_expr = "window.setTimeout(function() { invoke_static_method (" + + $"'{entry_method_name}'" + + "); }, 1);"; + + var pause_location = await EvaluateAndCheck(eval_expr, null, 0, 0, null); + //stop in the managed caught exception + pause_location = await WaitForManagedException(pause_location); + + AssertEqual("run", pause_location["callFrames"]?[0]?["functionName"]?.Value(), "pause1"); + + //stop in the uncaught exception + CheckLocation(debugger_test_loc, 28, 16, scripts, pause_location["callFrames"][0]["location"]); + + await CheckValue(pause_location["data"], JObject.FromObject(new + { + type = "object", + subtype = "error", + className = "DebuggerTests.CustomException", + uncaught = true + }), "exception1.data"); + + var exception_members = await GetProperties(pause_location["data"]["objectId"]?.Value()); + CheckString(exception_members, "message", "not implemented uncaught"); + } + async Task WaitForManagedException(JObject pause_location) { while (true) From 6e4f0dd1e53614d91abcc39c46d02730a4c400da Mon Sep 17 00:00:00 2001 From: Thays Date: Thu, 17 Jun 2021 04:55:36 -0300 Subject: [PATCH 5/8] Adding a test for Reload page with ALL set. --- .../DebuggerTestSuite/ExceptionTests.cs | 64 +++++++++++++++++++ 1 file changed, 64 insertions(+) diff --git a/src/mono/wasm/debugger/DebuggerTestSuite/ExceptionTests.cs b/src/mono/wasm/debugger/DebuggerTestSuite/ExceptionTests.cs index bcbd7e1fb3471..eb0ebe0cc0d54 100644 --- a/src/mono/wasm/debugger/DebuggerTestSuite/ExceptionTests.cs +++ b/src/mono/wasm/debugger/DebuggerTestSuite/ExceptionTests.cs @@ -232,6 +232,70 @@ await CheckValue(pause_location["data"], JObject.FromObject(new CheckString(exception_members, "message", "not implemented uncaught"); } + [Fact] + public async Task ExceptionTestAllWithReload() + { + string entry_method_name = "[debugger-test] DebuggerTests.ExceptionTestsClass:TestExceptions"; + var debugger_test_loc = "dotnet://debugger-test.dll/debugger-exception-test.cs"; + + await SetPauseOnException("all"); + + await SendCommand("Page.enable", null); + var pause_location = await SendCommandAndCheck(JObject.FromObject(new + { + ignoreCache = true + }), "Page.reload",null, 0, 0, null); + Thread.Sleep(1000); + + //send a lot of resumes to "skip" all the pauses on caught exception and completely reload the page + int i = 0; + while (i < 100) + { + Result res = await cli.SendCommand("Debugger.resume", null, token); + i++; + } + + + var eval_expr = "window.setTimeout(function() { invoke_static_method (" + + $"'{entry_method_name}'" + + "); }, 1);"; + + pause_location = await EvaluateAndCheck(eval_expr, null, 0, 0, null); + //stop in the managed caught exception + pause_location = await WaitForManagedException(pause_location); + + AssertEqual("run", pause_location["callFrames"]?[0]?["functionName"]?.Value(), "pause0"); + + await CheckValue(pause_location["data"], JObject.FromObject(new + { + type = "object", + subtype = "error", + className = "DebuggerTests.CustomException", + uncaught = false + }), "exception0.data"); + + var exception_members = await GetProperties(pause_location["data"]["objectId"]?.Value()); + CheckString(exception_members, "message", "not implemented caught"); + + pause_location = await WaitForManagedException(null); + AssertEqual("run", pause_location["callFrames"]?[0]?["functionName"]?.Value(), "pause1"); + + //stop in the uncaught exception + CheckLocation(debugger_test_loc, 28, 16, scripts, pause_location["callFrames"][0]["location"]); + + await CheckValue(pause_location["data"], JObject.FromObject(new + { + type = "object", + subtype = "error", + className = "DebuggerTests.CustomException", + uncaught = true + }), "exception1.data"); + + exception_members = await GetProperties(pause_location["data"]["objectId"]?.Value()); + CheckString(exception_members, "message", "not implemented uncaught"); + } + + async Task WaitForManagedException(JObject pause_location) { while (true) From 8a27dff4dd74cd7d9a2cc86a76446f7287dcbbc0 Mon Sep 17 00:00:00 2001 From: Thays Date: Wed, 23 Jun 2021 20:25:55 -0300 Subject: [PATCH 6/8] Fixing merge conflicts. --- src/mono/wasm/debugger/BrowserDebugProxy/MonoProxy.cs | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/mono/wasm/debugger/BrowserDebugProxy/MonoProxy.cs b/src/mono/wasm/debugger/BrowserDebugProxy/MonoProxy.cs index a3725334b9d3d..f45d31ca1c874 100644 --- a/src/mono/wasm/debugger/BrowserDebugProxy/MonoProxy.cs +++ b/src/mono/wasm/debugger/BrowserDebugProxy/MonoProxy.cs @@ -1203,15 +1203,13 @@ private async Task RuntimeReady(SessionId sessionId, CancellationTok Log("verbose", $"Failed to clear breakpoints"); } -<<<<<<< HEAD if (context.PauseOnCaught && context.PauseOnUncaught) - await SendMonoCommand(sessionId, MonoCommands.SetPauseOnExceptions("all"), token); + await sdbHelper.EnableExceptions(sessionId, "all", token); else if (context.PauseOnUncaught) - await SendMonoCommand(sessionId, MonoCommands.SetPauseOnExceptions("uncaught"), token); -======= + await sdbHelper.EnableExceptions(sessionId, "uncaught", token); + await sdbHelper.SetProtocolVersion(sessionId, token); await sdbHelper.EnableReceiveUserBreakRequest(sessionId, token); ->>>>>>> origin/main DebugStore store = await LoadStore(sessionId, token); From 5900616a43c149e6433d0e71bbee36a89089127f Mon Sep 17 00:00:00 2001 From: Thays Date: Wed, 23 Jun 2021 23:28:30 -0300 Subject: [PATCH 7/8] setting icordebug = false. --- src/mono/wasm/debugger/BrowserDebugProxy/MonoSDBHelper.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/mono/wasm/debugger/BrowserDebugProxy/MonoSDBHelper.cs b/src/mono/wasm/debugger/BrowserDebugProxy/MonoSDBHelper.cs index c296c8501473a..2920fbb446b95 100644 --- a/src/mono/wasm/debugger/BrowserDebugProxy/MonoSDBHelper.cs +++ b/src/mono/wasm/debugger/BrowserDebugProxy/MonoSDBHelper.cs @@ -547,6 +547,7 @@ public async Task SetProtocolVersion(SessionId sessionId, CancellationToke var command_params_writer = new MonoBinaryWriter(command_params); command_params_writer.Write(MAJOR_VERSION); command_params_writer.Write(MINOR_VERSION); + command_params_writer.Write((byte)0); var ret_debugger_cmd_reader = await SendDebuggerAgentCommand(sessionId, CmdVM.SetProtocolVersion, command_params, token); return true; From 67b266f5edce4ef7abfd9bd7bcabf337027d91bb Mon Sep 17 00:00:00 2001 From: Thays Date: Thu, 24 Jun 2021 02:31:03 -0300 Subject: [PATCH 8/8] Removing unrelated change. --- src/mono/wasm/debugger/BrowserDebugProxy/MonoSDBHelper.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/mono/wasm/debugger/BrowserDebugProxy/MonoSDBHelper.cs b/src/mono/wasm/debugger/BrowserDebugProxy/MonoSDBHelper.cs index 2920fbb446b95..c296c8501473a 100644 --- a/src/mono/wasm/debugger/BrowserDebugProxy/MonoSDBHelper.cs +++ b/src/mono/wasm/debugger/BrowserDebugProxy/MonoSDBHelper.cs @@ -547,7 +547,6 @@ public async Task SetProtocolVersion(SessionId sessionId, CancellationToke var command_params_writer = new MonoBinaryWriter(command_params); command_params_writer.Write(MAJOR_VERSION); command_params_writer.Write(MINOR_VERSION); - command_params_writer.Write((byte)0); var ret_debugger_cmd_reader = await SendDebuggerAgentCommand(sessionId, CmdVM.SetProtocolVersion, command_params, token); return true;