Skip to content

Commit c26475c

Browse files
authored
[wasm] Misc debugger improvements (#68988)
* [wasm] Move the browser provisioning stuff to a new targets file .. from `DebuggerTestSuite.csproj`. This will allow other projects to use this too. * [wasm][debugger] Handle failure to connect to the browser * [wasm] Improve the browser path lookup so it can be used outside the debugger tests project too. For example, with Wasm.Build.Tests+playwright . * [wasm][debugger] Throw exceptions from tasks correctly .. using `ExceptionDispatchInfo.Capture` so we get the original stack trace. * [wasm][debugger] General improvements in debug proxy - like logging - updated API to make it easier to use by other projects, like the upcoming wasm-app-host . * [wasm][debugger] Add support for setting an automatic breakpoint .. on the first line of the entrypoint method (`Main`). This will be useful for debugging with the upcoming wasm-app-host, along with the use of `wait-for-debugger`. Implemented by @thaystg . * [wasm][debugger] Add support for wait_for_debugger If requested, then it will cause invocation of `main` to be delayed till a debugger is attached. Implemented by @thaystg * [wasm] Cleanup in Wasm.Build.Tests .. in the lead up to wasm-app-host tests. * [wasm] Update the default paths used to trigger builds on CI .. to include templates, and provisioning props. * disable non-wasm builds * [wasm][debugger] Fix path to artifacts dir * [wasm] Emit message with bundle path * [wasm][templates] Make the project use the target dir's name as the .. project name, instead of always creating `browser.dll`, and `console.dll`. * [wasm][debugger] Use a single static instance of HttpClient .. as recommended by the documentation. * Revert "disable non-wasm builds" This reverts commit 7b8b60d. * Address review feedback, and improve the autogenerated bpid
1 parent 4751154 commit c26475c

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

45 files changed

+566
-302
lines changed

eng/pipelines/common/evaluate-default-paths.yml

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,7 @@ jobs:
109109
- src/tests/BuildWasmApps/*
110110
- src/mono/wasm/build/*
111111
- src/mono/wasm/runtime/*
112+
- src/mono/wasm/templates/*
112113
- src/mono/nuget/Microsoft.NET.Workload.Mono.Toolchain.Manifest/*
113114
- src/mono/nuget/Microsoft.NET.Runtime.WebAssembly.Sdk/*
114115
- src/mono/nuget/Microsoft.NET.Runtime.wasm.Sample.Mono/*
@@ -125,7 +126,9 @@ jobs:
125126
include:
126127
- src/mono/wasm/debugger/*
127128
- src/mono/wasm/runtime/*
128-
- src/mono/wasm/BrowsersForTesting.props
129+
- src/tests/BuildWasmApps/*
130+
- eng/testing/ProvisioningVersions.props
131+
- eng/testing/scenarios/WasmDebuggerTestsJobsList.txt
129132
- src/mono/mono/*
130133
- subset: allwasm
131134
include:
File renamed without changes.

eng/testing/provisioning.targets

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
<Project>
2+
<PropertyGroup>
3+
<ChromeDir>$(ArtifactsBinDir)chrome\</ChromeDir>
4+
<BrowserStampDir>$(ArtifactsBinDir)\</BrowserStampDir>
5+
<ChromeStampFile>$(BrowserStampDir).install-chrome-$(ChromiumRevision).stamp</ChromeStampFile>
6+
<FirefoxDir>$(ArtifactsBinDir)firefox\</FirefoxDir>
7+
<FirefoxStampFile>$(BrowserStampDir).install-firefox-$(FirefoxRevision).stamp</FirefoxStampFile>
8+
</PropertyGroup>
9+
10+
<Import Project="$(MSBuildThisFileDirectory)ProvisioningVersions.props" />
11+
12+
<Target Name="DownloadAndInstallChrome"
13+
AfterTargets="Build"
14+
Condition="!Exists($(ChromeStampFile)) and '$(InstallChromeForTests)' == 'true'">
15+
16+
<ItemGroup>
17+
<_StampFile Include="$(BrowserStampDir).install-chrome*.stamp" />
18+
</ItemGroup>
19+
20+
<Delete Files="@(_StampFile)" />
21+
<RemoveDir Directories="$(ChromeDir)" />
22+
23+
<DownloadFile SourceUrl="$(ChromiumUrl)" DestinationFolder="$(ChromeDir)" SkipUnchangedFiles="true">
24+
<Output TaskParameter="DownloadedFile" PropertyName="_DownloadedFile" />
25+
</DownloadFile>
26+
<Unzip SourceFiles="$(_DownloadedFile)" DestinationFolder="$(ChromeDir)" />
27+
28+
<PropertyGroup>
29+
<_ChromeBinaryPath>$([MSBuild]::NormalizePath($(ChromeDir), $(ChromiumDirName), $(ChromiumBinaryName)))</_ChromeBinaryPath>
30+
</PropertyGroup>
31+
32+
<Error Text="Cannot find chrome at $(_ChromeBinaryPath) in the downloaded copy"
33+
Condition="!Exists($(_ChromeBinaryPath))" />
34+
35+
<Exec Command="chmod +x $(_ChromeBinaryPath)" Condition="!$([MSBuild]::IsOSPlatform('windows'))" />
36+
37+
<Touch Files="$(ChromeStampFile)" AlwaysCreate="true" />
38+
</Target>
39+
40+
<Target Name="DownloadAndInstallFirefox"
41+
AfterTargets="Build"
42+
Condition="!Exists($(FirefoxStampFile)) and '$(InstallFirefoxForTests)' == 'true' and !$([MSBuild]::IsOSPlatform('windows'))">
43+
<ItemGroup>
44+
<_StampFile Include="$(BrowserStampDir).install-firefox*.stamp" />
45+
</ItemGroup>
46+
47+
<Delete Files="@(_StampFile)" />
48+
<RemoveDir Directories="$(FirefoxDir)" />
49+
50+
<DownloadFile SourceUrl="$(FirefoxUrl)" DestinationFolder="$(FirefoxDir)" SkipUnchangedFiles="true">
51+
<Output TaskParameter="DownloadedFile" PropertyName="_DownloadedFile" />
52+
</DownloadFile>
53+
<Exec Command="tar -xf $(_DownloadedFile) -C $(FirefoxDir)"/>
54+
<Exec Command="rm -rf $(_DownloadedFile)"/>
55+
56+
<PropertyGroup>
57+
<_FirefoxBinaryPath>$([MSBuild]::NormalizePath($(FirefoxDir), $(FirefoxBinaryName)))</_FirefoxBinaryPath>
58+
</PropertyGroup>
59+
60+
<Error Text="Cannot find firefox at $(_FirefoxBinaryPath) in the downloaded copy"
61+
Condition="!Exists($(_FirefoxBinaryPath))" />
62+
63+
<Exec Command="chmod +x $(_FirefoxBinaryPath)"/>
64+
65+
<Touch Files="$(FirefoxStampFile)" AlwaysCreate="true" />
66+
</Target>
67+
</Project>

src/libraries/sendtohelix-wasm.targets

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,7 @@
115115
</When>
116116
</Choose>
117117

118-
<Import Project="$(RepoRoot)src\mono\wasm\BrowsersForTesting.props" />
118+
<Import Project="$(RepositoryEngineeringDir)testing\ProvisioningVersions.props" />
119119

120120
<Target Name="PrepareForBuildHelixWorkItems_Wasm">
121121
<PropertyGroup>

src/mono/wasm/build/WasmApp.targets

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -305,6 +305,7 @@
305305
</WasmAppBuilder>
306306

307307
<CallTarget Targets="_GenerateRunV8Script" Condition="'$(WasmGenerateRunV8Script)' == 'true'" />
308+
<Message Text="Generated app bundle at $(WasmAppDir)" Importance="High" />
308309

309310
<WriteLinesToFile File="$(WasmAppDir)\.stamp" Lines="" Overwrite="true" />
310311
</Target>
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
4+
using System;
5+
using System.IO;
6+
using System.Threading;
7+
using System.Threading.Tasks;
8+
using Microsoft.AspNetCore.Hosting;
9+
using Microsoft.Extensions.Configuration;
10+
using Microsoft.Extensions.DependencyInjection;
11+
using Microsoft.Extensions.Logging;
12+
using Microsoft.Extensions.Options;
13+
14+
#nullable enable
15+
16+
namespace Microsoft.WebAssembly.Diagnostics;
17+
18+
public static class DebugProxyHost
19+
{
20+
public static Task RunDebugProxyAsync(ProxyOptions options, string[] args, ILoggerFactory loggerFactory, CancellationToken token)
21+
{
22+
return Task.WhenAny(
23+
RunFirefoxServerLoopAsync(options, args, loggerFactory, token),
24+
RunDevToolsProxyAsync(options, args, loggerFactory, token)
25+
)
26+
.ContinueWith(t => Console.WriteLine($"Debug proxy server failed with {t.Exception}"),
27+
token,
28+
TaskContinuationOptions.OnlyOnFaulted,
29+
TaskScheduler.Default);
30+
}
31+
32+
public static Task RunFirefoxServerLoopAsync(ProxyOptions options, string[] args, ILoggerFactory loggerFactory, CancellationToken token)
33+
=> FirefoxDebuggerProxy.RunServerLoopAsync(browserPort: options.FirefoxDebugPort,
34+
proxyPort: options.FirefoxProxyPort,
35+
loggerFactory,
36+
loggerFactory.CreateLogger("FirefoxMonoProxy"),
37+
token,
38+
autoSetBreakpointOnEntryPoint: options.AutoSetBreakpointOnEntryPoint);
39+
40+
public static async Task RunDevToolsProxyAsync(ProxyOptions options, string[] args, ILoggerFactory loggerFactory, CancellationToken token)
41+
{
42+
string proxyUrl = $"http://127.0.0.1:{options.DevToolsProxyPort}";
43+
IWebHost host = new WebHostBuilder()
44+
.UseSetting("UseIISIntegration", false.ToString())
45+
.UseKestrel()
46+
.UseContentRoot(Directory.GetCurrentDirectory())
47+
.UseStartup<Startup>()
48+
.ConfigureServices(services =>
49+
{
50+
services.AddSingleton(loggerFactory);
51+
services.AddLogging(configure => configure.AddSimpleConsole().AddFilter(null, LogLevel.Information));
52+
services.AddSingleton(Options.Create(options));
53+
services.AddRouting();
54+
})
55+
.ConfigureAppConfiguration((hostingContext, config) =>
56+
{
57+
config.AddCommandLine(args);
58+
})
59+
.UseUrls(proxyUrl)
60+
.Build();
61+
62+
token.Register(async () => { Console.WriteLine($"-- token got cancelled, stopping host"); await host.StopAsync(); });
63+
await host.RunAsync(token);
64+
}
65+
}

src/mono/wasm/debugger/BrowserDebugHost/Program.cs

Lines changed: 21 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -2,68 +2,53 @@
22
// The .NET Foundation licenses this file to you under the MIT license.
33

44
using System;
5-
using System.Collections.Generic;
65
using System.IO;
7-
using Microsoft.AspNetCore.Builder;
8-
using Microsoft.AspNetCore.Hosting;
6+
using System.Threading;
7+
using System.Threading.Tasks;
98
using Microsoft.Extensions.Configuration;
9+
using Microsoft.Extensions.DependencyInjection;
1010
using Microsoft.Extensions.Logging;
1111

1212
#nullable enable
1313

1414
namespace Microsoft.WebAssembly.Diagnostics
1515
{
16-
public class ProxyOptions
17-
{
18-
public Uri DevToolsUrl { get; set; } = new Uri("http://localhost:9222");
19-
20-
public int? OwnerPid { get; set; }
21-
}
22-
2316
public class Program
2417
{
25-
public static void Main(string[] args)
18+
public static async Task Main(string[] args)
2619
{
2720
IConfigurationRoot config = new ConfigurationBuilder().AddCommandLine(args).Build();
28-
int proxyPort = 0;
29-
if (config["proxy-port"] is not null && int.TryParse(config["proxy-port"], out int port))
30-
proxyPort = port;
31-
int firefoxDebugPort = 6000;
32-
if (config["firefox-debug-port"] is not null && int.TryParse(config["firefox-debug-port"], out int ffport))
33-
firefoxDebugPort = ffport;
34-
string? logPath = config["log-path"];
35-
21+
ProxyOptions options = new();
22+
config.Bind(options);
3623

3724
using ILoggerFactory loggerFactory = LoggerFactory.Create(builder =>
3825
{
3926
builder.AddSimpleConsole(options =>
4027
{
4128
options.TimestampFormat = "[HH:mm:ss] ";
4229
})
43-
.AddFilter(null, LogLevel.Debug);
30+
.AddFilter("DevToolsProxy", LogLevel.Information)
31+
.AddFilter("FirefoxMonoProxy", LogLevel.Information)
32+
.AddFilter(null, LogLevel.Warning);
4433

45-
if (!string.IsNullOrEmpty(logPath))
46-
builder.AddFile(Path.Combine(logPath, "proxy.log"),
34+
if (!string.IsNullOrEmpty(options.LogPath))
35+
builder.AddFile(Path.Combine(options.LogPath, "proxy.log"),
4736
minimumLevel: LogLevel.Trace,
4837
outputTemplate: "{Timestamp:o} [{Level:u3}] {SourceContext}: {Message}{NewLine}{Exception}");
4938
});
5039

51-
ILogger logger = loggerFactory.CreateLogger("FirefoxMonoProxy");
52-
_ = FirefoxDebuggerProxy.Run(browserPort: firefoxDebugPort, proxyPort: proxyPort, loggerFactory, logger);
40+
CancellationTokenSource cts = new();
41+
_ = Task.Run(() => DebugProxyHost.RunDebugProxyAsync(options, args, loggerFactory, cts.Token))
42+
.ConfigureAwait(false);
5343

54-
IWebHost host = new WebHostBuilder()
55-
.UseSetting("UseIISIntegration", false.ToString())
56-
.UseKestrel()
57-
.UseContentRoot(Directory.GetCurrentDirectory())
58-
.UseStartup<Startup>()
59-
.ConfigureAppConfiguration((hostingContext, config) =>
60-
{
61-
config.AddCommandLine(args);
62-
})
63-
.UseUrls($"http://127.0.0.1:{proxyPort}")
64-
.Build();
44+
TaskCompletionSource tcs = new();
45+
Console.CancelKeyPress += (_, _) =>
46+
{
47+
tcs.SetResult();
48+
cts.Cancel();
49+
};
6550

66-
host.Run();
51+
await tcs.Task;
6752
}
6853
}
6954
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
4+
using System;
5+
6+
#nullable enable
7+
8+
namespace Microsoft.WebAssembly.Diagnostics;
9+
10+
public class ProxyOptions
11+
{
12+
public Uri DevToolsUrl { get; set; } = new Uri($"http://localhost:9222");
13+
public int? OwnerPid { get; set; }
14+
public int FirefoxProxyPort { get; set; } = 6300;
15+
public int FirefoxDebugPort { get; set; } = 6000;
16+
public int DevToolsProxyPort { get; set; } = 9300;
17+
public int DevToolsDebugPort
18+
{
19+
get => DevToolsUrl.Port;
20+
set
21+
{
22+
var builder = new UriBuilder(DevToolsUrl)
23+
{
24+
Port = value
25+
};
26+
DevToolsUrl = builder.Uri;
27+
}
28+
}
29+
public string? LogPath { get; set; }
30+
public bool AutoSetBreakpointOnEntryPoint { get; set; }
31+
}

0 commit comments

Comments
 (0)