Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 9 additions & 2 deletions src/mono/wasm/debugger/BrowserDebugProxy/DevToolsHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -267,6 +267,14 @@ internal enum StepKind
Out
}

internal enum PauseOnExceptionsKind
{
Unset,
None,
Uncaught,
All
}

internal class ExecutionContext
{
public string DebugId { get; set; }
Expand All @@ -279,8 +287,7 @@ internal class ExecutionContext
public int Id { get; set; }
public object AuxData { get; set; }

public bool PauseOnUncaught { get; set; }
public bool PauseOnCaught { get; set; }
public PauseOnExceptionsKind PauseOnExceptions { get; set; }

public List<Frame> CallStack { get; set; }

Expand Down
57 changes: 30 additions & 27 deletions src/mono/wasm/debugger/BrowserDebugProxy/MonoProxy.cs
Original file line number Diff line number Diff line change
Expand Up @@ -134,9 +134,7 @@ protected override async Task<bool> AcceptEvent(SessionId sessionId, string meth
{
string exceptionError = args?["exceptionDetails"]?["exception"]?["value"]?.Value<string>();
if (exceptionError == sPauseOnUncaught || exceptionError == sPauseOnCaught)
{
return true;
}
}
break;
}
Expand All @@ -156,17 +154,19 @@ protected override async Task<bool> AcceptEvent(SessionId sessionId, string meth
if (exceptionError == sPauseOnUncaught)
{
await SendCommand(sessionId, "Debugger.resume", new JObject(), token);
context.PauseOnUncaught = true;
if (context.PauseOnExceptions == PauseOnExceptionsKind.Unset)
context.PauseOnExceptions = PauseOnExceptionsKind.Uncaught;
return true;
}
if (exceptionError == sPauseOnCaught)
{
await SendCommand(sessionId, "Debugger.resume", new JObject(), token);
context.PauseOnCaught = true;
context.PauseOnExceptions = PauseOnExceptionsKind.All;
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 @@ -442,23 +442,16 @@ protected override async Task<bool> AcceptCommand(MessageId id, string method, J
case "Debugger.setPauseOnExceptions":
{
string state = args["state"].Value<string>();
if (!context.IsRuntimeReady)
context.PauseOnExceptions = state switch
{
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);
"all" => PauseOnExceptionsKind.All,
"uncaught" => PauseOnExceptionsKind.Uncaught,
"none" => PauseOnExceptionsKind.None,
_ => PauseOnExceptionsKind.Unset
};

if (context.IsRuntimeReady)
await SdbHelper.EnableExceptions(id, context.PauseOnExceptions, token);
// Pass this on to JS too
return false;
}
Expand Down Expand Up @@ -874,7 +867,7 @@ private async Task<bool> OnReceiveDebuggerAgentEvent(SessionId sessionId, JObjec
int object_id = retDebuggerCmdReader.ReadInt32();
var caught = retDebuggerCmdReader.ReadByte();
var exceptionObject = await SdbHelper.GetObjectValues(sessionId, object_id, GetObjectCommandOptions.WithProperties | GetObjectCommandOptions.OwnProperties, token);
var exceptionObjectMessage = exceptionObject.FirstOrDefault(attr => attr["name"].Value<string>().Equals("message"));
var exceptionObjectMessage = exceptionObject.FirstOrDefault(attr => attr["name"].Value<string>().Equals("_message"));
var data = JObject.FromObject(new
{
type = "object",
Expand Down Expand Up @@ -964,6 +957,7 @@ private async Task OnDefaultContext(SessionId sessionId, ExecutionContext contex
{
context.BreakpointRequests[kvp.Key] = kvp.Value.Clone();
}
context.PauseOnExceptions = previousContext.PauseOnExceptions;
}

if (await IsRuntimeAlreadyReadyAlready(sessionId, token))
Expand Down Expand Up @@ -1227,10 +1221,8 @@ 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);
if (context.PauseOnExceptions != PauseOnExceptionsKind.None && context.PauseOnExceptions != PauseOnExceptionsKind.Unset)
await SdbHelper.EnableExceptions(sessionId, context.PauseOnExceptions, token);

await SdbHelper.SetProtocolVersion(sessionId, token);
await SdbHelper.EnableReceiveRequests(sessionId, EventKind.UserBreak, token);
Expand Down Expand Up @@ -1392,8 +1384,19 @@ 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 {{}}";
string checkUncaughtExceptions = string.Empty;
string checkCaughtExceptions = string.Empty;

//we only need this check if it's a non-vs debugging
if (sessionId == SessionId.Null)
{
if (!contexts.TryGetValue(sessionId, out ExecutionContext context) || context.PauseOnExceptions == PauseOnExceptionsKind.Unset)
{
checkUncaughtExceptions = $"throw \"{sPauseOnUncaught}\";";
checkCaughtExceptions = $"try {{throw \"{sPauseOnCaught}\";}} catch {{}}";
}
}

await SendMonoCommand(sessionId, new MonoCommands("globalThis.dotnetDebugger = true"), token);
Result res = await SendCommand(sessionId,
"Page.addScriptToEvaluateOnNewDocument",
Expand Down
14 changes: 11 additions & 3 deletions src/mono/wasm/debugger/BrowserDebugProxy/MonoSDBHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2210,23 +2210,31 @@ public async Task<JArray> GetArrayValues(SessionId sessionId, int arrayId, Cance
}
return array;
}
public async Task<bool> EnableExceptions(SessionId sessionId, string state, CancellationToken token)
public async Task<bool> EnableExceptions(SessionId sessionId, PauseOnExceptionsKind state, CancellationToken token)
{
if (state == PauseOnExceptionsKind.Unset)
{
logger.LogDebug($"Trying to setPauseOnExceptions using status Unset");
return false;
}

var commandParams = new MemoryStream();
var commandParamsWriter = new MonoBinaryWriter(commandParams);
commandParamsWriter.Write((byte)EventKind.Exception);
commandParamsWriter.Write((byte)SuspendPolicy.None);
commandParamsWriter.Write((byte)1);
commandParamsWriter.Write((byte)ModifierKind.ExceptionOnly);
commandParamsWriter.Write(0); //exc_class
if (state == "all")
if (state == PauseOnExceptionsKind.All)
commandParamsWriter.Write((byte)1); //caught
else
commandParamsWriter.Write((byte)0); //caught
if (state == "uncaught" || state == "all")

if (state == PauseOnExceptionsKind.Uncaught || state == PauseOnExceptionsKind.All)
commandParamsWriter.Write((byte)1); //uncaught
else
commandParamsWriter.Write((byte)0); //uncaught

commandParamsWriter.Write((byte)1);//subclasses
commandParamsWriter.Write((byte)0);//not_filtered_feature
commandParamsWriter.Write((byte)0);//everything_else
Expand Down
25 changes: 14 additions & 11 deletions src/mono/wasm/debugger/DebuggerTestSuite/ExceptionTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -232,10 +232,11 @@ await CheckValue(pause_location["data"], JObject.FromObject(new
CheckString(exception_members, "message", "not implemented uncaught");
}

[Fact]
public async Task ExceptionTestAllWithReload()
[Theory]
[InlineData("[debugger-test] DebuggerTests.ExceptionTestsClassDefault:TestExceptions", "System.Exception", 76)]
[InlineData("[debugger-test] DebuggerTests.ExceptionTestsClass:TestExceptions", "DebuggerTests.CustomException", 28)]
public async Task ExceptionTestAllWithReload(string entry_method_name, string class_name, int line_number)
{
string entry_method_name = "[debugger-test] DebuggerTests.ExceptionTestsClass:TestExceptions";
var debugger_test_loc = "dotnet://debugger-test.dll/debugger-exception-test.cs";

await SetPauseOnException("all");
Expand All @@ -255,7 +256,7 @@ public async Task ExceptionTestAllWithReload()
i++;
}


var eval_expr = "window.setTimeout(function() { invoke_static_method (" +
$"'{entry_method_name}'" +
"); }, 1);";
Expand All @@ -270,29 +271,31 @@ await CheckValue(pause_location["data"], JObject.FromObject(new
{
type = "object",
subtype = "error",
className = "DebuggerTests.CustomException",
uncaught = false
className = class_name,
uncaught = false,
description = "not implemented caught"
}), "exception0.data");

var exception_members = await GetProperties(pause_location["data"]["objectId"]?.Value<string>());
CheckString(exception_members, "message", "not implemented caught");
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"]);
CheckLocation(debugger_test_loc, line_number, 16, scripts, pause_location["callFrames"][0]["location"]);

await CheckValue(pause_location["data"], JObject.FromObject(new
{
type = "object",
subtype = "error",
className = "DebuggerTests.CustomException",
uncaught = true
className = class_name,
uncaught = true,
description = "not implemented uncaught"
}), "exception1.data");

exception_members = await GetProperties(pause_location["data"]["objectId"]?.Value<string>());
CheckString(exception_members, "message", "not implemented uncaught");
CheckString(exception_members, "_message", "not implemented uncaught");
}


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,4 +52,40 @@ public CustomException(string message)
this.message = message;
}
}

public class ExceptionTestsClassDefault
{
public class TestCaughtException
{
public void run()
{
try
{
throw new Exception("not implemented caught");
}
catch
{
Console.WriteLine("caught exception");
}
}
}

public class TestUncaughtException
{
public void run()
{
throw new Exception("not implemented uncaught");
}
}

public static void TestExceptions()
{
TestCaughtException f = new TestCaughtException();
f.run();

TestUncaughtException g = new TestUncaughtException();
g.run();
}

}
}