Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/dotnet/runtime.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAnkit Jain <radical@gmail.com>2022-02-24 04:08:23 +0300
committerGitHub <noreply@github.com>2022-02-24 04:08:23 +0300
commit67d8b96d6d04d8aae231425bbf2a192966c4a3f8 (patch)
tree67459cf7f57c1c4c3b26fe7aa6b3f6748a3191d6
parentef231a14052d25f54e0da6c866ec828418310c87 (diff)
[wasm] Misc Debugger improvements (including tests) (#65752)
* [wasm][debugger] Fail test if an assertion is detected * [wasm][debugger] Make DevToolsProxy's `pending_ops` thread-safe Currently, `pending_ops` can get written by different threads at the same time, and also read in parallel. This causes tests to randomly fail with: ``` DevToolsProxy::Run: Exception System.ArgumentException: The tasks argument included a null value. (Parameter 'tasks') at System.Threading.Tasks.Task.WhenAny(Task[] tasks) at Microsoft.WebAssembly.Diagnostics.DevToolsProxy.Run(Uri browserUri, WebSocket ideSocket) in /_/src/mono/wasm/debugger/BrowserDebugProxy/DevToolsProxy.cs:line 269 ``` Instead, we use `Channel<T>` to add the ops, and then read those in the main loop, and add to the *local* `pending_ops` list. * [wasm] Install chrome for debugger tests - controlled by `$(InstallChromeForDebuggerTests)` which defaults to `true` for non-CI docker containers - Useful for using the correct chrome version when testing locally, or on codespaces - Also, add support for detecting, and defaulting to such an installation * [wasm][debugger] Disable tests failing with runtime assertions `DebuggerTests.EvaluateOnCallFrameTests.EvaluateSimpleMethodCallsError`: https://github.com/dotnet/runtime/issues/65744 `DebuggerTests.ArrayTests.InvalidArrayId`: https://github.com/dotnet/runtime/issues/65742 * [wasm][debugger] Fix NRE with `"null"` condition for a breakpoint `ConditionalBreakpoint` test fails. The test with condition=`"null"` failed with a NRE, instead of correctly handling it as a condition that returns null. ``` Unable evaluate conditional breakpoint: System.Exception: Internal Error: Unable to run (null ), error: Object reference not set to an instance of an object.. ---> System.NullReferenceException: Object reference not set to an instance of an object. at Microsoft.WebAssembly.Diagnostics.EvaluateExpression.CompileAndRunTheExpression(String expression, MemberReferenceResolver resolver, CancellationToken token) in /workspaces/runtime/src/mono/wasm/debugger/BrowserDebugProxy/EvaluateExpression.cs:line 399 --- End of inner exception stack trace --- at Microsoft.WebAssembly.Diagnostics.EvaluateExpression.CompileAndRunTheExpression(String expression, MemberReferenceResolver resolver, CancellationToken token) in /workspaces/runtime/src/mono/wasm/debugger/BrowserDebugProxy/EvaluateExpression.cs:line 407 at Microsoft.WebAssembly.Diagnostics.MonoProxy.EvaluateCondition(SessionId sessionId, ExecutionContext context, Frame mono_frame, Breakpoint bp, CancellationToken token) in /workspaces/runtime/src/mono/wasm/debugger/BrowserDebugProxy/MonoProxy.cs:line 801 condition:null ``` * [wasm] Improve default message for ReturnAsErrorException, .. since we seem to be not catching them as intended in many places. * [wasm] Better log, and surface errors in evaluation conditional .. breakpoints. Catch the proper exception `ReturnAsErrorException`, so we can get the actual error message. And log that as an error for the user too. * [wasm][debugger] Allow setting proxy port for BrowserDebugHost .. with `--proxy-port=<port>`. Fixes https://github.com/dotnet/runtime/issues/65209 . * [wasm][debugger] Handle errors in getting value for locals Essentially, catch, and skip. * message cleanup * remove trailing space
-rw-r--r--src/libraries/sendtohelix-wasm.targets30
-rw-r--r--src/mono/wasm/BrowsersForTesting.props28
-rw-r--r--src/mono/wasm/README.md2
-rw-r--r--src/mono/wasm/debugger/BrowserDebugProxy/DevToolsProxy.cs76
-rw-r--r--src/mono/wasm/debugger/BrowserDebugProxy/EvaluateExpression.cs9
-rw-r--r--src/mono/wasm/debugger/BrowserDebugProxy/MonoProxy.cs30
-rw-r--r--src/mono/wasm/debugger/BrowserDebugProxy/MonoSDBHelper.cs12
-rw-r--r--src/mono/wasm/debugger/DebuggerTestSuite/ArrayTests.cs2
-rw-r--r--src/mono/wasm/debugger/DebuggerTestSuite/DebuggerTestBase.cs14
-rw-r--r--src/mono/wasm/debugger/DebuggerTestSuite/DebuggerTestSuite.csproj39
-rw-r--r--src/mono/wasm/debugger/DebuggerTestSuite/DevToolsClient.cs15
-rw-r--r--src/mono/wasm/debugger/DebuggerTestSuite/EvaluateOnCallFrameTests.cs2
-rw-r--r--src/mono/wasm/debugger/DebuggerTestSuite/Inspector.cs12
13 files changed, 207 insertions, 64 deletions
diff --git a/src/libraries/sendtohelix-wasm.targets b/src/libraries/sendtohelix-wasm.targets
index ee6267244d3..0c80ec4ec1f 100644
--- a/src/libraries/sendtohelix-wasm.targets
+++ b/src/libraries/sendtohelix-wasm.targets
@@ -38,8 +38,8 @@
<HelixPreCommand Include="export XHARNESS_DISABLE_COLORED_OUTPUT=true" />
<HelixPreCommand Include="export XHARNESS_LOG_WITH_TIMESTAMPS=true" />
- <HelixPreCommand Condition="'$(NeedsToRunOnBrowser)' == 'true'" Include="export PATH=$HELIX_CORRELATION_PAYLOAD/chromedriver_linux64:$PATH" />
- <HelixPreCommand Condition="'$(NeedsToRunOnBrowser)' == 'true'" Include="export PATH=$HELIX_CORRELATION_PAYLOAD/chrome-linux:$PATH" />
+ <HelixPreCommand Condition="'$(NeedsToRunOnBrowser)' == 'true'" Include="export PATH=$HELIX_CORRELATION_PAYLOAD/$(ChromeDriverDirName):$PATH" />
+ <HelixPreCommand Condition="'$(NeedsToRunOnBrowser)' == 'true'" Include="export PATH=$HELIX_CORRELATION_PAYLOAD/$(ChromiumDirName):$PATH" />
<!-- Fix symbolic links that are broken already on build machine and also in the correlation payload -->
<HelixPreCommand Condition="'$(NeedsEMSDKNode)' == 'true'" Include="export HELIX_NODEJS_VERSION=%24(ls $HELIX_CORRELATION_PAYLOAD/build/emsdk/node)" />
@@ -60,8 +60,8 @@
<HelixPreCommand Include="set XHARNESS_DISABLE_COLORED_OUTPUT=true" />
<HelixPreCommand Include="set XHARNESS_LOG_WITH_TIMESTAMPS=true" />
- <HelixPreCommand Condition="'$(NeedsToRunOnBrowser)' == 'true'" Include="set PATH=%HELIX_CORRELATION_PAYLOAD%\chromedriver_win32%3B%PATH%" />
- <HelixPreCommand Condition="'$(NeedsToRunOnBrowser)' == 'true'" Include="set PATH=%HELIX_CORRELATION_PAYLOAD%\chrome-win%3B%PATH%" />
+ <HelixPreCommand Condition="'$(NeedsToRunOnBrowser)' == 'true'" Include="set PATH=%HELIX_CORRELATION_PAYLOAD%\$(ChromeDriverDirName)%3B%PATH%" />
+ <HelixPreCommand Condition="'$(NeedsToRunOnBrowser)' == 'true'" Include="set PATH=%HELIX_CORRELATION_PAYLOAD%\$(ChromiumDirName)%3B%PATH%" />
<HelixPreCommand Condition="'$(NeedsEMSDKNode)' == 'true'" Include="for /f %%i in ('dir %HELIX_CORRELATION_PAYLOAD%\build\emsdk\node /b') do set HELIX_NODEJS_VERSION=%%i" />
<HelixPreCommand Condition="'$(NeedsEMSDKNode)' == 'true'" Include="set PATH=%HELIX_CORRELATION_PAYLOAD%\build\emsdk\node\%HELIX_NODEJS_VERSION%\bin%3B%PATH%" />
@@ -96,6 +96,8 @@
</When>
</Choose>
+ <Import Project="$(RepoRoot)src\mono\wasm\BrowsersForTesting.props" />
+
<Target Name="PrepareForBuildHelixWorkItems_Wasm">
<PropertyGroup>
<WasmBuildTargetsDir>$([MSBuild]::NormalizeDirectory('$(RepoRoot)', 'src', 'mono', 'wasm', 'build'))</WasmBuildTargetsDir>
@@ -106,26 +108,6 @@
<WorkItemPrefix Condition="'$(WorkItemPrefix)' == '' and '$(Scenario)' != ''">$(Scenario)-</WorkItemPrefix>
</PropertyGroup>
- <!-- Version number to revision number mapping from http://omahaproxy.appspot.com/ and find the closest one in
- https://commondatastorage.googleapis.com/chromium-browser-snapshots/index.html?prefix=Linux_x64/
- and https://commondatastorage.googleapis.com/chromium-browser-snapshots/index.html?prefix=Win_x64/
-
- Eg. latest stable version is 96.0.4664.45 with revision 929512.
- but the closest one available in the snapshosts is 929513.
- Please make sure to check both platforms as sometime
- the same snapshot might not be available in one of them.
- -->
- <PropertyGroup Condition="'$(BrowserHost)' != 'windows'">
- <ChromiumRevision>929513</ChromiumRevision>
- <ChromiumUrl>https://storage.googleapis.com/chromium-browser-snapshots/Linux_x64/$(ChromiumRevision)/chrome-linux.zip</ChromiumUrl>
- <ChromeDriverUrl>https://storage.googleapis.com/chromium-browser-snapshots/Linux_x64/$(ChromiumRevision)/chromedriver_linux64.zip</ChromeDriverUrl>
- </PropertyGroup>
- <PropertyGroup Condition="'$(BrowserHost)' == 'windows'">
- <ChromiumRevision>929513</ChromiumRevision>
- <ChromiumUrl>https://storage.googleapis.com/chromium-browser-snapshots/Win_x64/$(ChromiumRevision)/chrome-win.zip</ChromiumUrl>
- <ChromeDriverUrl>https://storage.googleapis.com/chromium-browser-snapshots/Win_x64/$(ChromiumRevision)/chromedriver_win32.zip</ChromeDriverUrl>
- </PropertyGroup>
-
<ItemGroup Condition="'$(NeedsToRunOnBrowser)' == 'true'">
<HelixCorrelationPayload Include="chromium" Uri="$(ChromiumUrl)" />
<HelixCorrelationPayload Include="chromedriver" Uri="$(ChromeDriverUrl)" />
diff --git a/src/mono/wasm/BrowsersForTesting.props b/src/mono/wasm/BrowsersForTesting.props
new file mode 100644
index 00000000000..eb0c1476f00
--- /dev/null
+++ b/src/mono/wasm/BrowsersForTesting.props
@@ -0,0 +1,28 @@
+<Project>
+ <!-- Version number to revision number mapping from http://omahaproxy.appspot.com/ and find the closest one in
+ https://commondatastorage.googleapis.com/chromium-browser-snapshots/index.html?prefix=Linux_x64/
+ and https://commondatastorage.googleapis.com/chromium-browser-snapshots/index.html?prefix=Win_x64/
+
+ Eg. latest stable version is 96.0.4664.45 with revision 929512.
+ but the closest one available in the snapshosts is 929513.
+ Please make sure to check both platforms as sometime
+ the same snapshot might not be available in one of them.
+ -->
+ <PropertyGroup Condition="'$(BrowserHost)' != 'windows'">
+ <ChromiumRevision>929513</ChromiumRevision>
+ <ChromiumUrl>https://storage.googleapis.com/chromium-browser-snapshots/Linux_x64/$(ChromiumRevision)/chrome-linux.zip</ChromiumUrl>
+ <ChromeDriverUrl>https://storage.googleapis.com/chromium-browser-snapshots/Linux_x64/$(ChromiumRevision)/chromedriver_linux64.zip</ChromeDriverUrl>
+ <ChromiumDirName>chrome-linux</ChromiumDirName>
+ <ChromeDriverDirName>chromedriver_linux64</ChromeDriverDirName>
+ <ChromiumBinaryName>chrome</ChromiumBinaryName>
+ </PropertyGroup>
+
+ <PropertyGroup Condition="'$(BrowserHost)' == 'windows'">
+ <ChromiumRevision>929513</ChromiumRevision>
+ <ChromiumUrl>https://storage.googleapis.com/chromium-browser-snapshots/Win_x64/$(ChromiumRevision)/chrome-win.zip</ChromiumUrl>
+ <ChromeDriverUrl>https://storage.googleapis.com/chromium-browser-snapshots/Win_x64/$(ChromiumRevision)/chromedriver_win32.zip</ChromeDriverUrl>
+ <ChromiumDirName>chrome-win</ChromiumDirName>
+ <ChromeDriverDirName>chromedriver_win32</ChromeDriverDirName>
+ <ChromiumBinaryName>chrome.exe</ChromiumBinaryName>
+ </PropertyGroup>
+</Project>
diff --git a/src/mono/wasm/README.md b/src/mono/wasm/README.md
index 86e7b569127..2fd07e682ad 100644
--- a/src/mono/wasm/README.md
+++ b/src/mono/wasm/README.md
@@ -143,6 +143,8 @@ To run a test with `FooBar` in the name:
Additional arguments for `dotnet test` can be passed via `MSBUILD_ARGS` or `TEST_ARGS`. For example `MSBUILD_ARGS="/p:WasmDebugLevel=5"`. Though only one of `TEST_ARGS`, or `TEST_FILTER` can be used at a time.
+- Chrome can be installed for testing by setting `InstallChromeForDebuggerTests=true` when building the tests.
+
## Run samples
The samples in `src/mono/sample/wasm` can be build and run like this:
diff --git a/src/mono/wasm/debugger/BrowserDebugProxy/DevToolsProxy.cs b/src/mono/wasm/debugger/BrowserDebugProxy/DevToolsProxy.cs
index c3f184d056a..567b1d7837e 100644
--- a/src/mono/wasm/debugger/BrowserDebugProxy/DevToolsProxy.cs
+++ b/src/mono/wasm/debugger/BrowserDebugProxy/DevToolsProxy.cs
@@ -8,6 +8,7 @@ using System.Linq;
using System.Net.WebSockets;
using System.Text;
using System.Threading;
+using System.Threading.Channels;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
@@ -24,7 +25,8 @@ namespace Microsoft.WebAssembly.Diagnostics
private ClientWebSocket browser;
private WebSocket ide;
private int next_cmd_id;
- private List<Task> pending_ops = new List<Task>();
+ private readonly ChannelWriter<Task> _channelWriter;
+ private readonly ChannelReader<Task> _channelReader;
private List<DevToolsQueue> queues = new List<DevToolsQueue>();
protected readonly ILogger logger;
@@ -33,6 +35,10 @@ namespace Microsoft.WebAssembly.Diagnostics
{
string loggerSuffix = string.IsNullOrEmpty(loggerId) ? string.Empty : $"-{loggerId}";
logger = loggerFactory.CreateLogger($"{nameof(DevToolsProxy)}{loggerSuffix}");
+
+ var channel = Channel.CreateUnbounded<Task>(new UnboundedChannelOptions { SingleReader = true });
+ _channelWriter = channel.Writer;
+ _channelReader = channel.Reader;
}
protected virtual Task<bool> AcceptEvent(SessionId sessionId, string method, JObject args, CancellationToken token)
@@ -94,7 +100,7 @@ namespace Microsoft.WebAssembly.Diagnostics
return queues.FirstOrDefault(q => q.CurrentSend == task);
}
- private void Send(WebSocket to, JObject o, CancellationToken token)
+ private async Task Send(WebSocket to, JObject o, CancellationToken token)
{
string sender = browser == to ? "Send-browser" : "Send-ide";
@@ -106,7 +112,7 @@ namespace Microsoft.WebAssembly.Diagnostics
Task task = queue.Send(bytes, token);
if (task != null)
- pending_ops.Add(task);
+ await _channelWriter.WriteAsync(task, token);
}
private async Task OnEvent(SessionId sessionId, string method, JObject args, CancellationToken token)
@@ -116,7 +122,7 @@ namespace Microsoft.WebAssembly.Diagnostics
if (!await AcceptEvent(sessionId, method, args, token))
{
//logger.LogDebug ("proxy browser: {0}::{1}",method, args);
- SendEventInternal(sessionId, method, args, token);
+ await SendEventInternal(sessionId, method, args, token);
}
}
catch (Exception e)
@@ -132,7 +138,7 @@ namespace Microsoft.WebAssembly.Diagnostics
if (!await AcceptCommand(id, method, args, token))
{
Result res = await SendCommandInternal(id, method, args, token);
- SendResponseInternal(id, res, token);
+ await SendResponseInternal(id, res, token);
}
}
catch (Exception e)
@@ -153,7 +159,7 @@ namespace Microsoft.WebAssembly.Diagnostics
logger.LogError("Cannot respond to command: {id} with result: {result} - command is not pending", id, result);
}
- private void ProcessBrowserMessage(string msg, CancellationToken token)
+ private Task ProcessBrowserMessage(string msg, CancellationToken token)
{
var res = JObject.Parse(msg);
@@ -161,23 +167,30 @@ namespace Microsoft.WebAssembly.Diagnostics
Log("protocol", $"browser: {msg}");
if (res["id"] == null)
- pending_ops.Add(OnEvent(res.ToObject<SessionId>(), res["method"].Value<string>(), res["params"] as JObject, token));
+ {
+ return OnEvent(res.ToObject<SessionId>(), res["method"].Value<string>(), res["params"] as JObject, token);
+ }
else
+ {
OnResponse(res.ToObject<MessageId>(), Result.FromJson(res));
+ return null;
+ }
}
- private void ProcessIdeMessage(string msg, CancellationToken token)
+ private Task ProcessIdeMessage(string msg, CancellationToken token)
{
Log("protocol", $"ide: {msg}");
if (!string.IsNullOrEmpty(msg))
{
var res = JObject.Parse(msg);
var id = res.ToObject<MessageId>();
- pending_ops.Add(OnCommand(
+ return OnCommand(
id,
res["method"].Value<string>(),
- res["params"] as JObject, token));
+ res["params"] as JObject, token);
}
+
+ return null;
}
internal async Task<Result> SendCommand(SessionId id, string method, JObject args, CancellationToken token)
@@ -186,7 +199,7 @@ namespace Microsoft.WebAssembly.Diagnostics
return await SendCommandInternal(id, method, args, token);
}
- private Task<Result> SendCommandInternal(SessionId sessionId, string method, JObject args, CancellationToken token)
+ private async Task<Result> SendCommandInternal(SessionId sessionId, string method, JObject args, CancellationToken token)
{
int id = Interlocked.Increment(ref next_cmd_id);
@@ -204,17 +217,17 @@ namespace Microsoft.WebAssembly.Diagnostics
//Log ("verbose", $"add cmd id {sessionId}-{id}");
pending_cmds[msgId] = tcs;
- Send(this.browser, o, token);
- return tcs.Task;
+ await Send(browser, o, token);
+ return await tcs.Task;
}
- public void SendEvent(SessionId sessionId, string method, JObject args, CancellationToken token)
+ public Task SendEvent(SessionId sessionId, string method, JObject args, CancellationToken token)
{
//Log ("verbose", $"sending event {method}: {args}");
- SendEventInternal(sessionId, method, args, token);
+ return SendEventInternal(sessionId, method, args, token);
}
- private void SendEventInternal(SessionId sessionId, string method, JObject args, CancellationToken token)
+ private Task SendEventInternal(SessionId sessionId, string method, JObject args, CancellationToken token)
{
var o = JObject.FromObject(new
{
@@ -224,7 +237,7 @@ namespace Microsoft.WebAssembly.Diagnostics
if (sessionId.sessionId != null)
o["sessionId"] = sessionId.sessionId;
- Send(this.ide, o, token);
+ return Send(ide, o, token);
}
internal void SendResponse(MessageId id, Result result, CancellationToken token)
@@ -232,13 +245,13 @@ namespace Microsoft.WebAssembly.Diagnostics
SendResponseInternal(id, result, token);
}
- private void SendResponseInternal(MessageId id, Result result, CancellationToken token)
+ private Task SendResponseInternal(MessageId id, Result result, CancellationToken token)
{
JObject o = result.ToJObject(id);
if (!result.IsOk)
logger.LogError($"sending error response for id: {id} -> {result}");
- Send(this.ide, o, token);
+ return Send(this.ide, o, token);
}
// , HttpContext context)
@@ -258,10 +271,14 @@ namespace Microsoft.WebAssembly.Diagnostics
Log("verbose", $"DevToolsProxy: Client connected on {browserUri}");
var x = new CancellationTokenSource();
+ List<Task> pending_ops = new();
+
pending_ops.Add(ReadOne(browser, x.Token));
pending_ops.Add(ReadOne(ide, x.Token));
pending_ops.Add(side_exception.Task);
pending_ops.Add(client_initiated_close.Task);
+ Task<bool> readerTask = _channelReader.WaitToReadAsync(x.Token).AsTask();
+ pending_ops.Add(readerTask);
try
{
@@ -278,6 +295,16 @@ namespace Microsoft.WebAssembly.Diagnostics
break;
}
+ if (readerTask.IsCompleted)
+ {
+ while (_channelReader.TryRead(out Task newTask))
+ {
+ pending_ops.Add(newTask);
+ }
+
+ pending_ops[4] = _channelReader.WaitToReadAsync(x.Token).AsTask();
+ }
+
//logger.LogTrace ("pump {0} {1}", task, pending_ops.IndexOf (task));
if (completedTask == pending_ops[0])
{
@@ -285,7 +312,9 @@ namespace Microsoft.WebAssembly.Diagnostics
if (msg != null)
{
pending_ops[0] = ReadOne(browser, x.Token); //queue next read
- ProcessBrowserMessage(msg, x.Token);
+ Task newTask = ProcessBrowserMessage(msg, x.Token);
+ if (newTask != null)
+ pending_ops.Add(newTask);
}
}
else if (completedTask == pending_ops[1])
@@ -294,7 +323,9 @@ namespace Microsoft.WebAssembly.Diagnostics
if (msg != null)
{
pending_ops[1] = ReadOne(ide, x.Token); //queue next read
- ProcessIdeMessage(msg, x.Token);
+ Task newTask = ProcessIdeMessage(msg, x.Token);
+ if (newTask != null)
+ pending_ops.Add(newTask);
}
}
else if (completedTask == pending_ops[2])
@@ -314,10 +345,13 @@ namespace Microsoft.WebAssembly.Diagnostics
}
}
}
+
+ _channelWriter.Complete();
}
catch (Exception e)
{
Log("error", $"DevToolsProxy::Run: Exception {e}");
+ _channelWriter.Complete(e);
//throw;
}
finally
diff --git a/src/mono/wasm/debugger/BrowserDebugProxy/EvaluateExpression.cs b/src/mono/wasm/debugger/BrowserDebugProxy/EvaluateExpression.cs
index 213a50ae1ce..dc9296b5f6b 100644
--- a/src/mono/wasm/debugger/BrowserDebugProxy/EvaluateExpression.cs
+++ b/src/mono/wasm/debugger/BrowserDebugProxy/EvaluateExpression.cs
@@ -396,7 +396,7 @@ namespace Microsoft.WebAssembly.Diagnostics
string.Join("\n", findVarNMethodCall.variableDefinitions) + "\nreturn " + syntaxTree.ToString());
var state = await newScript.RunAsync(cancellationToken: token);
- return JObject.FromObject(ConvertCSharpToJSType(state.ReturnValue, state.ReturnValue.GetType()));
+ return JObject.FromObject(ConvertCSharpToJSType(state.ReturnValue, state.ReturnValue?.GetType()));
}
catch (CompilationErrorException cee)
{
@@ -419,7 +419,7 @@ namespace Microsoft.WebAssembly.Diagnostics
private static object ConvertCSharpToJSType(object v, Type type)
{
if (v == null)
- return new { type = "object", subtype = "null", className = type.ToString(), description = type.ToString() };
+ return new { type = "object", subtype = "null", className = type?.ToString(), description = type?.ToString() };
if (v is string s)
return new { type = "string", value = s, description = s };
if (NumericTypes.Contains(v.GetType()))
@@ -443,10 +443,11 @@ namespace Microsoft.WebAssembly.Diagnostics
}
set { }
}
- public ReturnAsErrorException(JObject error)
+ public ReturnAsErrorException(JObject error) : base(error.ToString())
=> Error = Result.Err(error);
public ReturnAsErrorException(string message, string className)
+ : base($"[{className}] {message}")
{
var result = new
{
@@ -466,5 +467,7 @@ namespace Microsoft.WebAssembly.Diagnostics
}
}));
}
+
+ public override string ToString() => $"Error object: {Error}. {base.ToString()}";
}
}
diff --git a/src/mono/wasm/debugger/BrowserDebugProxy/MonoProxy.cs b/src/mono/wasm/debugger/BrowserDebugProxy/MonoProxy.cs
index 565c2bcb841..581f7ef5cf6 100644
--- a/src/mono/wasm/debugger/BrowserDebugProxy/MonoProxy.cs
+++ b/src/mono/wasm/debugger/BrowserDebugProxy/MonoProxy.cs
@@ -51,7 +51,7 @@ namespace Microsoft.WebAssembly.Diagnostics
internal Task<Result> SendMonoCommand(SessionId id, MonoCommands cmd, CancellationToken token) => SendCommand(id, "Runtime.evaluate", JObject.FromObject(cmd), token);
- internal void SendLog(SessionId sessionId, string message, CancellationToken token)
+ internal void SendLog(SessionId sessionId, string message, CancellationToken token, string type = "warning")
{
if (!contexts.TryGetValue(sessionId, out ExecutionContext context))
return;
@@ -68,7 +68,7 @@ namespace Microsoft.WebAssembly.Diagnostics
SendEvent(id, "Log.entryAdded", o, token);*/
var o = JObject.FromObject(new
{
- type = "warning",
+ type,
args = new JArray(JObject.FromObject(new
{
type = "string",
@@ -139,7 +139,7 @@ namespace Microsoft.WebAssembly.Diagnostics
case "Runtime.executionContextCreated":
{
- SendEvent(sessionId, method, args, token);
+ await SendEvent(sessionId, method, args, token);
JToken ctx = args?["context"];
var aux_data = ctx?["auxData"] as JObject;
int id = ctx["id"].Value<int>();
@@ -805,14 +805,22 @@ namespace Microsoft.WebAssembly.Diagnostics
if (retValue?["value"]?.Value<bool>() == true)
return true;
}
- else if (retValue?["value"]?.Type != JTokenType.Null)
+ else if (retValue?["value"] != null && // null object, missing value
+ retValue?["value"]?.Type != JTokenType.Null)
+ {
return true;
+ }
+ }
+ catch (ReturnAsErrorException raee)
+ {
+ logger.LogDebug($"Unable to evaluate breakpoint condition '{condition}': {raee}");
+ SendLog(sessionId, $"Unable to evaluate breakpoint condition '{condition}': {raee.Message}", token, type: "error");
+ bp.ConditionAlreadyEvaluatedWithError = true;
}
catch (Exception e)
{
- Log("info", $"Unable evaluate conditional breakpoint: {e} condition:{condition}");
+ Log("info", $"Unable to evaluate breakpoint condition '{condition}': {e}");
bp.ConditionAlreadyEvaluatedWithError = true;
- return false;
}
return false;
}
@@ -986,7 +994,7 @@ namespace Microsoft.WebAssembly.Diagnostics
await SendCommand(sessionId, "Debugger.resume", new JObject(), token);
return true;
}
- SendEvent(sessionId, "Debugger.paused", o, token);
+ await SendEvent(sessionId, "Debugger.paused", o, token);
return true;
@@ -1103,7 +1111,7 @@ namespace Microsoft.WebAssembly.Diagnostics
foreach (SourceFile source in asm.Sources)
{
var scriptSource = JObject.FromObject(source.ToScriptSource(context.Id, context.AuxData));
- SendEvent(sessionId, "Debugger.scriptParsed", scriptSource, token);
+ await SendEvent(sessionId, "Debugger.scriptParsed", scriptSource, token);
}
return asm.GetMethodByToken(method_token);
}
@@ -1333,7 +1341,7 @@ namespace Microsoft.WebAssembly.Diagnostics
{
JObject scriptSource = JObject.FromObject(source.ToScriptSource(context.Id, context.AuxData));
Log("debug", $"sending {source.Url} {context.Id} {sessionId.sessionId}");
- SendEvent(sessionId, "Debugger.scriptParsed", scriptSource, token);
+ await SendEvent(sessionId, "Debugger.scriptParsed", scriptSource, token);
foreach (var req in context.BreakpointRequests.Values)
{
@@ -1412,7 +1420,7 @@ namespace Microsoft.WebAssembly.Diagnostics
DebugStore store = await LoadStore(sessionId, token);
context.ready.SetResult(store);
- SendEvent(sessionId, "Mono.runtimeReady", new JObject(), token);
+ await SendEvent(sessionId, "Mono.runtimeReady", new JObject(), token);
context.SdbAgent.ResetStore(store);
return store;
}
@@ -1502,7 +1510,7 @@ namespace Microsoft.WebAssembly.Diagnostics
};
if (sendResolvedEvent)
- SendEvent(sessionId, "Debugger.breakpointResolved", JObject.FromObject(resolvedLocation), token);
+ await SendEvent(sessionId, "Debugger.breakpointResolved", JObject.FromObject(resolvedLocation), token);
}
req.Locations.AddRange(breakpoints);
diff --git a/src/mono/wasm/debugger/BrowserDebugProxy/MonoSDBHelper.cs b/src/mono/wasm/debugger/BrowserDebugProxy/MonoSDBHelper.cs
index 793d3c980b9..f30d42e69de 100644
--- a/src/mono/wasm/debugger/BrowserDebugProxy/MonoSDBHelper.cs
+++ b/src/mono/wasm/debugger/BrowserDebugProxy/MonoSDBHelper.cs
@@ -2121,8 +2121,16 @@ namespace Microsoft.WebAssembly.Diagnostics
using var localsDebuggerCmdReader = await SendDebuggerAgentCommand(CmdFrame.GetValues, commandParamsWriter, token);
foreach (var var in varIds)
{
- var var_json = await CreateJObjectForVariableValue(localsDebuggerCmdReader, var.Name, false, -1, false, token);
- locals.Add(var_json);
+ try
+ {
+ var var_json = await CreateJObjectForVariableValue(localsDebuggerCmdReader, var.Name, false, -1, false, token);
+ locals.Add(var_json);
+ }
+ catch (Exception ex)
+ {
+ logger.LogDebug($"Failed to create value for local var {var}: {ex}");
+ continue;
+ }
}
if (!method.Info.IsStatic())
{
diff --git a/src/mono/wasm/debugger/DebuggerTestSuite/ArrayTests.cs b/src/mono/wasm/debugger/DebuggerTestSuite/ArrayTests.cs
index 438eceea378..a9e87746bd8 100644
--- a/src/mono/wasm/debugger/DebuggerTestSuite/ArrayTests.cs
+++ b/src/mono/wasm/debugger/DebuggerTestSuite/ArrayTests.cs
@@ -552,6 +552,8 @@ namespace DebuggerTests
}
[Fact]
+ [Trait("Category", "windows-failing")] // https://github.com/dotnet/runtime/issues/65742
+ [Trait("Category", "linux-failing")] // https://github.com/dotnet/runtime/issues/65742
public async Task InvalidArrayId() => await CheckInspectLocalsAtBreakpointSite(
"DebuggerTests.Container", "PlaceholderMethod", 1, "PlaceholderMethod",
"window.setTimeout(function() { invoke_static_method ('[debugger-test] DebuggerTests.ArrayTestsClass:ObjectArrayMembers'); }, 1);",
diff --git a/src/mono/wasm/debugger/DebuggerTestSuite/DebuggerTestBase.cs b/src/mono/wasm/debugger/DebuggerTestSuite/DebuggerTestBase.cs
index d39c2c2c10b..aa61555ab22 100644
--- a/src/mono/wasm/debugger/DebuggerTestSuite/DebuggerTestBase.cs
+++ b/src/mono/wasm/debugger/DebuggerTestSuite/DebuggerTestBase.cs
@@ -90,14 +90,17 @@ namespace DebuggerTests
{
chrome_path = FindChromePath();
if (!string.IsNullOrEmpty(chrome_path))
+ {
+ chrome_path = Path.GetFullPath(chrome_path);
Console.WriteLine ($"** Using chrome from {chrome_path}");
+ }
else
throw new Exception("Could not find an installed Chrome to use");
}
return chrome_path;
- string FindChromePath()
+ static string FindChromePath()
{
string chrome_path_env_var = Environment.GetEnvironmentVariable("CHROME_PATH_FOR_DEBUGGER_TESTS");
if (!string.IsNullOrEmpty(chrome_path_env_var))
@@ -108,6 +111,15 @@ namespace DebuggerTests
Console.WriteLine ($"warning: Could not find CHROME_PATH_FOR_DEBUGGER_TESTS={chrome_path_env_var}");
}
+ // Look for a chrome installed in artifacts, for local runs
+ string baseDir = Path.Combine(Path.GetDirectoryName(typeof(DebuggerTestBase).Assembly.Location), "..", "..");
+ string path = Path.Combine(baseDir, "chrome", "chrome-linux", "chrome");
+ if (File.Exists(path))
+ return path;
+ path = Path.Combine(baseDir, "chrome", "chrome-win", "chrome.exe");
+ if (File.Exists(path))
+ return path;
+
return PROBE_LIST.FirstOrDefault(p => File.Exists(p));
}
}
diff --git a/src/mono/wasm/debugger/DebuggerTestSuite/DebuggerTestSuite.csproj b/src/mono/wasm/debugger/DebuggerTestSuite/DebuggerTestSuite.csproj
index 602f06cfc95..8f981735595 100644
--- a/src/mono/wasm/debugger/DebuggerTestSuite/DebuggerTestSuite.csproj
+++ b/src/mono/wasm/debugger/DebuggerTestSuite/DebuggerTestSuite.csproj
@@ -5,6 +5,17 @@
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<RunAnalyzers>false</RunAnalyzers>
<IsTestProject>true</IsTestProject>
+ <VersionsPropsFile>$(MSBuildThisFileDirectory)..\..\BrowsersForTesting.props</VersionsPropsFile>
+ <BrowserHost Condition="$([MSBuild]::IsOSPlatform('windows'))">windows</BrowserHost>
+ <InstallChromeForDebuggerTests Condition="'$(InstallChromeForDebuggerTests)' == '' and '$(ContinuousIntegrationBuild)' != 'true' and Exists('/.dockerenv')">true</InstallChromeForDebuggerTests>
+ </PropertyGroup>
+
+ <Import Project="$(VersionsPropsFile)" />
+
+ <PropertyGroup>
+ <ChromeDir>$(ArtifactsBinDir)DebuggerTestSuite\chrome\</ChromeDir>
+ <ChromeStampDir>$(ArtifactsBinDir)DebuggerTestSuite\</ChromeStampDir>
+ <ChromeStampFile>$(ChromeStampDir).install-chrome-$(ChromiumRevision).stamp</ChromeStampFile>
</PropertyGroup>
<ItemGroup>
@@ -34,4 +45,32 @@
<Copy SourceFiles="@(_FilesToCopy)" DestinationFiles="$(ArchiveDirForHelix)\%(TargetPath)\%(RecursiveDir)%(FileName)%(Extension)" />
</Target>
+
+ <Target Name="DownloadAndInstallChrome"
+ AfterTargets="Build"
+ Condition="!Exists($(ChromeStampFile)) and '$(InstallChromeForDebuggerTests)' == 'true'">
+
+ <ItemGroup>
+ <_StampFile Include="$(ChromeStampDir).install-chrome*.stamp" />
+ </ItemGroup>
+
+ <Delete Files="@(_StampFile)" />
+ <RemoveDir Directories="$(ChromeDir)" />
+
+ <DownloadFile SourceUrl="$(ChromiumUrl)" DestinationFolder="$(ChromeDir)" SkipUnchangedFiles="true">
+ <Output TaskParameter="DownloadedFile" PropertyName="_DownloadedFile" />
+ </DownloadFile>
+ <Unzip SourceFiles="$(_DownloadedFile)" DestinationFolder="$(ChromeDir)" />
+
+ <PropertyGroup>
+ <_ChromeBinaryPath>$([MSBuild]::NormalizePath($(ChromeDir), $(ChromiumDirName), $(ChromiumBinaryName)))</_ChromeBinaryPath>
+ </PropertyGroup>
+
+ <Error Text="Cannot find chrome at $(_ChromeBinaryPath) in the downloaded copy"
+ Condition="!Exists($(_ChromeBinaryPath))" />
+
+ <Exec Command="chmod +x $(_ChromeBinaryPath)" Condition="!$([MSBuild]::IsOSPlatform('windows'))" />
+
+ <Touch Files="$(ChromeStampFile)" AlwaysCreate="true" />
+ </Target>
</Project>
diff --git a/src/mono/wasm/debugger/DebuggerTestSuite/DevToolsClient.cs b/src/mono/wasm/debugger/DebuggerTestSuite/DevToolsClient.cs
index 7c5874df227..6eed6e177f9 100644
--- a/src/mono/wasm/debugger/DebuggerTestSuite/DevToolsClient.cs
+++ b/src/mono/wasm/debugger/DebuggerTestSuite/DevToolsClient.cs
@@ -18,6 +18,7 @@ namespace Microsoft.WebAssembly.Diagnostics
ClientWebSocket socket;
TaskCompletionSource _clientInitiatedClose = new TaskCompletionSource();
TaskCompletionSource _shutdownRequested = new TaskCompletionSource();
+ readonly TaskCompletionSource<Exception> _failRequested = new();
TaskCompletionSource _newSendTaskAvailable = new ();
protected readonly ILogger logger;
@@ -65,6 +66,14 @@ namespace Microsoft.WebAssembly.Diagnostics
}
}
+ public void Fail(Exception exception)
+ {
+ if (_failRequested.Task.IsCompleted)
+ logger.LogError($"Fail requested again with {exception}");
+ else
+ _failRequested.TrySetResult(exception);
+ }
+
async Task<string> ReadOne(CancellationToken token)
{
byte[] buff = new byte[4000];
@@ -172,7 +181,8 @@ namespace Microsoft.WebAssembly.Diagnostics
ReadOne(linkedCts.Token),
_newSendTaskAvailable.Task,
_clientInitiatedClose.Task,
- _shutdownRequested.Task
+ _shutdownRequested.Task,
+ _failRequested.Task
};
// In case we had a Send called already
@@ -192,6 +202,9 @@ namespace Microsoft.WebAssembly.Diagnostics
if (_clientInitiatedClose.Task.IsCompleted)
return (RunLoopStopReason.ClientInitiatedClose, new TaskCanceledException("Proxy or the browser closed the connection"));
+ if (_failRequested.Task.IsCompleted)
+ return (RunLoopStopReason.Exception, _failRequested.Task.Result);
+
if (_newSendTaskAvailable.Task.IsCompleted)
{
// Just needed to wake up. the new task has already
diff --git a/src/mono/wasm/debugger/DebuggerTestSuite/EvaluateOnCallFrameTests.cs b/src/mono/wasm/debugger/DebuggerTestSuite/EvaluateOnCallFrameTests.cs
index c38596af1e5..946dcbd394b 100644
--- a/src/mono/wasm/debugger/DebuggerTestSuite/EvaluateOnCallFrameTests.cs
+++ b/src/mono/wasm/debugger/DebuggerTestSuite/EvaluateOnCallFrameTests.cs
@@ -494,6 +494,8 @@ namespace DebuggerTests
[Fact]
+ [Trait("Category", "windows-failing")] // https://github.com/dotnet/runtime/issues/65744
+ [Trait("Category", "linux-failing")] // https://github.com/dotnet/runtime/issues/65744
public async Task EvaluateSimpleMethodCallsError() => await CheckInspectLocalsAtBreakpointSite(
"DebuggerTests.EvaluateMethodTestsClass.TestEvaluate", "run", 9, "run",
"window.setTimeout(function() { invoke_static_method ('[debugger-test] DebuggerTests.EvaluateMethodTestsClass:EvaluateMethods'); })",
diff --git a/src/mono/wasm/debugger/DebuggerTestSuite/Inspector.cs b/src/mono/wasm/debugger/DebuggerTestSuite/Inspector.cs
index 3a74ed70e78..cde3ef31145 100644
--- a/src/mono/wasm/debugger/DebuggerTestSuite/Inspector.cs
+++ b/src/mono/wasm/debugger/DebuggerTestSuite/Inspector.cs
@@ -29,6 +29,7 @@ namespace DebuggerTests
public const string READY = "ready";
public CancellationToken Token { get; }
public InspectorClient Client { get; }
+ public bool DetectAndFailOnAssertions { get; set; } = true;
private CancellationTokenSource _cancellationTokenSource;
@@ -184,8 +185,17 @@ namespace DebuggerTests
NotifyOf(READY, args);
break;
case "Runtime.consoleAPICalled":
- _logger.LogInformation(FormatConsoleAPICalled(args));
+ {
+ string line = FormatConsoleAPICalled(args);
+ _logger.LogInformation(line);
+ if (DetectAndFailOnAssertions && line.Contains("console.error: * Assertion at"))
+ {
+ args["__forMethod"] = method;
+ Client.Fail(new ArgumentException($"Assertion detected in the messages: {line}{Environment.NewLine}{args}"));
+ return;
+ }
break;
+ }
case "Inspector.detached":
case "Inspector.targetCrashed":
case "Inspector.targetReloadedAfterCrash":