Skip to content
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

[FEATURE REQ] Resolve Native AOT Warnings in Azure.Core #35572

Closed
eerhardt opened this issue Apr 14, 2023 · 12 comments
Closed

[FEATURE REQ] Resolve Native AOT Warnings in Azure.Core #35572

eerhardt opened this issue Apr 14, 2023 · 12 comments
Assignees
Labels
Azure.Core Client This issue points to a problem in the data-plane of the library. feature-request This issue requires a new behavior in the product in order be resolved.
Milestone

Comments

@eerhardt
Copy link
Member

eerhardt commented Apr 14, 2023

Library name

Azure.Core

Please describe the feature.

In .NET 8 our goal is to enable a subset of ASP.NET functionality to support publishing with Native AOT. For more information on this goal, see ASP.NET Core updates in .NET 8 Preview 3 - ASP.NET Core support for native AOT and Support publishing ASP.NET Core API apps with Native AOT. This work is being done with a focus on enhancing .NET for "Cloud Native" applications. One important aspect for cloud apps is being able to monitor and diagnose the deployed application. With this in mind, our intent is to enable Open Telemetry support for Native AOT (for Microsoft employees, see the internal work item). Getting the Azure.Monitor.OpenTelemetry.Exporter compatible with AOT is important so those Open Telemetry events can be exported to Azure Monitor.

Azure.Monitor.OpenTelemetry.Exporter has a dependency on Azure.Core and parts of Azure.Core are not compatible with Native AOT. The best way to prepare a library for Native AOT is following the guidance in:

Using that information, we should resolve the Native AOT warnings coming from Azure.Core, starting first with the code used by Azure.Monitor.OpenTelemetry.Exporter. To get the Native AOT warnings for the Azure.Core library, you can follow these steps (taken from the above docs):

  1. Using the latest .NET 8 SDK - https://dotnet.microsoft.com/download/dotnet/8.0 (7.0 can be used, but 8.0 has more of the core System libraries annotated, so it is preferred).
  2. dotnet publish the following application:
<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>net8.0</TargetFramework>
    <PublishAot>true</PublishAot>
    <TrimmerSingleWarn>false</TrimmerSingleWarn>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="Azure.Monitor.OpenTelemetry.Exporter" Version="1.0.0-beta.10" />
    <!-- Analyze the whole assembly -->
    <TrimmerRootAssembly Include="Azure.Monitor.OpenTelemetry.Exporter" />

    <!-- Update this dependency to its latest, which has all the annotations -->
    <PackageReference Include="Microsoft.Extensions.Logging.Configuration" Version="8.0.0-*" />
  </ItemGroup>
</Project>
System.Console.WriteLine("Hello World");
  1. Analyze the warnings that are emitted. We should resolve all the warnings coming from Azure.Core.

I have done the above with the current code, and here are the warnings I am seeing from Azure.Core:

ILC : Trim analysis warning IL2080: Azure.Core.Pipeline.ActivityExtensions.ActivitySourceStartActivity(Object,String,Int32,DateTimeOffset,ICollection`1<KeyValuePair`2<String,Object>>,IList,String,String): 'this' argument does not satisfy 'DynamicallyAccessedMemberTypes.PublicMethods' in call to 'System.Type.GetMethod(String,BindingFlags,Binder,Type[],ParameterModifier[])'. The field 'Azure.Core.Pipeline.ActivityExtensions.ActivitySourceType' 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. [C:\Users\eerhardt\source\repos\AzureAotCompatibility\AzureAotCompatibility\AzureAotCompatibility.csproj]
ILC : Trim analysis warning IL2080: Azure.Core.Pipeline.ActivityExtensions.ActivitySourceStartActivity(Object,String,Int32,DateTimeOffset,ICollection`1<KeyValuePair`2<String,Object>>,IList,String,String): 'this' argument does not satisfy 'DynamicallyAccessedMemberTypes.PublicMethods' in call to 'System.Type.GetMethod(String,BindingFlags)'. The field 'Azure.Core.Pipeline.ActivityExtensions.ActivityContextType' 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. [C:\Users\eerhardt\source\repos\AzureAotCompatibility\AzureAotCompatibility\AzureAotCompatibility.csproj]
ILC : Trim analysis warning IL2077: Azure.Core.Pipeline.ActivityExtensions.ActivitySourceStartActivity(Object,String,Int32,DateTimeOffset,ICollection`1<KeyValuePair`2<String,Object>>,IList,String,String): 'type' argument does not satisfy 'DynamicallyAccessedMemberTypes.PublicParameterlessConstructor' in call to 'System.Activator.CreateInstance(Type)'. The field 'Azure.Core.Pipeline.ActivityExtensions.ActivityContextType' 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. [C:\Users\eerhardt\source\repos\AzureAotCompatibility\AzureAotCompatibility\AzureAotCompatibility.csproj]
ILC : AOT analysis warning IL3050: Azure.Core.Pipeline.ActivityExtensions.ActivitySourceStartActivity(Object,String,Int32,DateTimeOffset,ICollection`1<KeyValuePair`2<String,Object>>,IList,String,String): Using member 'System.Type.MakeGenericType(Type[])' which has 'RequiresDynamicCodeAttribute' can break functionality when AOT compiling. The native code for this instantiation might not be available at runtime. [C:\Users\eerhardt\source\repos\AzureAotCompatibility\AzureAotCompatibility\AzureAotCompatibility.csproj]

