When recording a new AOT profile for .NET MAUI apps running on Android,
we noticed that System.Reflection.Emit work was being done on a
background thread. The call seen in `dotnet-trace` output:
11.32ms microsoft.extensions.dependencyinjection!Microsoft.Extensions.DependencyInjection.ServiceLookup.ILEmitResolverBuilder.GenerateMethodBody(Microsoft.Extensions.DependencyInjection.ServiceLookup.ServiceCallSite,System.Reflection.Emit.ILGenerator)
.NET Android apps are unique in that there is a JIT,
`RuntimeFeature.IsDynamicCodeCompiled` is true, System.Reflection.Emit
is possible -- S.R.E is however, not great for startup performance.
Starting threads on Android during startup can also be slow, as Android
will commonly put all but a single core to sleep for battery saving
purposes. We try to avoid starting threads on startup for "hello world"
applications on Android.
To solve this for now, introduce a new feature flag:
Microsoft.Extensions.DependencyInjection.DisableDynamicEngine
Which, we will provide a value in either the Android or .NET MAUI
optional workload via an MSBuild property. To test, I put this in my
app's `.csproj` file:
<RuntimeHostConfigurationOption Include="Microsoft.Extensions.DependencyInjection.DisableDynamicEngine"
Condition="'$(DisableDynamicEngine)' != ''"
Value="$(DisableDynamicEngine)"
Trim="true" />
Customers *could* opt to change this flag, but we don't think it will
particularly useful. An example of services realized by .NET MAUI at
startup, via some logging added:
08-25 13:21:55.647 16530 16530 I DOTNET : RealizeService called: System.Collections.Generic.IEnumerable`1[Microsoft.Maui.Hosting.IMauiInitializeService]
08-25 13:21:55.664 16530 16530 I DOTNET : RealizeService called: System.Collections.Generic.IEnumerable`1[Microsoft.Maui.Hosting.IMauiInitializeScopedService]
08-25 13:21:55.665 16530 16530 I DOTNET : RealizeService called: Microsoft.Maui.Dispatching.IDispatcher
08-25 13:21:55.668 16530 16530 I DOTNET : RealizeService called: System.Collections.Generic.IEnumerable`1[Microsoft.Maui.LifecycleEvents.LifecycleEventRegistration]
08-25 13:21:56.057 16530 16530 I DOTNET : RealizeService called: System.Collections.Generic.IEnumerable`1[Microsoft.Maui.Hosting.HandlerMauiAppBuilderExtensions+HandlerRegistration]
08-25 13:21:56.115 16530 16530 I DOTNET : RealizeService called: Microsoft.Extensions.DependencyInjection.IServiceScopeFactory
08-25 13:21:56.670 16530 16530 I DOTNET : RealizeService called: Microsoft.Maui.Controls.HideSoftInputOnTappedChangedManager
08-25 13:21:56.712 16530 16530 I DOTNET : RealizeService called: System.Collections.Generic.IEnumerable`1[Microsoft.Maui.Hosting.ImageSourcesMauiAppBuilderExtensions+ImageSourceRegistration]
08-25 13:21:57.700 16530 16530 I DOTNET : RealizeService using S.R.E: Microsoft.Maui.Controls.HideSoftInputOnTappedChangedManager
`HideSoftInputOnTappedChangedManager` would be realized once per page,
which would not be a huge payoff to use S.R.E for. So the only way the
S.R.E codepath could be useful on Android would be if the customer is
registering lots of services themselves. They might be better off just
using `new()` in that case?
An example of the startup time Android reports with the new flag on/off:
DisableDynamicEngine=false
08-25 14:31:37.462 2090 2330 I ActivityTaskManager: Displayed com.companyname.testmaui/crc643c09abdeec717b83.MainActivity: +733ms
08-25 14:31:39.394 2090 2330 I ActivityTaskManager: Displayed com.companyname.testmaui/crc643c09abdeec717b83.MainActivity: +737ms
08-25 14:31:41.326 2090 2330 I ActivityTaskManager: Displayed com.companyname.testmaui/crc643c09abdeec717b83.MainActivity: +730ms
DisableDynamicEngine=true
08-25 14:32:20.233 2090 2330 I ActivityTaskManager: Displayed com.companyname.testmaui/crc643c09abdeec717b83.MainActivity: +724ms
08-25 14:32:22.137 2090 2330 I ActivityTaskManager: Displayed com.companyname.testmaui/crc643c09abdeec717b83.MainActivity: +727ms
08-25 14:32:24.042 2090 2330 I ActivityTaskManager: Displayed com.companyname.testmaui/crc643c09abdeec717b83.MainActivity: +716ms
This was a `dotnet new maui` project, using dotnet/maui/main on a Pixel
5 device.
Co-authored-by: Eric Erhardt <eric.erhardt@microsoft.com>
* Fix UseManagedNtlm linker Substitutions
- Specify the default value (false) for the feature on Linux so the linker runs the substitution when no value was specified by the user.
- Make the `UseManagedNtlm` property public because the linker and IL compiler doesn't support substitution of private properties.
- Add `--ignore-substitutions` switch to ILLink during library build to prevent the substitution with default value taking place.
* Revert unnecessary changes, document _UseManagedNtlm switch
* Set _UseManagedNtlm in NativeAOT integration
* Revert unnecessary change, move default values for trimming properties from NativeAOT targets to ILLink ones
* Fix build
* A bit faster version of indexing. WIP
* Tiny speedup.
* Fixed IndexOf, ToDo: LastIndexOf.
* All tests pass.
* Updated docs.
* Update docs.
* Slicing + saving previous absolute index instead of pushing the iterator to the start position.
* Refactored.
* Fixed tests on browser.
* Str1 and str2 was confusing.
* Fix CI- correctly trimming Hybrid properties.
* Previous commit should target only Browser.
* Applied @mkhamoyan's suggestion to avoid code duplication.
* Applied @pavelsavara's review.
* Get rid of build errors.
* Revert.
* Implement the STJ.DisableDefaultReflection feature switch.
* Reinstate accidentally stripped attribute
* Address feedback.
* Address feedback.
* Add a trimming test for STJ
* Move trimming test to existing trimming tests folder.
* Add source gen serialization test case to Trimming test.
* Fix style.
* Expose the feature switch as a property on JsonSerializer -- rename feature switch to match namespace.
* Update src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Helpers.cs
Co-authored-by: Eric Erhardt <eric.erhardt@microsoft.com>
* Update src/libraries/System.Text.Json/tests/System.Text.Json.Tests/TrimmingTests/IsReflectionEnabledByDefaultFalse.cs
Co-authored-by: Eric Erhardt <eric.erhardt@microsoft.com>
* Address feedback.
* Address feedback.
* Add entry to feature-switches.md
---------
Co-authored-by: Eric Erhardt <eric.erhardt@microsoft.com>
Change github illink links to point from dotnet/linker to dotnet/runtime
Delete scripts that clone illink into runtime since illink now lives in runtime
Create a forward link for error 1012 in illink, that leads users to open an issue in runtime
* Resolve MakeGenericType ILLink warning in DependencyInjection
Resolve the ILLink warning in DependencyInjection by adding a runtime check that is behind a new AppContext switch 'Microsoft.Extensions.DependencyInjection.VerifyOpenGenericServiceTrimmability'. The runtime check ensures the trimming annotations on the open generic types are compatible between the service and implementation types. The check is enabled by default when PublishTrimmed=true.
* Make VerifyOpenGenericServiceTrimmability a full feature switch
* Copy ILLink.LinkAttributes from mono to shared.
* Update ILLink.LinkAttributes that are now shared between mono and coreclr
- Move IntrinsicAttribute to mono only
- Introduce feature switches for NullabilityInfoContext and AggressiveAttributeTrimming
- Minor other cleanup
* Respect the NullabilityInfoContext.IsSupported feature switch in NullabilityInfoContext.
* Document new feature switches
* Add trimming tests for NullabilityInfoContextSupport
* Move NonVersionableAttribute to mono-only
* Fix Linq tests now that the ExtensionAttribute isn't being trimmed all the time on mono WASM
Move the metadata update related APIs to the MetadataUpdater class
The old APIs AssemblyExtensions.ApplyUpdate() and AssemblyExtensions.GetApplyUpdateCapabilities() will be removed
after all the references to them have been changed to the new APIs.
Add the new IsSupported API described in issue https://github.com/dotnet/runtime/issues/51159.
Change the tests to use the MetadataUpdater APIs.
Fix the ApplyUpdate qcalls and icalls
Add the ILLink substitutions for MetadataUpdater.IsSupported property
Change the old APIs to call the new ones
Update mono's MetadataUpdater.IsSupported property
Update feature switch doc
Fixed the argument checking in coreclr's MetadataUpdater.ApplyUpdate().
Add an NSAutoreleasePool to all managed create threads including the Main and Finalizer.
This expands the current support where support was only added to ThreadPool threads.
New feature switch was created and the ThreadPool one was removed.
- System.Threading.Thread.EnableAutoreleasePool
Updated AutoReleaseTest for the new scenarios.
* Making System.Resources.ResourceManager trim safe
* Addressing PR Feedback
* Rename objects => types
* Also rename the strings.resx
* More PR Feedback
* Add UnconditionalSuppressMessage to Extensions
* Rename feature switch and reword error messages
* Removing new warning that is already addressed
* Rename AppContext switch and add feature switch to docs
* Addressing Feedback
* Revert changes to findtype plus some other feedback
* Revert FindType back
* move feature check up
Since the DiagnosticsHandler still gets instantiated in the HttpClientHandler, none of its overriden methods are getting trimmed. This leads to System.Diagnostics.DiagnosticListener still being preserved in a linked application.
The fix is to split DiagnosticsHandler.IsEnabled() into two methods:
* IsGloballyEnabled() - checks the AppContext switch, and is replaced by the linker by a feature swtich.
* IsEnabled() - which checks IsGloballyEnabled() and if there is an Activity or listener available.
This allows all but the IsEnabled and IsGloballyEnabled methods to get trimmed on DiagnosticsHandler.
Contributes to #38765
Applications are given the option of disabling BinaryFormatter.Serialize and BinaryFormatter.Deserialize, meaning they'll throw NotSupportedException if anybody tries calling them. WASM projects always disable these APIs via a hardcoded mechanism; there is no re-enablement mechanism for those projects.
This commit only introduces the switch. It does not disable it in any project types by default. (Exception: wasm, as mentioned above.) The related PR https://github.com/dotnet/sdk/pull/12434 causes aspnet 5.0+ applications to disable BinaryFormatter functionality by default, but apps can re-enable if needed.