-
-
Notifications
You must be signed in to change notification settings - Fork 226
Description
Problem Statement
.NET 7 introduced "Native AOT" deployments for Windows and Linux and .NET 8 adds Native AOT support for macOS.
- This is different than ".NET Native", that is used with UWP
- It is also different than the Mono AOT compilation used on .NET iOS apps
Initial investigation
Testing on Windows, a .NET console app with Sentry publishing with Native AOT gives these warnings:
C:\Users\mattj\.nuget\packages\sentry\3.29.1\lib\net6.0\Sentry.dll : warning IL2104: Assembly 'Sentry' produced trim warnings. For more information see https://aka.ms/dotnet-illink/libraries [C:\Users\mattj\Code\Temp\AotConsoleApp\AotConsoleApp.csproj]
C:\Users\mattj\.nuget\packages\sentry\3.29.1\lib\net6.0\Sentry.dll : warning IL3053: Assembly 'Sentry' produced AOT analysis warnings. [C:\Users\mattj\Code\Temp\AotConsoleApp\AotConsoleApp.csproj]
Trying to capture an exception shows internal serialization failures related AOT trimming:
Error: Failed to serialize object for property 'Dynamic Code'. Original depth: 2, current depth: 2
System.NotSupportedException: 'System.Text.Json.Serialization.Converters.DictionaryOfTKeyTValueConverter`3[System.Collections.Generic.Dictionary`2[System.String, System.Boolean], System.String, System.Boolean]' is missing native code or metadata. This can happen for code that is not compatible with trimming or AOT. Inspect and fix trimming and AOT related warnings that were generated when the app was published. For more information see https://aka.ms/nativeaot-compatibility
at System.Reflection.Runtime.General.TypeUnifier.WithVerifiedTypeHandle(RuntimeConstructedGenericTypeInfo, RuntimeTypeInfo[]) + 0x88
at System.Text.Json.Serialization.Converters.IEnumerableConverterFactory.CreateConverter(Type, JsonSerializerOptions) + 0x6a2
at System.Text.Json.Serialization.JsonConverterFactory.GetConverterInternal(Type, JsonSerializerOptions) + 0x15
at System.Text.Json.JsonSerializerOptions.ExpandConverterFactory(JsonConverter, Type) + 0x32
at System.Text.Json.Serialization.Metadata.DefaultJsonTypeInfoResolver.GetConverterForType(Type, JsonSerializerOptions, Boolean) + 0x78
at System.Text.Json.Serialization.Metadata.DefaultJsonTypeInfoResolver.CreateJsonTypeInfo(Type, JsonSerializerOptions) + 0x21
at System.Text.Json.Serialization.Metadata.DefaultJsonTypeInfoResolver.GetTypeInfo(Type, JsonSerializerOptions) + 0x47
at System.Text.Json.JsonSerializerOptions.GetTypeInfoNoCaching(Type type) + 0x33
at System.Collections.Concurrent.ConcurrentDictionary`2.GetOrAdd(TKey, Func`2) + 0x82
at System.Text.Json.JsonSerializerOptions.GetTypeInfoInternal(Type, Boolean, Boolean) + 0x3f
at System.Text.Json.JsonSerializer.ResolvePolymorphicTypeInfo[TValue](TValue&, JsonTypeInfo, Boolean&) + 0x82
at System.Text.Json.JsonSerializer.WriteCore[TValue](Utf8JsonWriter, TValue&, JsonTypeInfo`1) + 0x99
at System.Text.Json.JsonSerializer.Serialize[TValue](Utf8JsonWriter, TValue, JsonSerializerOptions) + 0x3a
at Sentry.Internal.Extensions.JsonExtensions.WriteDynamicValue(Utf8JsonWriter, Object, IDiagnosticLogger) + 0x437
at Sentry.Internal.Extensions.JsonExtensions.WriteDynamic(Utf8JsonWriter, String, Object, IDiagnosticLogger) + 0x4a
The error event does come through to Sentry, but without its stack trace.
Trimming was added in .NET 6 as an option for self-contained deployments, but is required when Native AOT is used. There is guidance for libraries here:
https://learn.microsoft.com/dotnet/core/deploying/trimming/prepare-libraries-for-trimming
Checking for trimming errors by adding <IsTrimmable>true</IsTrimmable> to Sentry.csproj shows three main areas where there are trimming issues in the Sentry SDK:
-
Ben.Demystifieruses reflection heavily. We already learned (in Fix: Disable Ben.Demystifier for UWP by default. #821) that we can't use it by default in UWP because .NET Native also doesn't like it. We'll have to do something similar for Native AOT. That means noStackTraceMode.Enhancedfor Native AOT. 😞 (unless we can find an alternative)modules\Ben.Demystifier\src\Ben.Demystifier\Internal\ReflectionHelper.cs(59,23): error IL2070: 'this' argument does not satisfy 'DynamicallyAccessedMemberTypes.PublicProperties' in call to 'System.Type.GetProperty(String, BindingFlags)'. The parameter 'attributeType' of method 'System.Diagnostics.Internal.ReflectionHelper.GetTransformNamesPropertyInfo(Type)' does not have matching annotations. The source value must declare at least the same requirements as those declared on the target location it is assigned to. [src\Sentry\Sentry.csproj::TargetFramework=net6.0] modules\Ben.Demystifier\src\Ben.Demystifier\EnhancedStackTrace.Frames.cs(39,54): error IL2026: Using member 'System.Reflection.Assembly.GetType(String, Boolean)' which has 'RequiresUnreferencedCodeAttribute' can break functionality when trimming application code. Types might be removed. [src\Sentry\Sentry.csproj::TargetFramework=net6.0] modules\Ben.Demystifier\src\Ben.Demystifier\Internal\ILReader.cs(61,32): error IL2026: Using member 'System.Reflection.Module.ResolveMember(Int32, Type[], Type[])' which has 'RequiresUnreferencedCodeAttribute' can break functionality when trimming application code. Trimming changes metadata tokens. [src\Sentry\Sentry.csproj::TargetFramework=net6.0] modules\Ben.Demystifier\src\Ben.Demystifier\EnhancedStackTrace.Frames.cs(389,21): error IL2026: Using member 'System.Reflection.MethodBase.GetMethodBody()' which has 'RequiresUnreferencedCodeAttribute' can break functionality when trimming application code. Trimming may change method bodies. For example it can change some instructions, remove branches or local variables. [src\Sentry\Sentry.csproj::TargetFramework=net6.0] modules\Ben.Demystifier\src\Ben.Demystifier\EnhancedStackTrace.Frames.cs(463,52): error IL2075: 'this' argument does not satisfy 'DynamicallyAccessedMemberTypes.PublicMethods', 'DynamicallyAccessedMemberTypes.NonPublicMethods' in call to 'System.Type.GetMethods(BindingFlags)'. The return value of method 'System.Reflection.MemberInfo.DeclaringType.get' does not have matching annotations. The source value must declare at least the same requirements as those declared on the target location it is assigned to. [src\Sentry\Sentry.csproj::TargetFramework=net6.0] modules\Ben.Demystifier\src\Ben.Demystifier\EnhancedStackTrace.Frames.cs(348,36): error IL2075: 'this' argument does not satisfy 'DynamicallyAccessedMemberTypes.PublicMethods', 'DynamicallyAccessedMemberTypes.NonPublicMethods' in call to 'System.Type.GetMethods(BindingFlags)'. The return value of method 'System.Reflection.MemberInfo.DeclaringType.get' does not have matching annotations. The source value must declare at least the same requirements as those declared on the target location it is assigned to. [src\Sentry\Sentry.csproj::TargetFramework=net6.0] modules\Ben.Demystifier\src\Ben.Demystifier\EnhancedStackTrace.Frames.cs(351,41): error IL2075: 'this' argument does not satisfy 'DynamicallyAccessedMemberTypes.PublicConstructors', 'DynamicallyAccessedMemberTypes.NonPublicConstructors' in call to 'System.Type.GetConstructors(BindingFlags)'. The return value of method 'System.Reflection.MemberInfo.DeclaringType.get' does not have matching annotations. The source value must declare at least the same requirements as those declared on the target location it is assigned to. [src\Sentry\Sentry.csproj::TargetFramework=net6.0] modules\Ben.Demystifier\src\Ben.Demystifier\EnhancedStackTrace.Frames.cs(363,36): error IL2075: 'this' argument does not satisfy 'DynamicallyAccessedMemberTypes.PublicMethods', 'DynamicallyAccessedMemberTypes.NonPublicMethods' in call to 'System.Type.GetMethods(BindingFlags)'. The return value of method 'System.Type.DeclaringType.get' does not have matching annotations. The source value must declare at least the same requirements as those declared on the target location it is assigned to. [src\Sentry\Sentry.csproj::TargetFramework=net6.0] modules\Ben.Demystifier\src\Ben.Demystifier\EnhancedStackTrace.Frames.cs(366,41): error IL2075: 'this' argument does not satisfy 'DynamicallyAccessedMemberTypes.PublicConstructors', 'DynamicallyAccessedMemberTypes.NonPublicConstructors' in call to 'System.Type.GetConstructors(BindingFlags)'. The return value of method 'System.Type.DeclaringType.get' does not have matching annotations. The source value must declare at least the same requirements as those declared on the target location it is assigned to. [src\Sentry\Sentry.csproj::TargetFramework=net6.0] modules\Ben.Demystifier\src\Ben.Demystifier\EnhancedStackTrace.Frames.cs(371,45): error IL2075: 'this' argument does not satisfy 'DynamicallyAccessedMemberTypes.PublicConstructors', 'DynamicallyAccessedMemberTypes.NonPublicConstructors' in call to 'System.Type.GetConstructors(BindingFlags)'. The return value of method 'System.Type.DeclaringType.get' does not have matching annotations. The source value must declare at least the same requirements as those declared on the target location it is assigned to. [src\Sentry\Sentry.csproj::TargetFramework=net6.0] modules\Ben.Demystifier\src\Ben.Demystifier\EnhancedStackTrace.Frames.cs(188,42): error IL2075: 'this' argument does not satisfy 'DynamicallyAccessedMemberTypes.PublicFields', 'DynamicallyAccessedMemberTypes.NonPublicFields' in call to 'System.Type.GetFields(BindingFlags)'. The return value of method 'System.Reflection.MemberInfo.DeclaringType.get' does not have matching annotations. The source value must declare at least the same requirements as those declared on the target location it is assigned to. [src\Sentry\Sentry.csproj::TargetFramework=net6.0] modules\Ben.Demystifier\src\Ben.Demystifier\EnhancedStackTrace.Frames.cs(867,17): error IL2070: 'this' argument does not satisfy 'DynamicallyAccessedMemberTypes.PublicMethods', 'DynamicallyAccessedMemberTypes.NonPublicMethods' in call to 'System.Type.GetMethods(BindingFlags)'. The parameter 'type' of method 'GetDeclaredMethods(Type)' does not have matching annotations. The source value must declare at least the same requirements as those declared on the target location it is assigned to. [src\Sentry\Sentry.csproj::TargetFramework=net6.0] -
Our
WinUIUnhandledExceptionIntegrationuses reflection to attach toMicrosoft.UI.Xaml.Application.Current.UnhandledException. This is primarily for .NET MAUI on Windows, but is also for non-MAUI WinUI 3 applications.src\Sentry\Integrations\WinUIUnhandledExceptionIntegration.cs(84,35): error IL2026: Using member 'System.Reflection.Assembly.GetType(String)' which has 'RequiresUnreferencedCodeAttribute' can break functionality when trimming application code. Types might be removed. [src\Sentry\Sentry.csproj::TargetFramework=net6.0] src\Sentry\Integrations\WinUIUnhandledExceptionIntegration.cs(85,31): error IL2075: 'this' argument does not satisfy 'DynamicallyAccessedMemberTypes.PublicProperties' in call to 'System.Type.GetProperty(String)'. The return value of method 'System.Reflection.Assembly.GetType(String)' does not have matching annotations. The source value must declare at least the same requirements as those declared on the target location it is assigned to. [src\Sentry\Sentry.csproj::TargetFramework=net6.0] src\Sentry\Integrations\WinUIUnhandledExceptionIntegration.cs(86,29): error IL2075: 'this' argument does not satisfy 'DynamicallyAccessedMemberTypes.PublicEvents' in call to 'System.Type.GetEvent(String)'. The return value of method 'System.Reflection.Assembly.GetType(String)' does not have matching annotations. The source value must declare at least the same requirements as those declared on the target location it is assigned to. [src\Sentry\Sentry.csproj::TargetFramework=net6.0] -
Our
JsonExtensions.WriteDynamicValuemethod has a long list of types it will serialize, but then ultimately just tries to serializeobject. Such usage is invalid in STJ when Native AOT is used.src\Sentry\Internal\Extensions\JsonExtensions.cs(488,17): error IL2026: Using member 'System.Text.Json.JsonSerializer.Serialize<TValue>(Utf8JsonWriter, TValue, JsonSerializerOptions)' which has 'RequiresUnreferencedCodeAttribute' can break functionality when trimming application code. JSON serialization and deserialization might require types that cannot be statically analyzed. Use the overload that takes a JsonTypeInfo or JsonSerializerContext, or make sure all of the required types are preserved. [src\Sentry\Sentry.csproj::TargetFramework=net6.0] src\Sentry\Internal\Extensions\JsonExtensions.cs(497,17): error IL2026: Using member 'System.Text.Json.JsonSerializer.Serialize<TValue>(Utf8JsonWriter, TValue, JsonSerializerOptions)' which has 'RequiresUnreferencedCodeAttribute' can break functionality when trimming application code. JSON serialization and deserialization might require types that cannot be statically analyzed. Use the overload that takes a JsonTypeInfo or JsonSerializerContext, or make sure all of the required types are preserved. [src\Sentry\Sentry.csproj::TargetFramework=net6.0] src\Sentry\Internal\Extensions\JsonExtensions.cs(504,17): error IL2026: Using member 'System.Text.Json.JsonSerializer.Serialize<TValue>(Utf8JsonWriter, TValue, JsonSerializerOptions)' which has 'RequiresUnreferencedCodeAttribute' can break functionality when trimming application code. JSON serialization and deserialization might require types that cannot be statically analyzed. Use the overload that takes a JsonTypeInfo or JsonSerializerContext, or make sure all of the required types are preserved. [src\Sentry\Sentry.csproj::TargetFramework=net6.0]
Solution Brainstorm
We should be able to disable Ben.Demystifier and just use StackTraceMode.Original, but we'll need to add various trimmer attributes to the code to prevent the trimming warnings from breaking the build. This will be tricky, as we import Ben.Demystifier as a submodule.
We can either move WinUIUnhandledExceptionIntegration back to the Sentry.Maui package, or to a new package, or we can make a Windows target for the main Sentry package to avoid reflection. Alternatively, we could explore if DynamicDependencyAttribute would let us keep it as-is without it being trimmed out.
The main pain point is going to be JSON serialization. We have lots of places where we serialize object types - some of which are strictly internal, but others are exposed to end users. We could consider an API that would let the end-user provide JsonSerializerContext. See https://devblogs.microsoft.com/dotnet/try-the-new-system-text-json-source-generator/
Here are all the public APIs that would be affected by such a change:
- Contexts
- Envelope.Header
- EnvelopeItem.Header
- Mechanism.Data
- Mechanism.Meta
- Request.Data
- SentryEvent.Extra
- SentryMessage.Params
- SentryValues
- Span.Extra
- Transaction.Extra
All of these currently flow through to serialization via JsonExtensions.WriteDynamicValue.
After we ensure Sentry works without compile errors/warnings, we should also see what might need to change to support symbolication for .NET Native AOT apps.
Metadata
Metadata
Assignees
Labels
Projects
Status