ILC : Trim analysis warning IL2080: Azure.Core.Pipeline.ActivityExtensions.CreateActivityLink(String,String,ICollection`1<KeyValuePair`2<String,Object>>): 'this' argument does not satisfy 'DynamicallyAccessedMemberTypes.PublicMethods' in call to 'System.Type.GetMethod(String,BindingFlags)'. The field 'Azure.Core.Pipeline.ActivityExtensions.ActivityContextType' 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. [C:\Users\eerhardt\source\repos\AzureAotCompatibility\AzureAotCompatibility\AzureAotCompatibility.csproj]
ILC : Trim analysis warning IL2080: Azure.Core.Pipeline.ActivityExtensions.CreateActivityLink(String,String,ICollection`1<KeyValuePair`2<String,Object>>): 'this' argument does not satisfy 'DynamicallyAccessedMemberTypes.PublicConstructors' in call to 'System.Type.GetConstructor(Type[])'. The field 'Azure.Core.Pipeline.ActivityExtensions.ActivityLinkType' 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. [C:\Users\eerhardt\source\repos\AzureAotCompatibility\AzureAotCompatibility\AzureAotCompatibility.csproj]
ILC : Trim analysis warning IL2077: Azure.Core.Pipeline.ActivityExtensions.CreateActivitySource(String): 'type' argument does not satisfy 'DynamicallyAccessedMemberTypes.PublicConstructors' in call to 'System.Activator.CreateInstance(Type,Object[])'. The field 'Azure.Core.Pipeline.ActivityExtensions.ActivitySourceType' 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. [C:\Users\eerhardt\source\repos\AzureAotCompatibility\AzureAotCompatibility\AzureAotCompatibility.csproj]

ILC : AOT analysis warning IL3050: Azure.Core.Pipeline.ActivityExtensions.CreateLinkCollection(): Using member 'System.Type.MakeGenericType(Type[])' which has 'RequiresDynamicCodeAttribute' can break functionality when AOT compiling. The native code for this instantiation might not be available at runtime. [C:\Users\eerhardt\source\repos\AzureAotCompatibility\AzureAotCompatibility\AzureAotCompatibility.csproj]
ILC : Trim analysis warning IL2080: Azure.Core.Pipeline.ActivityExtensions.CreateTagsCollection(): 'this' argument does not satisfy 'DynamicallyAccessedMemberTypes.PublicParameterlessConstructor' in call to 'System.Type.GetConstructor(Type[])'. The field 'Azure.Core.Pipeline.ActivityExtensions.ActivityTagsCollectionType' 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. [C:\Users\eerhardt\source\repos\AzureAotCompatibility\AzureAotCompatibility\AzureAotCompatibility.csproj]
ILC : Trim analysis warning IL2080: Azure.Core.Pipeline.ActivityExtensions.ActivitySourceHasListeners(Object): 'this' argument does not satisfy 'DynamicallyAccessedMemberTypes.PublicMethods' in call to 'System.Type.GetMethod(String,BindingFlags)'. The field 'Azure.Core.Pipeline.ActivityExtensions.ActivitySourceType' 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. [C:\Users\eerhardt\source\repos\AzureAotCompatibility\AzureAotCompatibility\AzureAotCompatibility.csproj]

