diff --git a/eng/pipelines/coreclr/nativeaot-post-build-steps.yml b/eng/pipelines/coreclr/nativeaot-post-build-steps.yml index a44c7223039580..a920993b093cbc 100644 --- a/eng/pipelines/coreclr/nativeaot-post-build-steps.yml +++ b/eng/pipelines/coreclr/nativeaot-post-build-steps.yml @@ -29,3 +29,9 @@ steps: - ${{ if ne(parameters.osGroup, 'windows') }}: - script: $(Build.SourcesDirectory)/src/tests/run.sh --runnativeaottests $(buildConfigUpper) ${{ parameters.archType }} displayName: Run tests in single file mode + + # Publishing tooling doesn't support different configs between runtime and libs, so only run tests in Release config + # PublishAot on OSX doesn't work yet. Need an SDK with https://github.com/dotnet/installer/pull/14443. + - ${{ if and(eq(parameters.buildConfig, 'release'), ne(parameters.osGroup, 'OSX')) }}: + - script: $(Build.SourcesDirectory)$(dir)build$(scriptExt) -ci -arch ${{ parameters.archType }} $(_osParameter) -s libs.tests -c $(_BuildConfig) /p:TestAssemblies=false /p:RunNativeAotTestApps=true $(_officialBuildParameter) $(_crossBuildPropertyArg) /bl:$(Build.SourcesDirectory)/artifacts/log/$(buildConfigUpper)/NativeAotTests.binlog ${{ parameters.extraTestArgs }} + displayName: Run NativeAot Library Tests diff --git a/eng/testing/linker/SupportFiles/Directory.Build.props b/eng/testing/linker/SupportFiles/Directory.Build.props index da3533e49c939f..1e08117fd3171c 100644 --- a/eng/testing/linker/SupportFiles/Directory.Build.props +++ b/eng/testing/linker/SupportFiles/Directory.Build.props @@ -2,7 +2,6 @@ true true - true full false true diff --git a/eng/testing/linker/SupportFiles/Directory.Build.targets b/eng/testing/linker/SupportFiles/Directory.Build.targets index 491c45e4a43fa9..e735d71fa0f723 100644 --- a/eng/testing/linker/SupportFiles/Directory.Build.targets +++ b/eng/testing/linker/SupportFiles/Directory.Build.targets @@ -12,6 +12,10 @@ DependsOnTargets="BundleTestWasmApp" Condition="'$(TargetArchitecture)' == 'wasm' And '$(TargetOS)' == 'browser'" /> + + $(CoreCLRBuildIntegrationDir)Microsoft.DotNet.ILCompiler.SingleEntry.targets + + diff --git a/eng/testing/linker/project.csproj.template b/eng/testing/linker/project.csproj.template index 80180798fd30e8..9b1c14e36b814d 100644 --- a/eng/testing/linker/project.csproj.template +++ b/eng/testing/linker/project.csproj.template @@ -6,6 +6,7 @@ {NetCoreAppMaximumVersion} {UseMonoRuntime} {RuntimeIdentifier} + {PublishAot} {MonoAOTCompilerDir} @@ -25,6 +26,15 @@ {RepositoryEngineeringDir} <_ExtraTrimmerArgs>{ExtraTrimmerArgs} $(_ExtraTrimmerArgs) + {AdditionalProperties} + + + {IlcToolsPath} + {IlcBuildTasksPath} + {IlcSdkPath} + {IlcFrameworkPath} + {IlcFrameworkNativePath} + {CoreCLRBuildIntegrationDir} diff --git a/eng/testing/linker/trimmingTests.targets b/eng/testing/linker/trimmingTests.targets index ac688f61b6b2d7..9f821e99c2a3a5 100644 --- a/eng/testing/linker/trimmingTests.targets +++ b/eng/testing/linker/trimmingTests.targets @@ -72,10 +72,14 @@ <_switchesAsItems Include="%(TestConsoleApps.DisabledFeatureSwitches)" Value="false" /> <_switchesAsItems Include="%(TestConsoleApps.EnabledFeatureSwitches)" Value="true" /> + + <_propertiesAsItems Include="%(TestConsoleApps.DisabledProperties)" Value="false" /> + <_propertiesAsItems Include="%(TestConsoleApps.EnabledProperties)" Value="true" /> <_runtimeHostConfigurationOptionsString>@(_switchesAsItems->'<RuntimeHostConfigurationOption Include="%(Identity)" Value="%(Value)" Trim="true" />', '%0a ') + <_additionalPropertiesString>@(_propertiesAsItems->'<%(Identity)>%(Value)</%(Identity)>', '%0a ') @@ -85,8 +89,16 @@ .Replace('{NetCoreAppMaximumVersion}', '$(NetCoreAppMaximumVersion)') .Replace('{UseMonoRuntime}','$(UseMonoRuntime)') .Replace('{RuntimeIdentifier}','%(TestConsoleApps.TestRuntimeIdentifier)') + .Replace('{PublishAot}','$(IsNativeAotTestProject)') .Replace('{MicrosoftNETILLinkTasksVersion}', '$(MicrosoftNETILLinkTasksVersion)') .Replace('{ExtraTrimmerArgs}', '%(TestConsoleApps.ExtraTrimmerArgs)') + .Replace('{AdditionalProperties}', '$(_additionalPropertiesString)') + .Replace('{IlcToolsPath}', '$(CoreCLRILCompilerDir)') + .Replace('{IlcBuildTasksPath}', '$(CoreCLRILCompilerDir)netstandard/ILCompiler.Build.Tasks.dll') + .Replace('{IlcSdkPath}', '$(CoreCLRAotSdkDir)') + .Replace('{IlcFrameworkPath}', '$(MicrosoftNetCoreAppRuntimePackRidLibTfmDir)') + .Replace('{IlcFrameworkNativePath}', '$(MicrosoftNetCoreAppRuntimePackNativeDir)') + .Replace('{CoreCLRBuildIntegrationDir}', '$(CoreCLRBuildIntegrationDir)') .Replace('{RuntimeHostConfigurationOptions}', '$(_runtimeHostConfigurationOptionsString)') .Replace('{AdditionalProjectReferences}', '$(_additionalProjectReferencesString)') .Replace('{RepositoryEngineeringDir}', '$(RepositoryEngineeringDir)') diff --git a/src/libraries/Directory.Build.props b/src/libraries/Directory.Build.props index d609d71b29f30d..b7abf93f6d9efb 100644 --- a/src/libraries/Directory.Build.props +++ b/src/libraries/Directory.Build.props @@ -22,10 +22,12 @@ true true - true + true + true + true - false + false @@ -36,14 +38,14 @@ '$(IsReferenceAssemblyProject)' != 'true' and '$(IsGeneratorProject)' != 'true' and '$(IsTestProject)' != 'true' and - '$(IsTrimmingTestProject)' != 'true' and + '$(IsPublishedAppTestProject)' != 'true' and '$(IsTestSupportProject)' != 'true' and '$(UsingMicrosoftNoTargetsSdk)' != 'true' and '$(UsingMicrosoftTraversalSdk)' != 'true'">true - + $(NoWarn);SYSLIB0011 diff --git a/src/libraries/Directory.Build.targets b/src/libraries/Directory.Build.targets index 8f9b52adb2805d..46cc84f810c209 100644 --- a/src/libraries/Directory.Build.targets +++ b/src/libraries/Directory.Build.targets @@ -127,7 +127,7 @@ - + diff --git a/src/libraries/Microsoft.Extensions.Hosting/src/HostBuilder.cs b/src/libraries/Microsoft.Extensions.Hosting/src/HostBuilder.cs index a285d3e487ef7f..9c864e489ab139 100644 --- a/src/libraries/Microsoft.Extensions.Hosting/src/HostBuilder.cs +++ b/src/libraries/Microsoft.Extensions.Hosting/src/HostBuilder.cs @@ -188,8 +188,6 @@ internal static DiagnosticListener LogHostBuilding(HostApplicationBuilder hostAp return diagnosticListener; } - [UnconditionalSuppressMessage("AOT", "IL3050:RequiresDynamicCode", - Justification = "DiagnosticSource is used here to pass objects in-memory to code using HostFactoryResolver. This won't require creating new generic types.")] [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:UnrecognizedReflectionPattern", Justification = "The values being passed into Write are being consumed by the application already.")] private static void Write( diff --git a/src/libraries/System.Diagnostics.DiagnosticSource/ref/System.Diagnostics.DiagnosticSource.cs b/src/libraries/System.Diagnostics.DiagnosticSource/ref/System.Diagnostics.DiagnosticSource.cs index 75b85af1913d47..1b08291cccd2ab 100644 --- a/src/libraries/System.Diagnostics.DiagnosticSource/ref/System.Diagnostics.DiagnosticSource.cs +++ b/src/libraries/System.Diagnostics.DiagnosticSource/ref/System.Diagnostics.DiagnosticSource.cs @@ -20,7 +20,6 @@ public virtual void Dispose() { } public virtual System.IDisposable Subscribe(System.IObserver> observer, System.Predicate? isEnabled) { throw null; } public override string ToString() { throw null; } [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCode("The type of object being written to DiagnosticSource cannot be discovered statically.")] - [System.Diagnostics.CodeAnalysis.RequiresDynamicCode("DiagnosticSource may require creating new generic types or methods, which requires creating code at runtime. This may not work when AOT compiling.")] public override void Write(string name, object? value) { } } public abstract partial class DiagnosticSource @@ -29,7 +28,6 @@ protected DiagnosticSource() { } public abstract bool IsEnabled(string name); public virtual bool IsEnabled(string name, object? arg1, object? arg2 = null) { throw null; } [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCode("The type of object being written to DiagnosticSource cannot be discovered statically.")] - [System.Diagnostics.CodeAnalysis.RequiresDynamicCode("DiagnosticSource may require creating new generic types or methods, which requires creating code at runtime. This may not work when AOT compiling.")] public abstract void Write(string name, object? value); } } diff --git a/src/libraries/System.Diagnostics.DiagnosticSource/ref/System.Diagnostics.DiagnosticSourceActivity.cs b/src/libraries/System.Diagnostics.DiagnosticSource/ref/System.Diagnostics.DiagnosticSourceActivity.cs index 5177cb52339eca..488e6c39224fe0 100644 --- a/src/libraries/System.Diagnostics.DiagnosticSource/ref/System.Diagnostics.DiagnosticSourceActivity.cs +++ b/src/libraries/System.Diagnostics.DiagnosticSource/ref/System.Diagnostics.DiagnosticSourceActivity.cs @@ -197,10 +197,8 @@ public abstract partial class DiagnosticSource public virtual void OnActivityExport(System.Diagnostics.Activity activity, object? payload) { } public virtual void OnActivityImport(System.Diagnostics.Activity activity, object? payload) { } [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCode("The type of object being written to DiagnosticSource cannot be discovered statically.")] - [System.Diagnostics.CodeAnalysis.RequiresDynamicCode("DiagnosticSource may require creating new generic types or methods, which requires creating code at runtime. This may not work when AOT compiling.")] public System.Diagnostics.Activity StartActivity(System.Diagnostics.Activity activity, object? args) { throw null; } [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCode("The type of object being written to DiagnosticSource cannot be discovered statically.")] - [System.Diagnostics.CodeAnalysis.RequiresDynamicCode("DiagnosticSource may require creating new generic types or methods, which requires creating code at runtime. This may not work when AOT compiling.")] public void StopActivity(System.Diagnostics.Activity activity, object? args) { } } public enum ActivitySamplingResult diff --git a/src/libraries/System.Diagnostics.DiagnosticSource/src/System.Diagnostics.DiagnosticSource.csproj b/src/libraries/System.Diagnostics.DiagnosticSource/src/System.Diagnostics.DiagnosticSource.csproj index 9c22bce30dae9b..97163bbceec0a0 100644 --- a/src/libraries/System.Diagnostics.DiagnosticSource/src/System.Diagnostics.DiagnosticSource.csproj +++ b/src/libraries/System.Diagnostics.DiagnosticSource/src/System.Diagnostics.DiagnosticSource.csproj @@ -87,6 +87,8 @@ System.Diagnostics.DiagnosticSource + diff --git a/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/DiagnosticListener.cs b/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/DiagnosticListener.cs index 75a7116c58fc16..c1677307acb5e0 100644 --- a/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/DiagnosticListener.cs +++ b/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/DiagnosticListener.cs @@ -34,8 +34,6 @@ public partial class DiagnosticListener : DiagnosticSource, IObservable public static IObservable AllListeners { - [UnconditionalSuppressMessage("AotAnalysis", "IL3050:RequiresDynamicCode", - Justification = "ENABLE_HTTP_HANDLER is not enabled in the .NET current version")] get { #if ENABLE_HTTP_HANDLER @@ -255,7 +253,6 @@ public override bool IsEnabled(string name, object? arg1, object? arg2 = null) /// Override abstract method /// [RequiresUnreferencedCode(WriteRequiresUnreferencedCode)] - [RequiresDynamicCode(WriteRequiresDynamicCode)] public override void Write(string name, object? value) { for (DiagnosticSubscription? curSubscription = _subscriptions; curSubscription != null; curSubscription = curSubscription.Next) diff --git a/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/DiagnosticSource.cs b/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/DiagnosticSource.cs index 47fad270bab4b4..89e62d4102c988 100644 --- a/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/DiagnosticSource.cs +++ b/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/DiagnosticSource.cs @@ -17,7 +17,6 @@ namespace System.Diagnostics public abstract partial class DiagnosticSource { internal const string WriteRequiresUnreferencedCode = "The type of object being written to DiagnosticSource cannot be discovered statically."; - internal const string WriteRequiresDynamicCode = "DiagnosticSource may require creating new generic types or methods, which requires creating code at runtime. This may not work when AOT compiling."; /// /// Write is a generic way of logging complex payloads. Each notification @@ -36,7 +35,6 @@ public abstract partial class DiagnosticSource /// An object that represent the value being passed as a payload for the event. /// This is often an anonymous type which contains several sub-values. [RequiresUnreferencedCode(WriteRequiresUnreferencedCode)] - [RequiresDynamicCode(WriteRequiresDynamicCode)] public abstract void Write(string name, object? value); /// diff --git a/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/DiagnosticSourceActivity.cs b/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/DiagnosticSourceActivity.cs index 1156ed5fe3ee62..5bf9fa66916692 100644 --- a/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/DiagnosticSourceActivity.cs +++ b/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/DiagnosticSourceActivity.cs @@ -26,7 +26,6 @@ public abstract partial class DiagnosticSource /// Started Activity for convenient chaining /// [RequiresUnreferencedCode(WriteRequiresUnreferencedCode)] - [RequiresDynamicCode(WriteRequiresDynamicCode)] public Activity StartActivity(Activity activity, object? args) { activity.Start(); @@ -45,7 +44,6 @@ public Activity StartActivity(Activity activity, object? args) /// An object that represent the value being passed as a payload for the event. /// [RequiresUnreferencedCode(WriteRequiresUnreferencedCode)] - [RequiresDynamicCode(WriteRequiresDynamicCode)] public void StopActivity(Activity activity, object? args) { // Stop sets the end time if it was unset, but we want it set before we issue the write diff --git a/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/DiagnosticSourceEventSource.cs b/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/DiagnosticSourceEventSource.cs index b1b78fedc80b85..73b3ed529f070a 100644 --- a/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/DiagnosticSourceEventSource.cs +++ b/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/DiagnosticSourceEventSource.cs @@ -1,5 +1,6 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System.Collections; using System.Collections.Concurrent; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; @@ -234,7 +235,8 @@ public void Message(string? Message) /// Events from DiagnosticSource can be forwarded to EventSource using this event. /// [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode", - Justification = "Arguments parameter is trimmer safe")] + Justification = "Arguments parameter is preserved by DynamicDependency")] + [DynamicDependency(DynamicallyAccessedMemberTypes.PublicProperties, typeof(KeyValuePair<,>))] [Event(2, Keywords = Keywords.Events)] private void Event(string SourceName, string EventName, IEnumerable>? Arguments) { @@ -255,7 +257,8 @@ private void EventJson(string SourceName, string EventName, string ArgmentsJson) /// Used to mark the beginning of an activity /// [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode", - Justification = "Arguments parameter is trimmer safe")] + Justification = "Arguments parameter is preserved by DynamicDependency")] + [DynamicDependency(DynamicallyAccessedMemberTypes.PublicProperties, typeof(KeyValuePair<,>))] [Event(4, Keywords = Keywords.Events)] private void Activity1Start(string SourceName, string EventName, IEnumerable> Arguments) { @@ -266,7 +269,8 @@ private void Activity1Start(string SourceName, string EventName, IEnumerable [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode", - Justification = "Arguments parameter is trimmer safe")] + Justification = "Arguments parameter is preserved by DynamicDependency")] + [DynamicDependency(DynamicallyAccessedMemberTypes.PublicProperties, typeof(KeyValuePair<,>))] [Event(5, Keywords = Keywords.Events)] private void Activity1Stop(string SourceName, string EventName, IEnumerable> Arguments) { @@ -277,7 +281,8 @@ private void Activity1Stop(string SourceName, string EventName, IEnumerable [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode", - Justification = "Arguments parameter is trimmer safe")] + Justification = "Arguments parameter is preserved by DynamicDependency")] + [DynamicDependency(DynamicallyAccessedMemberTypes.PublicProperties, typeof(KeyValuePair<,>))] [Event(6, Keywords = Keywords.Events)] private void Activity2Start(string SourceName, string EventName, IEnumerable> Arguments) { @@ -288,7 +293,8 @@ private void Activity2Start(string SourceName, string EventName, IEnumerable [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode", - Justification = "Arguments parameter is trimmer safe")] + Justification = "Arguments parameter is preserved by DynamicDependency")] + [DynamicDependency(DynamicallyAccessedMemberTypes.PublicProperties, typeof(KeyValuePair<,>))] [Event(7, Keywords = Keywords.Events)] private void Activity2Stop(string SourceName, string EventName, IEnumerable> Arguments) { @@ -299,7 +305,8 @@ private void Activity2Stop(string SourceName, string EventName, IEnumerable [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode", - Justification = "Arguments parameter is trimmer safe")] + Justification = "Arguments parameter is preserved by DynamicDependency")] + [DynamicDependency(DynamicallyAccessedMemberTypes.PublicProperties, typeof(KeyValuePair<,>))] [Event(8, Keywords = Keywords.Events, ActivityOptions = EventActivityOptions.Recursive)] private void RecursiveActivity1Start(string SourceName, string EventName, IEnumerable> Arguments) { @@ -310,7 +317,8 @@ private void RecursiveActivity1Start(string SourceName, string EventName, IEnume /// Used to mark the end of an activity that can be recursive. /// [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode", - Justification = "Arguments parameter is trimmer safe")] + Justification = "Arguments parameter is preserved by DynamicDependency")] + [DynamicDependency(DynamicallyAccessedMemberTypes.PublicProperties, typeof(KeyValuePair<,>))] [Event(9, Keywords = Keywords.Events, ActivityOptions = EventActivityOptions.Recursive)] private void RecursiveActivity1Stop(string SourceName, string EventName, IEnumerable> Arguments) { @@ -334,7 +342,8 @@ private void NewDiagnosticListener(string SourceName) /// The Activity name /// Name and value pairs of the Activity properties [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode", - Justification = "Arguments parameter is trimmer safe")] + Justification = "Arguments parameter is preserved by DynamicDependency")] + [DynamicDependency(DynamicallyAccessedMemberTypes.PublicProperties, typeof(KeyValuePair<,>))] [Event(11, Keywords = Keywords.Events, ActivityOptions = EventActivityOptions.Recursive)] private void ActivityStart(string SourceName, string ActivityName, IEnumerable> Arguments) => WriteEvent(11, SourceName, ActivityName, Arguments); @@ -346,7 +355,8 @@ private void ActivityStart(string SourceName, string ActivityName, IEnumerableThe Activity name /// Name and value pairs of the Activity properties [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode", - Justification = "Arguments parameter is trimmer safe")] + Justification = "Arguments parameter is preserved by DynamicDependency")] + [DynamicDependency(DynamicallyAccessedMemberTypes.PublicProperties, typeof(KeyValuePair<,>))] [Event(12, Keywords = Keywords.Events, ActivityOptions = EventActivityOptions.Recursive)] private void ActivityStop(string SourceName, string ActivityName, IEnumerable> Arguments) => WriteEvent(12, SourceName, ActivityName, Arguments); @@ -641,8 +651,6 @@ public FilterAndTransform(string filterAndPayloadSpec, int startIdx, int endIdx, [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2119", Justification = "DAM on EventSource references this compiler-generated local function which calls a " + "method that requires unreferenced code. EventSource will not access this local function.")] - [UnconditionalSuppressMessage("AotAnalysis", "IL3050:RequiresDynamicCode", - Justification = "DiagnosticSource.Write is marked with RequiresDynamicCode.")] void OnEventWritten(KeyValuePair evnt) { // The filter given to the DiagnosticSource may not work if users don't is 'IsEnabled' as expected. @@ -890,8 +898,6 @@ internal static void CreateActivityListener(DiagnosticSourceEventSource eventSou [DynamicDependency(nameof(TimeSpan.Ticks), typeof(TimeSpan))] [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode", Justification = "Activity's properties are being preserved with the DynamicDependencies on OnActivityStarted.")] - [UnconditionalSuppressMessage("AotAnalysis", "IL3050:RequiresDynamicCode", - Justification = "Activity is a reference type and is safe in aot.")] private static void OnActivityStarted(DiagnosticSourceEventSource eventSource, Activity activity) { FilterAndTransform? list = eventSource._activitySourceSpecs; @@ -911,8 +917,6 @@ private static void OnActivityStarted(DiagnosticSourceEventSource eventSource, A [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode", Justification = "Activity's properties are being preserved with the DynamicDependencies on OnActivityStarted.")] - [UnconditionalSuppressMessage("AotAnalysis", "IL3050:RequiresDynamicCode", - Justification = "Activity is a reference type and is safe with aot.")] private static void OnActivityStopped(DiagnosticSourceEventSource eventSource, Activity activity) { FilterAndTransform? list = eventSource._activitySourceSpecs; @@ -1015,7 +1019,6 @@ private void Dispose() Justification = "In EventSource, EnsureDescriptorsInitialized's use of GetType preserves this method which " + "requires unreferenced code, but EnsureDescriptorsInitialized does not access this member and is safe to call.")] [RequiresUnreferencedCode(DiagnosticSource.WriteRequiresUnreferencedCode)] - [RequiresDynamicCode(DiagnosticSource.WriteRequiresDynamicCode)] public List> Morph(object? args) { // Transform the args into a bag of key-value strings. @@ -1193,7 +1196,6 @@ public TransformSpec(string transformSpec, int startIdx, int endIdx, TransformSp Justification = "In EventSource, EnsureDescriptorsInitialized's use of GetType preserves this method which " + "requires unreferenced code, but EnsureDescriptorsInitialized does not access this member and is safe to call.")] [RequiresUnreferencedCode(DiagnosticSource.WriteRequiresUnreferencedCode)] - [RequiresDynamicCode(DiagnosticSource.WriteRequiresDynamicCode)] public KeyValuePair Morph(object? obj) { for (PropertySpec? cur = _fetches; cur != null; cur = cur.Next) @@ -1248,7 +1250,6 @@ public PropertySpec(string propertyName, PropertySpec? next) Justification = "In EventSource, EnsureDescriptorsInitialized's use of GetType preserves this method which " + "requires unreferenced code, but EnsureDescriptorsInitialized does not access this member and is safe to call.")] [RequiresUnreferencedCode(DiagnosticSource.WriteRequiresUnreferencedCode)] - [RequiresDynamicCode(DiagnosticSource.WriteRequiresDynamicCode)] public object? Fetch(object? obj) { PropertyFetch? fetch = _fetchForExpectedType; @@ -1295,7 +1296,6 @@ public PropertyFetch(Type? type) Justification = "In EventSource, EnsureDescriptorsInitialized's use of GetType preserves this method which " + "requires unreferenced code, but EnsureDescriptorsInitialized does not access this member and is safe to call.")] [RequiresUnreferencedCode(DiagnosticSource.WriteRequiresUnreferencedCode)] - [RequiresDynamicCode(DiagnosticSource.WriteRequiresDynamicCode)] public static PropertyFetch FetcherForProperty(Type? type, string propertyName) { if (propertyName == null) @@ -1319,10 +1319,7 @@ public static PropertyFetch FetcherForProperty(Type? type, string propertyName) continue; } - Type elemType = iFaceTypeInfo.GetGenericArguments()[0]; - Type instantiatedTypedPropertyFetcher = typeof(EnumeratePropertyFetch<>) - .GetTypeInfo().MakeGenericType(elemType); - return (PropertyFetch)Activator.CreateInstance(instantiatedTypedPropertyFetcher, type)!; + return CreateEnumeratePropertyFetch(type, iFaceTypeInfo); } // no implementation of IEnumerable found, return a null fetcher @@ -1355,20 +1352,50 @@ public static PropertyFetch FetcherForProperty(Type? type, string propertyName) Log.Message($"Property {propertyName} is static."); return new PropertyFetch(type); } - Type typedPropertyFetcher = typeInfo.IsValueType ? - typeof(ValueTypedFetchProperty<,>) : typeof(RefTypedFetchProperty<,>); - Type instantiatedTypedPropertyFetcher = typedPropertyFetcher.GetTypeInfo().MakeGenericType( - propertyInfo.DeclaringType!, propertyInfo.PropertyType); - return (PropertyFetch)Activator.CreateInstance(instantiatedTypedPropertyFetcher, type, propertyInfo)!; + + return CreatePropertyFetch(typeInfo, propertyInfo); } } + [UnconditionalSuppressMessage("AOT", "IL3050:RequiresDynamicCode", + Justification = "MakeGenericType is only called when IsDynamicCodeSupported is true or only with ref types.")] + private static PropertyFetch CreateEnumeratePropertyFetch(Type type, Type enumerableOfTType) + { + Type elemType = enumerableOfTType.GetGenericArguments()[0]; +#if NETCOREAPP + if (!RuntimeFeature.IsDynamicCodeSupported && elemType.IsValueType) + { + return new EnumeratePropertyFetch(type); + } +#endif + Type instantiatedTypedPropertyFetcher = typeof(EnumeratePropertyFetch<>) + .GetTypeInfo().MakeGenericType(elemType); + return (PropertyFetch)Activator.CreateInstance(instantiatedTypedPropertyFetcher, type)!; + } + + [UnconditionalSuppressMessage("AOT", "IL3050:RequiresDynamicCode", + Justification = "MakeGenericType is only called when IsDynamicCodeSupported is true or only with ref types.")] + private static PropertyFetch CreatePropertyFetch(Type type, PropertyInfo propertyInfo) + { +#if NETCOREAPP + if (!RuntimeFeature.IsDynamicCodeSupported && (propertyInfo.DeclaringType!.IsValueType || propertyInfo.PropertyType.IsValueType)) + { + return new ReflectionPropertyFetch(type, propertyInfo); + } +#endif + Type typedPropertyFetcher = type.IsValueType ? + typeof(ValueTypedFetchProperty<,>) : typeof(RefTypedFetchProperty<,>); + Type instantiatedTypedPropertyFetcher = typedPropertyFetcher.GetTypeInfo().MakeGenericType( + propertyInfo.DeclaringType!, propertyInfo.PropertyType); + return (PropertyFetch)Activator.CreateInstance(instantiatedTypedPropertyFetcher, type, propertyInfo)!; + } + /// /// Given an object, fetch the property that this propertyFech represents. /// public virtual object? Fetch(object? obj) { return null; } - #region private +#region private private sealed class RefTypedFetchProperty : PropertyFetch { @@ -1407,6 +1434,74 @@ public ValueTypedFetchProperty(Type type, PropertyInfo property) : base(type) private readonly StructFunc _propertyFetch; } +#if NETCOREAPP + /// + /// A fetcher that can be used when MakeGenericType isn't available. + /// + private sealed class ReflectionPropertyFetch : PropertyFetch + { + private readonly PropertyInfo _property; + public ReflectionPropertyFetch(Type type, PropertyInfo property) : base(type) + { + _property = property; + } + + public override object? Fetch(object? obj) => _property.GetValue(obj); + } + + /// + /// A fetcher that enumerates and formats an IEnumerable when MakeGenericType isn't available. + /// + private sealed class EnumeratePropertyFetch : PropertyFetch + { + public EnumeratePropertyFetch(Type type) : base(type) { } + + public override object? Fetch(object? obj) + { + IEnumerable? enumerable = obj as IEnumerable; + Debug.Assert(enumerable is not null); + + // string.Join for a non-generic IEnumerable + IEnumerator en = enumerable.GetEnumerator(); + using (IDisposable? disposable = en as IDisposable) + { + if (!en.MoveNext()) + { + return string.Empty; + } + + object? currentValue = en.Current; + string? firstString = currentValue?.ToString(); + + // If there's only 1 item, simply return the ToString of that + if (!en.MoveNext()) + { + // Only one value available + return firstString ?? string.Empty; + } + + var result = new ValueStringBuilder(stackalloc char[256]); + + result.Append(firstString); + + do + { + currentValue = en.Current; + + result.Append(","); + if (currentValue != null) + { + result.Append(currentValue.ToString()); + } + } + while (en.MoveNext()); + + return result.ToString(); + } + } + } +#endif + /// /// A fetcher that returns the result of Activity.Current /// @@ -1431,17 +1526,17 @@ public EnumeratePropertyFetch(Type type) : base(type) { } return string.Join(",", (IEnumerable)obj); } } - #endregion +#endregion } private readonly string _propertyName; private volatile PropertyFetch? _fetchForExpectedType; - #endregion +#endregion } private readonly string _outputName = null!; private readonly PropertySpec? _fetches; - #endregion +#endregion } /// @@ -1454,13 +1549,13 @@ internal sealed class CallbackObserver : IObserver { public CallbackObserver(Action callback) { _callback = callback; } - #region private +#region private public void OnCompleted() { } public void OnError(Exception error) { } public void OnNext(T value) { _callback(value); } private readonly Action _callback; - #endregion +#endregion } // A linked list of IObservable subscriptions (which are IDisposable). @@ -1477,11 +1572,11 @@ public Subscriptions(IDisposable subscription, Subscriptions? next) public Subscriptions? Next; } - #endregion +#endregion private FilterAndTransform? _specs; // Transformation specifications that indicate which sources/events are forwarded. private FilterAndTransform? _activitySourceSpecs; // ActivitySource Transformation specifications that indicate which sources/events are forwarded. private ActivityListener? _activityListener; - #endregion +#endregion } } diff --git a/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/HttpHandlerDiagnosticListener.cs b/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/HttpHandlerDiagnosticListener.cs index a35ea9ce5807d4..94d4192721ca54 100644 --- a/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/HttpHandlerDiagnosticListener.cs +++ b/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/HttpHandlerDiagnosticListener.cs @@ -25,7 +25,6 @@ namespace System.Diagnostics /// when it sees the System.Net.Http.Desktop source, subscribe to it. This will trigger the /// initialization of this DiagnosticListener. /// - [RequiresDynamicCode(WriteRequiresDynamicCode)] internal sealed class HttpHandlerDiagnosticListener : DiagnosticListener { /// @@ -204,7 +203,6 @@ public override void Remove(object key) /// intercept each new ServicePoint object being added to ServicePointManager.s_ServicePointTable /// and replace its ConnectionGroupList hashtable field. /// - [RequiresDynamicCode(WriteRequiresDynamicCode)] private sealed class ServicePointHashtable : HashtableWrapper { public ServicePointHashtable(Hashtable table) : base(table) @@ -245,7 +243,6 @@ public override object this[object key] /// intercept each new ConnectionGroup object being added to ServicePoint.m_ConnectionGroupList /// and replace its m_ConnectionList arraylist field. /// - [RequiresDynamicCode(WriteRequiresDynamicCode)] private sealed class ConnectionGroupHashtable : HashtableWrapper { public ConnectionGroupHashtable(Hashtable table) : base(table) @@ -485,7 +482,6 @@ public override void TrimToSize() /// intercept each new Connection object being added to ConnectionGroup.m_ConnectionList /// and replace its m_WriteList arraylist field. /// - [RequiresDynamicCode(WriteRequiresDynamicCode)] private sealed class ConnectionArrayList : ArrayListWrapper { public ConnectionArrayList(ArrayList list) : base(list) @@ -516,7 +512,6 @@ public override int Add(object value) /// It also intercepts all HttpWebRequest objects that are about to get removed from /// Connection.m_WriteList as they have completed the request. /// - [RequiresDynamicCode(WriteRequiresDynamicCode)] private sealed class HttpWebRequestArrayList : ArrayListWrapper { public HttpWebRequestArrayList(ArrayList list) : base(list) diff --git a/src/libraries/System.Diagnostics.DiagnosticSource/tests/NativeAotTests/DiagnosticSourceEventSourceTests.cs b/src/libraries/System.Diagnostics.DiagnosticSource/tests/NativeAotTests/DiagnosticSourceEventSourceTests.cs new file mode 100644 index 00000000000000..fab21b5a7b716c --- /dev/null +++ b/src/libraries/System.Diagnostics.DiagnosticSource/tests/NativeAotTests/DiagnosticSourceEventSourceTests.cs @@ -0,0 +1,110 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.Diagnostics.Tracing; + +/// +/// Tests that using writing to a DiagnosticSource writes the correct payloads +/// to the DiagnosticSourceEventSource. +/// +internal class Program +{ + private class TestEventListener : EventListener + { + public ReadOnlyCollection LogDataPayload { get; set; } + + protected override void OnEventSourceCreated(EventSource eventSource) + { + if (eventSource.Name == "Microsoft-Diagnostics-DiagnosticSource") + { + EnableEvents(eventSource, EventLevel.Verbose, EventKeywords.All, new Dictionary + { + { "FilterAndPayloadSpecs", "TestDiagnosticListener/Test.Start@Activity2Start:-Id;Ints.*Enumerate"} + }); + } + + base.OnEventSourceCreated(eventSource); + } + + protected override void OnEventWritten(EventWrittenEventArgs eventData) + { + if (eventData.EventName == "Activity2Start") + { + LogDataPayload = eventData.Payload; + } + + base.OnEventWritten(eventData); + } + } + + public static int Main() + { + DiagnosticSource diagnosticSource = new DiagnosticListener("TestDiagnosticListener"); + using (var listener = new TestEventListener()) + { + var data = new EventData() + { + Id = Guid.NewGuid(), + }; + + Write(diagnosticSource, "Test.Start", data); + + if (!(listener.LogDataPayload?.Count == 3 && + (string)listener.LogDataPayload[0] == "TestDiagnosticListener" && + (string)listener.LogDataPayload[1] == "Test.Start")) + { + return -1; + } + + object[] args = (object[])listener.LogDataPayload[2]; + if (args.Length != 2) + { + return -2; + } + + IDictionary arg = (IDictionary)args[0]; + if (!((string)arg["Key"] == "Id" && (string)arg["Value"] == data.Id.ToString())) + { + return -3; + } + + arg = (IDictionary)args[1]; + if (!((string)arg["Key"] == "*Enumerate" && (string)arg["Value"] == "1,2,3")) + { + return -4; + } + + return 100; + } + } + + [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode", + Justification = "The value being passed into Write has the necessary properties being preserved with DynamicallyAccessedMembers.")] + private static void Write<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties)] T>( + DiagnosticSource diagnosticSource, + string name, + T value) + { + diagnosticSource.Write(name, value); + } + + public class EventData + { + public Guid Id { get; set; } + + public IEnumerable Ints + { + get + { + yield return 1; + yield return 2; + yield return 3; + } + } + } +} diff --git a/src/libraries/System.Diagnostics.DiagnosticSource/tests/NativeAotTests/System.Diagnostics.DiagnosticSource.NativeAotTests.proj b/src/libraries/System.Diagnostics.DiagnosticSource/tests/NativeAotTests/System.Diagnostics.DiagnosticSource.NativeAotTests.proj new file mode 100644 index 00000000000000..8001203352b581 --- /dev/null +++ b/src/libraries/System.Diagnostics.DiagnosticSource/tests/NativeAotTests/System.Diagnostics.DiagnosticSource.NativeAotTests.proj @@ -0,0 +1,10 @@ + + + + + + + + + diff --git a/src/libraries/tests.proj b/src/libraries/tests.proj index 6ea3f13a2200b7..2220c89cedced0 100644 --- a/src/libraries/tests.proj +++ b/src/libraries/tests.proj @@ -4,7 +4,7 @@ $([MSBuild]::ValueOrDefault('$(BuildTargetFramework)', '$(NetCoreAppCurrent)'))-$(TargetOS) - true + true @@ -18,6 +18,7 @@ true false false + false false @@ -590,6 +591,11 @@ Condition="'$(TestTrimming)' == 'true'" AdditionalProperties="%(AdditionalProperties);SkipTrimmingProjectsRestore=true" /> + + - - - + - + -