Skip to content

Commit

Permalink
[wasm][debugger] Detect initial status of pause on exceptions. (#54040)
Browse files Browse the repository at this point in the history
* Detect initial status of pause on exceptions.

* Changing what @radical suggested.

* Changing more things.

* 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.

* Adding a test for Reload page with ALL set.

* Fixing merge conflicts.

* setting icordebug = false.

* Removing unrelated change.
  • Loading branch information
thaystg authored Jun 25, 2021
1 parent 75b6c99 commit 881e902
Show file tree
Hide file tree
Showing 3 changed files with 168 additions and 2 deletions.
3 changes: 3 additions & 0 deletions src/mono/wasm/debugger/BrowserDebugProxy/DevToolsHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -279,6 +279,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<Frame> CallStack { get; set; }

public string[] LoadedFiles { get; set; }
Expand Down
62 changes: 60 additions & 2 deletions src/mono/wasm/debugger/BrowserDebugProxy/MonoProxy.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ internal class MonoProxy : DevToolsProxy
private static HttpClient client = new HttpClient();
private HashSet<SessionId> sessions = new HashSet<SessionId>();
private Dictionary<SessionId, ExecutionContext> contexts = new Dictionary<SessionId, ExecutionContext>();
private const string sPauseOnUncaught = "pause_on_uncaught";
private const string sPauseOnCaught = "pause_on_caught";

public MonoProxy(ILoggerFactory loggerFactory, IList<string> urlSymbolServerList) : base(loggerFactory)
{
Expand Down Expand Up @@ -122,8 +124,41 @@ protected override async Task<bool> AcceptEvent(SessionId sessionId, string meth
return true;
}

case "Runtime.exceptionThrown":
{
if (!GetContext(sessionId).IsRuntimeReady)
{
string exceptionError = args?["exceptionDetails"]?["exception"]?["value"]?.Value<string>();
if (exceptionError == sPauseOnUncaught || exceptionError == sPauseOnCaught)
{
return true;
}
}
break;
}

case "Debugger.paused":
{
if (!GetContext(sessionId).IsRuntimeReady)
{
string reason = args?["reason"]?.Value<string>();
if (reason == "exception")
{
string exceptionError = args?["data"]?["value"]?.Value<string>();
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;
}
}
}
//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<string>();
switch (top_func) {
Expand Down Expand Up @@ -398,7 +433,23 @@ protected override async Task<bool> AcceptCommand(MessageId id, string method, J
case "Debugger.setPauseOnExceptions":
{
string state = args["state"].Value<string>();
await sdbHelper.EnableExceptions(id, 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 sdbHelper.EnableExceptions(id, state, token);
// Pass this on to JS too
return false;
}
Expand Down Expand Up @@ -1152,6 +1203,11 @@ private async Task<DebugStore> RuntimeReady(SessionId sessionId, CancellationTok
Log("verbose", $"Failed to clear breakpoints");
}

if (context.PauseOnCaught && context.PauseOnUncaught)
await sdbHelper.EnableExceptions(sessionId, "all", token);
else if (context.PauseOnUncaught)
await sdbHelper.EnableExceptions(sessionId, "uncaught", token);

await sdbHelper.SetProtocolVersion(sessionId, token);
await sdbHelper.EnableReceiveUserBreakRequest(sessionId, token);

Expand Down Expand Up @@ -1289,10 +1345,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 \"{sPauseOnUncaught}\";";
string checkCaughtExceptions = $"try {{throw \"{sPauseOnCaught}\";}} 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; {checkCaughtExceptions} {checkUncaughtExceptions}" }),
token);

if (sessionId != SessionId.Null && !res.IsOk)
Expand Down
105 changes: 105 additions & 0 deletions src/mono/wasm/debugger/DebuggerTestSuite/ExceptionTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
using System.Threading.Tasks;
using Microsoft.WebAssembly.Diagnostics;
using Newtonsoft.Json.Linq;
using System.Threading;
using Xunit;

namespace DebuggerTests
Expand Down Expand Up @@ -191,6 +192,110 @@ 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<string>(), "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<string>());
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<string>(), "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<string>());
CheckString(exception_members, "message", "not implemented caught");

pause_location = await WaitForManagedException(null);
AssertEqual("run", pause_location["callFrames"]?[0]?["functionName"]?.Value<string>(), "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<string>());
CheckString(exception_members, "message", "not implemented uncaught");
}


async Task<JObject> WaitForManagedException(JObject pause_location)
{
while (true)
Expand Down

0 comments on commit 881e902

Please sign in to comment.