ILC : Trim analysis warning IL2026: Azure.Core.Pipeline.ClientDiagnostics.ExtractAzureErrorContent(String): Using member 'System.Text.Json.JsonSerializer.Deserialize<ClientDiagnostics.ErrorResponse>(String,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. [C:\Users\eerhardt\source\repos\AzureAotCompatibility\AzureAotCompatibility\AzureAotCompatibility.csproj]
ILC : AOT analysis warning IL3050: Azure.Core.Pipeline.ClientDiagnostics.ExtractAzureErrorContent(String): Using member 'System.Text.Json.JsonSerializer.Deserialize<ClientDiagnostics.ErrorResponse>(String,JsonSerializerOptions)' which has 'RequiresDynamicCodeAttribute' can break functionality when AOT compiling. JSON serialization and deserialization might require types that cannot be statically analyzed and might need runtime code generation. Use System.Text.Json source generation for native AOT applications. [C:\Users\eerhardt\source\repos\AzureAotCompatibility\AzureAotCompatibility\AzureAotCompatibility.csproj]
ILC : Trim analysis warning IL2075: Azure.Core.Pipeline.ClientDiagnostics.GetResourceProviderNamespace(Assembly): 'this' argument does not satisfy 'DynamicallyAccessedMemberTypes.PublicProperties' in call to 'System.Type.GetProperty(String)'. The return value of method 'System.Object.GetType()' 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. [C:\Users\eerhardt\source\repos\AzureAotCompatibility\AzureAotCompatibility\AzureAotCompatibility.csproj]

ILC : Trim analysis warning IL2026: Azure.Core.Pipeline.DiagnosticScope.ActivityAdapter.Start(): Using member 'System.Diagnostics.DiagnosticSource.Write(String,Object)' which has 'RequiresUnreferencedCodeAttribute' can break functionality when trimming application code. The type of object being written to DiagnosticSource cannot be discovered statically. [C:\Users\eerhardt\source\repos\AzureAotCompatibility\AzureAotCompatibility\AzureAotCompatibility.csproj]
ILC : Trim analysis warning IL2026: Azure.Core.Pipeline.DiagnosticScope.ActivityAdapter.MarkFailed(Exception): Using member 'System.Diagnostics.DiagnosticSource.Write(String,Object)' which has 'RequiresUnreferencedCodeAttribute' can break functionality when trimming application code. The type of object being written to DiagnosticSource cannot be discovered statically. [C:\Users\eerhardt\source\repos\AzureAotCompatibility\AzureAotCompatibility\AzureAotCompatibility.csproj]
ILC : Trim analysis warning IL2026: Azure.Core.Pipeline.DiagnosticScope.ActivityAdapter.Dispose(): Using member 'System.Diagnostics.DiagnosticSource.Write(String,Object)' which has 'RequiresUnreferencedCodeAttribute' can break functionality when trimming application code. The type of object being written to DiagnosticSource cannot be discovered statically. [C:\Users\eerhardt\source\repos\AzureAotCompatibility\AzureAotCompatibility\AzureAotCompatibility.csproj]

ILC : Trim analysis warning IL2026: Azure.Core.Serialization.JsonObjectSerializer.SerializeToBinaryDataInternal(Object,Type): Using member 'System.Text.Json.JsonSerializer.SerializeToUtf8Bytes(Object,Type,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. [C:\Users\eerhardt\source\repos\AzureAotCompatibility\AzureAotCompatibility\AzureAotCompatibility.csproj]
ILC : AOT analysis warning IL3050: Azure.Core.Serialization.JsonObjectSerializer.SerializeToBinaryDataInternal(Object,Type): Using member 'System.Text.Json.JsonSerializer.SerializeToUtf8Bytes(Object,Type,JsonSerializerOptions)' which has 'RequiresDynamicCodeAttribute' can break functionality when AOT compiling. JSON serialization and deserialization might require types that cannot be statically analyzed and might need runtime code generation. Use System.Text.Json source generation for native AOT applications. [C:\Users\eerhardt\source\repos\AzureAotCompatibility\AzureAotCompatibility\AzureAotCompatibility.csproj]

