Skip to content

Debug proxy as external tool #19767

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 38 commits into from
Mar 12, 2020

Conversation

SteveSandersonMS
Copy link
Member

@SteveSandersonMS SteveSandersonMS commented Mar 11, 2020

Implements #19702 and #19144

The list of commits makes this look more complicated than it is, because I tried out several approaches in sequence. It's only worth reviewing the final state. (Nonetheless it is still quite complicated, which is inherent to what we have to do here.)

One of the main design questions in this was how to get the debug proxy binaries into a place we can find them at runtime. I tried out all of the following approaches:

  1. Actually having a compile-time reference from M.A.C.WA.Server to M.A.C.WA.DebugProxy. This means .NET Core itself will take care of getting the debug proxy assemblies into the output directory and locating them at runtime.

    • However, this approach is bad because then the debug proxy's dependencies (including Newtonsoft.Json and Mono.Cecil) become real transitive dependencies of customers' "hosted" server projects, which makes no sense and could interfere if they want to use different versions of those packages.
  2. Keeping the binaries inside the M.A.C.WA.DebugProxy package directory. I added some MSBuild stuff that, during build, writes out the location of that package directory as a file into the build output dir. At runtime we read that file.

    • However, this approach is bad because it means you no longer have a self-contained build. You have a build that's tied to one machine and one OS. We had all sorts of problems with this before. Particularly in Docker scenarios (which aren't going to work out of the box anyway, but this approach would prevent you from making it work by enabling container->host networking).
  3. Writing the binaries to the output directory in an independent subfolder.

    • I think this is the most reasonable option because then you get a self-contained build and don't interfere with references. Also most of the mechanism happens automatically due to NuGet features and .NET build conventions (those binaries are just <Content> items in the M.A.C.WA.Server package).

If we want to change any aspects of this in the future we certainly can, but we won't have time to get too tied up about it for the preview 3 release.

Future follow-ups could include:

  • Moving the DebugProxy sources into the dotnet/blazor repo. If we did that, we could update the Mono runtime and its ws-proxy sources atomically using @pranavkm's automation.
  • Adding E2E tests covering actual debugging actions (i.e., automating clicks inside the Chrome dev tools debugger UI). That's kind of independent of this PR since it's something we could have done before and this PR doesn't change it. However the value to having them goes up now there are more moving parts involved. I'm not doing it now because it will be pretty expensive, and this feature has to ship almost immediately and we need to get on with the process of making builds and doing manual verification.

{
public void Configure(IApplicationBuilder app, DebugProxyOptions debugProxyOptions)
{
app.UseDeveloperExceptionPage();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

UseExceptionHandler?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure why. The developer exception page provides useful diagnostic info, and this is a development-time feature only.

return await tcs.Task;
}

private static void RemoveUnwantedEnvironmentVariables(IDictionary<string, string> environment)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we have @Tratcher review this?

Copy link
Member Author

@SteveSandersonMS SteveSandersonMS Mar 12, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes but unless that happens particularly quickly, let's not block the initial merge on it.

It does make sense not to pass through the ASPNETCORE_* env vars (since we don't want the child process to try using the same ports as the parent, for example).

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we have the list of environment variables before filtering? That would help us understand which ones we should remove and which ones we should preserve, but I believe they all should be removed.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You fully control the process being started, correct? In that case it would be cleaner to control the config from within that app. E.g. Don't use Host.CreateDefaultBuilder in that app and never add environment variables as a config source.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @Tratcher. We'll track your suggestions as follow up items

<Target Name="IncludeDebugProxyBinariesAsContent" BeforeTargets="GetCopyToOutputDirectoryItems">
<ItemGroup>
<DebugProxyBinaries Include="..\..\DebugProxy\src\bin\$(Configuration)\$(DefaultNetCoreTargetFramework)\**" />
<ContentWithTargetPath Include="@(DebugProxyBinaries)"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This feels very strange. Is there a reason the tool couldn't carry a copy of the proxy so it's not in the build output of the project?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See option 2 in the PR description.

@mkArtakMSFT mkArtakMSFT added area-blazor Includes: Blazor, Razor Components feature-blazor-wasm This issue is related to and / or impacts Blazor WebAssembly labels Mar 11, 2020
@SteveSandersonMS
Copy link
Member Author

@pranavkm @javiercn Don't let the build failures discourage you from approving this PR :) The build failures are a separate issue.

@javiercn
Copy link
Member

  • Adding E2E tests covering actual debugging actions (i.e., automating clicks inside the Chrome dev tools debugger UI). That's kind of independent of this PR since it's something we could have done before and this PR doesn't change it. However the value to having them goes up now there are more moving parts involved. I'm not doing it now because it will be pretty expensive, and this feature has to ship almost immediately and we need to get on with the process of making builds and doing manual verification.

Do we need to setup CTI tests for this?

}
}

private static async Task<string> LaunchAndGetUrl(IServiceProvider serviceProvider)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can I suggest that instead or relying on the Regexes (given that we control the proxy) we define on the server a "ready" endpoint and pass that down to the proxy and have the proxy send an Http request back to the server to indicate it's up and running? That way we avoid having to depend on regexes and reading console output and I think we would have a more reliable solution.

We can actually do this for keeping the process alive too, by passing in a "keep alive" endpoint that the proxy can ping to determine if the launcher process is still alive and terminate itself after a defined period of time if that's not the case. That way you don't just rely on the parent PID (which although is very rare, can be reused sometimes)

Copy link
Member Author

@SteveSandersonMS SteveSandersonMS Mar 12, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd like to not do that unless it becomes necessary. That would be yet more moving parts and a more complex polling system.

The approach of regexing console output is pretty commonplace for this purpose. We did it for NodeServices where it's continued to work fine for many years, plus we do it in our tests. VS Code does the same thing too when launching ASP.NET Core and connecting the debugger.

We don't need to keep the process alive. It stays alive on its own. We're using the parent's PID only when starting up the listener, and thereafter we attach to the Exited event which is designed for this scenario.

If any of this turns out to be inadequate in real use I'm very open to changing it, but current have no reason to think there are realistic drawbacks to this approach.

Copy link
Member

@javiercn javiercn left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't have a lot of context on the finer level details, but overall I agree with your solution and your changes look good.

If we end up not launching the debugger after May, then I'm not that worried about the specific mechanics we do for now.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area-blazor Includes: Blazor, Razor Components feature-blazor-wasm This issue is related to and / or impacts Blazor WebAssembly
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants