Mono Method Body Replacement
lambdageek opened this issue · 7 comments
Contributes to dotnet/xamarin#13 and dotnet/aspnetcore#5456
Overview
The goal of Mono method body replacement is to improve the inner dev loop experience for customers using Xamarin to create mobile apps and those using Blazor to create WebAssembly projects.
Method body replacement is a subset of a more general hot reload experience with a restricted subset of allowed edits. Non-supported ("rude") edits will generally be rejected by the tooling outside the runtime, however the runtime
itself should be transactional in that a change is either committed by the runtime in total or else it is rejected and the runtime state is unchanged.
The changes are delivered to the runtime using the EnC "dmeta" and "DIL" (and "DPDB") delta files. If the changes are accepted, then future invocations of methods affected by the delta will use the new method bodies. Currently executing versions of the methods will use the previous versions.
Multiple versions of changes can be applied in succession. Once a change is applied, it cannot be unapplied (but of course a new change can be used to undo the previous change). For collectible ALCs unloading the assembly will unload all its changes.
Method body replacement will only be supported with the Mono interpreter with inlining turned off. The DOTNET_MODIFIABLE_ASSEMBLIES environment variable must be set to the (case-insensitive) value debug. The assemblies that are eligible for modifications must be compiled with the /debug option.
Method body replacement will not interfere with debugging.
Metadata deltas will be injected either using the debugger or using a managed API. If the debugger is not involved, the runtime will pause the executing user threads at critical points during the update.
End to end scenarios
- CLI (
dotnet-watch)- Blazor WebAssembly
-
MAUIpostponed to .net7
- Visual Studio "Apply Changes" during debugging
- Android
- iOS
- Depends on dotnet/macios#11421
-
WebAssemblypostponed to .net7
Tasks
Priority 0
-
DONE PR #45612 Put the metadata update work-in-progress under a compile-time feature flag and merge to dotnet/runtime master.
-
Fix potential race when one thread is doing a metadata update and another is transforming some method. May need some kind of rwlock. (Fixed as part of #45612 - we now use an explicit call to expose the update to a thread (at entry to the interpreter transformer)
-
Support preemptive suspend, or elsePR #46873 use hybrid suspend by default, and communicate to downstream projects that MBR requires hybrid/coop suspend (mostly affects XM and XI - single-threaded wasm is ok) -
Write up a list of edits that we expect to support and coordinate with Roslyn team to implement a delta generator that rejects unsupported edits. Summary ecma335 gist
-
Measure perf impact of metadata update infrastructure on the interpreter. (That is, if there are no updates coming, what is the overhead?) #46842 (comment)
-
set compile time feature flag to on by default; put functionality under a runtime feature flag
-
Debugger
- Apply metadata updates using debugger-libs (#48458).
- debugger-libs PR mono/debugger-libs#342
- mono/mono (Mono.Debugger.Soft) PR mono/mono#20889
- dotnet/runtime (debugger agent) PR #49043
- Notify debugger client about updated sequence points, breakpoints, etc (depends on runtime dpdb consumption) #55220
- Add support for consuming "dpdb" debugger file deltas
- When applying updates through the debugger, ensure debugger engine and debugger are in sync after the delta. #55220
-
If there is a breakpoint in an old version of a method we need to send back info identifying the method version, not just a token so that the debugger frontend can display a view of the old code. This may require debugger-libs and soft debugger protocol workWe invalidate old breakpoints and set them in the updated method body.
- Apply hot reload changes through WebAssembly debugger #55220
- Apply metadata updates using debugger-libs (#48458).
-
Propose and implement required runtime APIs (Coordinate with #45629 so that ideally Mono and CoreCLR implement the same API.)
- (P0) Issue #45689 Public (or at least well-known) managed API for applying updates. Mono PR #48380
- (P0) Issue #47274 Startup runtime with hot reload enabled if
DOTNET_MODIFIABLE_ASSEMBLIES=debugis set. PR #49507
-
Properly detect cases we don't support, backout metadata updates and raise a single well-defined managed exception that leaves the runtime in a functional state.No need to roll back, update failures are fatal and don't need to be rolled back. -
set up testing infrastructure and run CI
- publish external testing artifact that our CI will depend on ⟶ https://github.com/dotnet/hotreload-utils
- Public hotreload-utils to DARC and consume in dotnet/runtime
- msbuild task for working with pre-canned deltas
- Add testing infrastructure to
System.Runtime.Loader.Teststestsuite #51144 - Enable testing on WebAssembly
-
Enable testing on Android, iOSnet7 - Enable testing on Desktop mono
- convert samples (
src/mono/netcore/samples/mbr) into functional tests
-
Managed API to return hot reload supported edits #50111
Priority 1
-
Investigate MonoImage refcount mistake - fix the shutdown hack ((The shutdown code is dead on netcore).exe_image) -
Support more edits
-
(P1) #49361 Managed events that fire when a delta is applied
-
Use MetadataUpdateHandlerAttribute to clear Mono's reflection cache #50978net7 -
Hot Reload Manager support may need #47462 - add support for host startup hooks to monoMAUI and Blazor WebAssembly will inject the hot reload manager using their own mechanisms. -
Feature flag API to enable IL trimming of hot reload support code #51159
-
Block applying changes to assemblies that have AOT images loaded.There's no supported hybrid interp/AOT scenarios.
Future (.net7 +)
Moved to #57365
Bugs
- #49227 -> PR #49328
- This was previously reported as:
Most changes to a
protected override void BuildRenderTree (Microsoft.AspNetCore.Components.Rendering.RenderTreeBuilder builder);method on a component class ends up producing a kind of change that the runtime can't apply and crashes with errors like"BadImageFormatException: Image with invalid assemblyref token 0000000f".
- This was previously reported as:
- PR #49795
- This was previously reported as:
Changing logic in basic ways usually does not work fine. For example, changing
return "my string";tovar other = "test"; return "my string" + other;leads to a crash with error"TypeLoadException: Could not resolve type with token 0100004c from typeref". - and as Blazor Wasm rendering incorrect components (ie multiple
<Counter/>components would render as another previously added component)
- This was previously reported as:
- #50190 - editing type 'B' (referenced from A) then editing 'A' causes changes to 'B' to be reverted - PR #50248 (also backported to 6.0 preview3 branch - #50254)
- #52344 - A GC after applying an update can collect the updated method bodies (!!). This manifests in a variety of ways but is usually a
BadImageFormatExceptionthrown from the caller of an updated method. - #55097
Tagging subscribers to this area: @CoffeeFlux
See info in area-owners.md if you want to be subscribed.
Issue Details
| Description: |
Method Body Replacement Contributes to dotnet/xamarin#13 and dotnet/aspnetcore#5456 OverviewThe goal of Mono method body replacement is to improve the inner dev loop experience for customers using Xamarin to create mobile apps and those using Blazor to create WebAssembly projects. Method body replacement is a subset of a more general hot reload experience with a restricted subset of allowed edits. Non-supported ("rude") edits will generally be rejected by the tooling outside the runtime, however the runtime The changes are delivered to the runtime using the EnC "dmeta" and "DIL" (and "DPDB") delta files. If the changes are accepted, then future invocations of methods affected by the delta will use the new method bodies. Currently executing versions of the methods will use the previous versions. Method body replacement will only be supported with the Mono interpreter with inlining turned off. Method body replacement will not interfere with debugging. Metadata deltas will be injected either using the debugger or using another API. If the debugger is not involved, the runtime will pause the executing user threads at critical points during the update. TasksPriority 0
Priority 1
Future
Out of scope
|
|---|---|
| Author: | lambdageek |
| Assignees: | - |
| Labels: |
|
| Milestone: | - |
why not support for jit?
hope you can support it in jit mode
@srxqds The reason we're initially focusing on the interpreter is both for practical implementation reasons and due to the use-case that we're targeting. The use-case is Blazor WebAssembly and Xamarin mobile developers who are iterating on their code. This functionality is intended to be an improvement to the developer experience, not something that we expect developers to ship in production to end-users. For this use-case, it made sense to focus on the interpreter - because that's all that is available for WebAssembly, and it can also be supported on mobile.
The practical engineering reason is because this is going to involve a lot of changes at a foundational level in Mono. There are basically three places where we will need to make changes: in the low-level code that deals with .NET IL metadata; in the type system representation (MonoClass etc); and in the compilation stage and the execution engine. The advantage of doing interpreter first is that it has a relatively isolated compilation (inter/transform.c) and there is a single place where the code to be executed is looked up. So we can make a small change that we are pretty sure won't lead to a ton of bugs.
So we can focus on making changes to the metadata and the type system without also having to deal with the compilation and execution engine at the same time.
Once the metadata and typesystem changes are stable, it might make sense to look at the JIT, but right now it would just be a distraction.
@srxqds The reason we're initially focusing on the interpreter is both for practical implementation reasons and due to the use-case that we're targeting. The use-case is Blazor WebAssembly and Xamarin mobile developers who are iterating on their code. This functionality is intended to be an improvement to the developer experience, not something that we expect developers to ship in production to end-users. For this use-case, it made sense to focus on the interpreter - because that's all that is available for WebAssembly, and it can also be supported on mobile.
The practical engineering reason is because this is going to involve a lot of changes at a foundational level in Mono. There are basically three places where we will need to make changes: in the low-level code that deals with .NET IL metadata; in the type system representation (
MonoClassetc); and in the compilation stage and the execution engine. The advantage of doing interpreter first is that it has a relatively isolated compilation (inter/transform.c) and there is a single place where the code to be executed is looked up. So we can make a small change that we are pretty sure won't lead to a ton of bugs.So we can focus on making changes to the metadata and the type system without also having to deal with the compilation and execution engine at the same time.
Once the metadata and typesystem changes are stable, it might make sense to look at the JIT, but right now it would just be a distraction.
ok, thank you for reply.
hi, when can you begin to support add static fields and methods to existing classes work?
Pushed the future work to a separate tracking issue. This one is done. #57365