ILC : Trim analysis warning IL2075: Azure.Core.Pipeline.HttpPipelineSynchronousPolicy.HttpPipelineSynchronousPolicy(): 'this' argument does not satisfy 'DynamicallyAccessedMemberTypes.PublicMethods' in call to 'System.Type.GetMethod(String,BindingFlags,Binder,Type[],ParameterModifier[])'. The return value of method 'System.Object.GetType()' 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. [C:\Users\eerhardt\source\repos\AzureAotCompatibility\AzureAotCompatibility\AzureAotCompatibility.csproj]

ILC : Trim analysis warning IL2026: Azure.Core.Diagnostics.AzureCoreEventSource.RequestRetrying(String,Int32,Double): Using member 'System.Diagnostics.Tracing.EventSource.WriteEvent(Int32,Object[])' which has 'RequiresUnreferencedCodeAttribute' can break functionality when trimming application code. EventSource will serialize the whole object graph. Trimmer will not safely handle this case because properties may be trimmed. This can be suppressed if the object is a primitive type. [C:\Users\eerhardt\source\repos\AzureAotCompatibility\AzureAotCompatibility\AzureAotCompatibility.csproj]

ILC : Trim analysis warning IL2026: Azure.RequestFailedException.TryExtractErrorContent(Response,ResponseError&,IDictionary`2<String,String>&): Using member 'System.Text.Json.JsonSerializer.Deserialize<RequestFailedException.ErrorResponse>(String,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. [C:\Users\eerhardt\source\repos\AzureAotCompatibility\AzureAotCompatibility\AzureAotCompatibility.csproj]
ILC : AOT analysis warning IL3050: Azure.RequestFailedException.TryExtractErrorContent(Response,ResponseError&,IDictionary`2<String,String>&): Using member 'System.Text.Json.JsonSerializer.Deserialize<RequestFailedException.ErrorResponse>(String,JsonSerializerOptions)' which has 'RequiresDynamicCodeAttribute' can break functionality when AOT compiling. JSON serialization and deserialization might require types that cannot be statically analyzed and might need runtime code generation. Use System.Text.Json source generation for native AOT applications. [C:\Users\eerhardt\source\repos\AzureAotCompatibility\AzureAotCompatibility\AzureAotCompatibility.csproj]

See the above 2 documentation links for more information on these warnings, and how to address them. (Or reach out to me and I will gladly help as well.)

cc @KrzysztofCwalina @jsquire @annelo-msft @pallavit @tg-msft @Yun-Ting

@github-actions github-actions bot added the needs-triage Workflow: This is a new issue that needs to be triaged to the appropriate team. label Apr 14, 2023
@jsquire jsquire added Client This issue points to a problem in the data-plane of the library. Azure.Core feature-request This issue requires a new behavior in the product in order be resolved. labels Apr 14, 2023
@jsquire jsquire added this to the Backlog milestone Apr 14, 2023
@github-actions github-actions bot removed the needs-triage Workflow: This is a new issue that needs to be triaged to the appropriate team. label Apr 14, 2023
@jsquire
Copy link
Member

jsquire commented Apr 14, 2023

@jsquire
Copy link
Member

jsquire commented Apr 14, 2023

Thank you for filing this, @eerhardt. We'll start to take a look over the next couple of weeks and reach out once we know more.

@eerhardt
Copy link
Member Author

Thanks, @jsquire.

As I look through some of the warnings from the above steps, I see that the following code uses System.Linq.Expressions:

#pragma warning disable SA1507 // File can not contain multiple types
/// <summary>
/// Until we can reference the 5.0 of System.Diagnostics.DiagnosticSource
/// </summary>
internal static class ActivityExtensions
{
static ActivityExtensions()
{
ResetFeatureSwitch();
}
private static bool SupportsActivitySourceSwitch;
private static readonly Type? ActivitySourceType = Type.GetType("System.Diagnostics.ActivitySource, System.Diagnostics.DiagnosticSource");
private static readonly Type? ActivityKindType = Type.GetType("System.Diagnostics.ActivityKind, System.Diagnostics.DiagnosticSource");
private static readonly Type? ActivityTagsCollectionType = Type.GetType("System.Diagnostics.ActivityTagsCollection, System.Diagnostics.DiagnosticSource");
private static readonly Type? ActivityLinkType = Type.GetType("System.Diagnostics.ActivityLink, System.Diagnostics.DiagnosticSource");
private static readonly Type? ActivityContextType = Type.GetType("System.Diagnostics.ActivityContext, System.Diagnostics.DiagnosticSource");
private static readonly Type? ActivityStatusCodeType = Type.GetType("System.Diagnostics.ActivityStatusCode, System.Diagnostics.DiagnosticSource");
private static Action<Activity, int>? SetIdFormatMethod;
private static Func<Activity, string?>? GetTraceStateStringMethod;
private static Action<Activity, string?>? SetTraceStateStringMethod;
private static Action<Activity, int, string?>? SetErrorStatusMethod;
private static Func<Activity, int>? GetIdFormatMethod;
private static Action<Activity, string, object?>? ActivityAddTagMethod;
private static Func<object, string, int, object?, ICollection<KeyValuePair<string, object>>?, IList?, DateTimeOffset, Activity?>? ActivitySourceStartActivityMethod;
private static Func<object, bool>? ActivitySourceHasListenersMethod;
private static Func<string, string?, ICollection<KeyValuePair<string, object>>?, object?>? CreateActivityLinkMethod;
private static Func<ICollection<KeyValuePair<string,object>>?>? CreateTagsCollectionMethod;
private static Func<Activity, string, object?>? GetCustomPropertyMethod;
private static Action<Activity, string, object>? SetCustomPropertyMethod;
private static readonly ParameterExpression ActivityParameter = Expression.Parameter(typeof(Activity));
private static MethodInfo? ParseActivityContextMethod;
public static object? GetCustomProperty(this Activity activity, string propertyName)
{
if (GetCustomPropertyMethod == null)
{
var method = typeof(Activity).GetMethod("GetCustomProperty");
if (method == null)
{
GetCustomPropertyMethod = (_, _) => null;
}
else
{
var nameParameter = Expression.Parameter(typeof(string));
GetCustomPropertyMethod = Expression.Lambda<Func<Activity, string, object>>(
Expression.Convert(Expression.Call(ActivityParameter, method, nameParameter), typeof(object)),
ActivityParameter, nameParameter).Compile();
}
}
return GetCustomPropertyMethod(activity, propertyName);
}
public static void SetCustomProperty(this Activity activity, string propertyName, object propertyValue)
{
if (SetCustomPropertyMethod == null)
{
var method = typeof(Activity).GetMethod("SetCustomProperty");
if (method == null)
{
SetCustomPropertyMethod = (_, _, _) => { };
}
else
{
var nameParameter = Expression.Parameter(typeof(string));
var valueParameter = Expression.Parameter(typeof(object));
SetCustomPropertyMethod = Expression.Lambda<Action<Activity, string, object>>(
Expression.Call(ActivityParameter, method, nameParameter, valueParameter),
ActivityParameter, nameParameter, valueParameter).Compile();
}
}
SetCustomPropertyMethod(activity, propertyName, propertyValue);
}
public static void SetW3CFormat(this Activity activity)
{
if (SetIdFormatMethod == null)
{
var method = typeof(Activity).GetMethod("SetIdFormat");
if (method == null)
{
SetIdFormatMethod = (_, _) => { };
}
else
{
var idParameter = Expression.Parameter(typeof(int));
var convertedId = Expression.Convert(idParameter, method.GetParameters()[0].ParameterType);
SetIdFormatMethod = Expression.Lambda<Action<Activity, int>>(
Expression.Call(ActivityParameter, method, convertedId),
ActivityParameter, idParameter).Compile();
}
}
SetIdFormatMethod(activity, 2 /* ActivityIdFormat.W3C */);
}
public static bool IsW3CFormat(this Activity activity)
{
if (GetIdFormatMethod == null)
{
var method = typeof(Activity).GetProperty("IdFormat")?.GetMethod;
if (method == null)
{
GetIdFormatMethod = _ => -1;
}
else
{
GetIdFormatMethod = Expression.Lambda<Func<Activity, int>>(
Expression.Convert(Expression.Call(ActivityParameter, method), typeof(int)),
ActivityParameter).Compile();
}
}
int result = GetIdFormatMethod(activity);
return result == 2 /* ActivityIdFormat.W3C */;
}
public static string? GetTraceState(this Activity activity)
{
if (GetTraceStateStringMethod == null)
{
var method = typeof(Activity).GetProperty("TraceStateString")?.GetMethod;
if (method == null)
{
GetTraceStateStringMethod = _ => null;
}
else
{
GetTraceStateStringMethod = Expression.Lambda<Func<Activity, string?>>(
Expression.Call(ActivityParameter, method),
ActivityParameter).Compile();
}
}
return GetTraceStateStringMethod(activity);
}
public static void SetTraceState(this Activity activity, string? tracestate)
{
if (SetTraceStateStringMethod == null)
{
var method = typeof(Activity).GetProperty("TraceStateString")?.SetMethod;
if (method == null)
{
SetTraceStateStringMethod = (_, _) => { };
}
else
{
var tracestateParameter = Expression.Parameter(typeof(string));
var convertedParameter = Expression.Convert(tracestateParameter, method.GetParameters()[0].ParameterType);
SetTraceStateStringMethod = Expression.Lambda<Action<Activity, string?>>(
Expression.Call(ActivityParameter, method, convertedParameter),
ActivityParameter, tracestateParameter).Compile();
}
}
SetTraceStateStringMethod(activity, tracestate);
}
public static void SetErrorStatus(this Activity activity, string? errorDescription)
{
if (SetErrorStatusMethod == null)
{
if (ActivityStatusCodeType == null)
{
SetErrorStatusMethod = (_, _, _) => { };
}
else
{
var method = typeof(Activity).GetMethod("SetStatus", BindingFlags.Instance | BindingFlags.Public, null, new Type[]
{
ActivityStatusCodeType,
typeof(string)
}, null);
if (method == null)
{
SetErrorStatusMethod = (_, _, _) => { };
}
else
{
var methodParameters = method.GetParameters();
var statusParameter = Expression.Parameter(typeof(int));
var descriptionParameter = Expression.Parameter(typeof(string));
SetErrorStatusMethod = Expression.Lambda<Action<Activity, int, string?>>(
Expression.Call(ActivityParameter, method, Expression.Convert(statusParameter, methodParameters[0].ParameterType), descriptionParameter),
ActivityParameter, statusParameter, descriptionParameter).Compile();
}
}
}
SetErrorStatusMethod(activity, 2 /* Error */, errorDescription);
}
public static void AddObjectTag(this Activity activity, string name, object value)
{
if (ActivityAddTagMethod == null)
{
var method = typeof(Activity).GetMethod("AddTag", BindingFlags.Instance | BindingFlags.Public, null, new Type[]
{
typeof(string),
typeof(object)
}, null);
if (method == null)
{
// If the object overload is not available, fall back to the string overload. The assumption is that the object overload
// not being available means that we cannot be using activity source, so the string cast should never fail because we will always
// be passing a string value.
ActivityAddTagMethod = (activityParameter, nameParameter, valueParameter) => activityParameter.AddTag(
nameParameter,
// null check is required to keep nullable reference compilation happy
valueParameter == null ? null : (string)valueParameter);
}
else
{
var nameParameter = Expression.Parameter(typeof(string));
var valueParameter = Expression.Parameter(typeof(object));
ActivityAddTagMethod = Expression.Lambda<Action<Activity, string, object?>>(
Expression.Call(ActivityParameter, method, nameParameter, valueParameter),
ActivityParameter, nameParameter, valueParameter).Compile();
}
}
ActivityAddTagMethod(activity, name, value);
}
public static bool SupportsActivitySource()
{
return SupportsActivitySourceSwitch && ActivitySourceType != null;
}
public static ICollection<KeyValuePair<string,object>>? CreateTagsCollection()
{
if (CreateTagsCollectionMethod == null)
{
var ctor = ActivityTagsCollectionType?.GetConstructor(Array.Empty<Type>());
if (ctor == null)
{
CreateTagsCollectionMethod = () => null;
}
else
{
CreateTagsCollectionMethod = Expression.Lambda<Func<ICollection<KeyValuePair<string,object>>?>>(
Expression.New(ctor)).Compile();
}
}
return CreateTagsCollectionMethod();
}
public static object? CreateActivityLink(string traceparent, string? tracestate, ICollection<KeyValuePair<string,object>>? tags)
{
if (ActivityLinkType == null)
{
return null;
}
if (CreateActivityLinkMethod == null)
{
var parseMethod = ActivityContextType?.GetMethod("Parse", BindingFlags.Static | BindingFlags.Public);
var ctor = ActivityLinkType?.GetConstructor(new[] { ActivityContextType!, ActivityTagsCollectionType! });
if (parseMethod == null ||
ctor == null ||
ActivityTagsCollectionType == null ||
ActivityContextType == null)
{
CreateActivityLinkMethod = (_, _, _) => null;
}
else
{
var traceparentParameter = Expression.Parameter(typeof(string));
var tracestateParameter = Expression.Parameter(typeof(string));
var tagsParameter = Expression.Parameter(typeof(ICollection<KeyValuePair<string,object>>));
CreateActivityLinkMethod = Expression.Lambda<Func<string, string?, ICollection<KeyValuePair<string, object>>?, object?>>(
Expression.TryCatch(
Expression.Convert(Expression.New(ctor,
Expression.Call(parseMethod, traceparentParameter, tracestateParameter),
Expression.Convert(tagsParameter, ActivityTagsCollectionType)), typeof(object)),
Expression.Catch(typeof(Exception), Expression.Default(typeof(object)))),
traceparentParameter, tracestateParameter, tagsParameter).Compile();
}
}
return CreateActivityLinkMethod(traceparent, tracestate, tags);
}
public static bool ActivitySourceHasListeners(object? activitySource)
{
if (!SupportsActivitySource())
{
return false;
}
if (activitySource == null)
{
return false;
}
if (ActivitySourceHasListenersMethod == null)
{
var method = ActivitySourceType?.GetMethod("HasListeners", BindingFlags.Instance | BindingFlags.Public);
if (method == null ||
ActivitySourceType == null)
{
ActivitySourceHasListenersMethod = _ => false;
}
else
{
var sourceParameter = Expression.Parameter(typeof(object));
ActivitySourceHasListenersMethod = Expression.Lambda<Func<object, bool>>(
Expression.Call(Expression.Convert(sourceParameter, ActivitySourceType), method),
sourceParameter).Compile();
}
}
return ActivitySourceHasListenersMethod.Invoke(activitySource);
}
public static Activity? ActivitySourceStartActivity(
object? activitySource,
string activityName,
int kind,
DateTimeOffset startTime,
ICollection<KeyValuePair<string, object>>? tags,
IList? links,
string? traceparent,
string? tracestate)
{
if (activitySource == null)
{
return null;
}
object? activityContext = default;
if (ActivitySourceStartActivityMethod == null)
{
if (ActivityLinkType == null ||
ActivitySourceType == null ||
ActivityContextType == null ||
ActivityKindType == null)
{
ActivitySourceStartActivityMethod = (_, _, _, _, _, _, _) => null;
}
else
{
var method = ActivitySourceType?.GetMethod("StartActivity", BindingFlags.Instance | BindingFlags.Public, null, new[]
{
typeof(string),
ActivityKindType,
ActivityContextType,
typeof(IEnumerable<KeyValuePair<string, object>>),
typeof(IEnumerable<>).MakeGenericType(ActivityLinkType),
typeof(DateTimeOffset)
}, null);
if (method == null)
{
ActivitySourceStartActivityMethod = (_, _, _, _, _, _, _) => null;
}
else
{
var sourceParameter = Expression.Parameter(typeof(object));
var nameParameter = Expression.Parameter(typeof(string));
var kindParameter = Expression.Parameter(typeof(int));
var contextParameter = Expression.Parameter(typeof(object));
var startTimeParameter = Expression.Parameter(typeof(DateTimeOffset));
var tagsParameter = Expression.Parameter(typeof(ICollection<KeyValuePair<string, object>>));
var linksParameter = Expression.Parameter(typeof(IList));
var methodParameter = method.GetParameters();
ParseActivityContextMethod = ActivityContextType.GetMethod("Parse", BindingFlags.Static | BindingFlags.Public);
ActivitySourceStartActivityMethod = Expression.Lambda<Func<object, string, int, object?, ICollection<KeyValuePair<string, object>>?, IList?, DateTimeOffset, Activity?>>(
Expression.Call(
Expression.Convert(sourceParameter, method.DeclaringType!),
method,
nameParameter,
Expression.Convert(kindParameter, methodParameter[1].ParameterType),
Expression.Convert(contextParameter, methodParameter[2].ParameterType),
Expression.Convert(tagsParameter, methodParameter[3].ParameterType),
Expression.Convert(linksParameter, methodParameter[4].ParameterType),
Expression.Convert(startTimeParameter, methodParameter[5].ParameterType)),
sourceParameter, nameParameter, kindParameter, contextParameter, tagsParameter, linksParameter, startTimeParameter).Compile();

This is unfortunate because:

  1. System.Linq.Expressions hasn't been made AOT compatible (yet) in .NET 8. See Annotate remaining runtime libraries for NativeAOT dotnet/runtime#75480.
  2. Even once it is "compatible" since Native AOT won't allow emitting IL at runtime, this means that any lambdas created using System.Linq.Expressions executed will be interpreted, which will be slower than normal code.

Looking at the comment on that code, it appears that this code is working around not being able to reference a newer System.Diagnostics.DiagnosticSource with the necessary APIs. Would it make sense to update to a newer System.Diagnostics.DiagnosticSource version as part of this work? Then the Linq.Expressions usage could be removed?

cc @agocke @vitek-karas @LakshanF @MichalStrehovsky

@jsquire
Copy link
Member

jsquire commented Apr 15, 2023

it appears that this code is working around not being able to reference a newer System.Diagnostics.DiagnosticSource with the necessary APIs. Would it make sense to update to a newer System.Diagnostics.DiagnosticSource version as part of this work?

@JoshLove-msft would be best able to offer an opinion on whether there's something that we still need in the older package.

@JoshLove-msft
Copy link
Member

We have been blocked from upgrading to 5.0 due to concerns with Azure Functions and PowerShell compatibility. We were recently given the all-clear that it is okay from a Functions perspective, but we still need to do some research on the PowerShell side. #33683 has some more details.
/cc @lmolkova

@eerhardt
Copy link
Member Author

One option (if we really needed to work around this) would be to add a net8.0 target, and only upgrade the System.Diagnostics.DiagnosticSource version for the net8.0 target. On older TFMs, you can still use the 4.6 version and the above Linq.Expressions usage to reflect against the DiagnosticSource APIs. But I'd only do that if the update couldn't occur.

@AlexanderSher AlexanderSher self-assigned this Apr 17, 2023
@heaths
Copy link
Member

heaths commented Apr 18, 2023

On a related note, we should also work on trimming: #24238. The whole assembly doesn't need to be AOT-compatible if we properly annotate what won't work (and people don't use it).

@annelo-msft
Copy link
Member

annelo-msft commented Apr 18, 2023

@eerhardt, one thing we'd like to understand, related to your comment:

Using that information, we should resolve the Native AOT warnings coming from Azure.Core, starting first with the code used by Azure.Monitor.OpenTelemetry.Exporter.

This sounds like there is an eventual goal of resolving all of the Native AOT warnings in Azure.Core (although not included in the scope of work represented by this issue). I'd like to understand this better, per @heaths's comment above.

Given that non-compliant types can be trimmed, what would be the benefit of 100% AOT-compatibility for Azure.Core? Or conversely, what would we lose by adding new non-compliant types to Azure.Core that would prevent us from eventually reaching this goal?

@eerhardt
Copy link
Member Author

My first goal is to get Open Telemetry in an ASP.NET app working with no warnings. Getting the whole Azure.Core assembly annotated (for example marking the APIs that are inherently incompatible with [RequiresUnreferencedCode] or [RequiresDynamicCode as necessary]) would be the next step, but wouldn’t be blocking me. I don’t think it is necessarily a goal to get the whole Azure.Core assembly fully compatible with AOT. There can be APIs that are not trimming/AOT compatible, as long as they are annotated as such. If those APIs aren't used, they will be trimmed and the user won't see any warnings from them.

The advantage of annotating incompatible APIs is that any callers of the incompatible APIs will get warnings where they call the incompatible API. This is preferred because callers know immediately what they are doing isn’t trim/AOT-compatible, and there is a custom message you can use to explain why it isn’t compatible.

When the incompatible code isn't annotated, the dev won't get warnings from their code (at build time). But instead, they will see the warnings at publish-time coming from within the Azure.Core library itself - which they can't fix.

@Yun-Ting
Copy link

@jsquire
Copy link
Member

jsquire commented Sep 20, 2023

Duplicate: #38773

@TimothyMothra
Copy link
Contributor

@m-redding I don't think this is resolved yet. See #39410

@github-actions github-actions bot locked and limited conversation to collaborators Jan 18, 2024
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Azure.Core Client This issue points to a problem in the data-plane of the library. feature-request This issue requires a new behavior in the product in order be resolved.
Projects
None yet
Development

No branches or pull requests

9 participants