Skip to content

Commit 6578f25

Browse files
authored
Resolve ILLink warnings in System.Diagnostics.DiagnosticSource (#50265)
* Resolve ILLink warnings in System.Diagnostics.DiagnosticSource Contributes to #45623 1. Mark DiagnosticSource.Write(string,object) as RequiresUnreferencedCode 2. Suppress the warnings for any .NET libraries that call the DiagnosticSource.Write() API, and annotate the .NET types being passed in to preserve their important properties. - This was done for HttpClient. ASP.NET and EF will need separate changes when those assemblies are made trim compatible 3. Annotate Activity and its small closure of types (ActivityLink, ActivityEvent, ActivityContext, etc) to ensure none of those properties are trimmed. 4. Suppress trim warnings inside DiagnosticSourceEventSource since the public Write method is marked with RequiresUnreferencedCode.
1 parent bc7cf1c commit 6578f25

10 files changed

+131
-57
lines changed

Diff for: src/libraries/System.Diagnostics.DiagnosticSource/ref/System.Diagnostics.DiagnosticSource.cs

+2
Original file line numberDiff line numberDiff line change
@@ -19,13 +19,15 @@ public virtual void Dispose() { }
1919
public virtual System.IDisposable Subscribe(System.IObserver<System.Collections.Generic.KeyValuePair<string, object?>> observer, System.Func<string, object?, object?, bool>? isEnabled) { throw null; }
2020
public virtual System.IDisposable Subscribe(System.IObserver<System.Collections.Generic.KeyValuePair<string, object?>> observer, System.Predicate<string>? isEnabled) { throw null; }
2121
public override string ToString() { throw null; }
22+
[System.Diagnostics.CodeAnalysis.RequiresUnreferencedCode("The type of object being written to DiagnosticSource cannot be discovered statically.")]
2223
public override void Write(string name, object? value) { }
2324
}
2425
public abstract partial class DiagnosticSource
2526
{
2627
protected DiagnosticSource() { }
2728
public abstract bool IsEnabled(string name);
2829
public virtual bool IsEnabled(string name, object? arg1, object? arg2 = null) { throw null; }
30+
[System.Diagnostics.CodeAnalysis.RequiresUnreferencedCode("The type of object being written to DiagnosticSource cannot be discovered statically.")]
2931
public abstract void Write(string name, object? value);
3032
}
3133
}

Diff for: src/libraries/System.Diagnostics.DiagnosticSource/ref/System.Diagnostics.DiagnosticSource.csproj

+1
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
</PropertyGroup>
1313
<ItemGroup>
1414
<Compile Include="System.Diagnostics.DiagnosticSource.cs" />
15+
<Compile Include="$(CoreLibSharedDir)System\Diagnostics\CodeAnalysis\RequiresUnreferencedCodeAttribute.cs" />
1516
</ItemGroup>
1617
<ItemGroup Condition="'$(TargetFramework)' != 'netstandard1.1'">
1718
<Compile Include="System.Diagnostics.DiagnosticSourceActivity.cs" />

Diff for: src/libraries/System.Diagnostics.DiagnosticSource/ref/System.Diagnostics.DiagnosticSourceActivity.cs

+2
Original file line numberDiff line numberDiff line change
@@ -192,7 +192,9 @@ public abstract partial class DiagnosticSource
192192
{
193193
public virtual void OnActivityExport(System.Diagnostics.Activity activity, object? payload) { }
194194
public virtual void OnActivityImport(System.Diagnostics.Activity activity, object? payload) { }
195+
[System.Diagnostics.CodeAnalysis.RequiresUnreferencedCode("The type of object being written to DiagnosticSource cannot be discovered statically.")]
195196
public System.Diagnostics.Activity StartActivity(System.Diagnostics.Activity activity, object? args) { throw null; }
197+
[System.Diagnostics.CodeAnalysis.RequiresUnreferencedCode("The type of object being written to DiagnosticSource cannot be discovered statically.")]
196198
public void StopActivity(System.Diagnostics.Activity activity, object? args) { }
197199
}
198200
public enum ActivitySamplingResult

Diff for: src/libraries/System.Diagnostics.DiagnosticSource/src/ILLink/ILLink.Suppressions.xml

-17
This file was deleted.

Diff for: src/libraries/System.Diagnostics.DiagnosticSource/src/System.Diagnostics.DiagnosticSource.csproj

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
<Project Sdk="Microsoft.NET.Sdk">
1+
<Project Sdk="Microsoft.NET.Sdk">
22
<PropertyGroup>
33
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
44
<CLSCompliant>false</CLSCompliant>
@@ -31,6 +31,7 @@
3131
<ItemGroup Condition="$([MSBuild]::GetTargetFrameworkIdentifier('$(TargetFramework)')) != '.NETCoreApp'">
3232
<Compile Include="$(CoreLibSharedDir)System\Diagnostics\CodeAnalysis\DynamicDependencyAttribute.cs" />
3333
<Compile Include="$(CoreLibSharedDir)System\Diagnostics\CodeAnalysis\DynamicallyAccessedMemberTypes.cs" />
34+
<Compile Include="$(CoreLibSharedDir)System\Diagnostics\CodeAnalysis\RequiresUnreferencedCodeAttribute.cs" />
3435
<Compile Include="$(CoreLibSharedDir)System\Diagnostics\CodeAnalysis\UnconditionalSuppressMessageAttribute.cs" />
3536
</ItemGroup>
3637
<ItemGroup Condition="'$(TargetFramework)' != 'netstandard1.1'">

Diff for: src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/DiagnosticListener.cs

+2
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
using System.Threading;
55
using System.Collections.Generic;
6+
using System.Diagnostics.CodeAnalysis;
67

78
namespace System.Diagnostics
89
{
@@ -255,6 +256,7 @@ public override bool IsEnabled(string name, object? arg1, object? arg2 = null)
255256
/// <summary>
256257
/// Override abstract method
257258
/// </summary>
259+
[RequiresUnreferencedCode(WriteRequiresUnreferencedCode)]
258260
public override void Write(string name, object? value)
259261
{
260262
for (DiagnosticSubscription? curSubscription = _subscriptions; curSubscription != null; curSubscription = curSubscription.Next)

Diff for: src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/DiagnosticSource.cs

+4
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
// The .NET Foundation licenses this file to you under the MIT license.
33

44
using System;
5+
using System.Diagnostics.CodeAnalysis;
56

67
namespace System.Diagnostics
78
{
@@ -15,6 +16,8 @@ namespace System.Diagnostics
1516
/// </summary>
1617
public abstract partial class DiagnosticSource
1718
{
19+
internal const string WriteRequiresUnreferencedCode = "The type of object being written to DiagnosticSource cannot be discovered statically.";
20+
1821
/// <summary>
1922
/// Write is a generic way of logging complex payloads. Each notification
2023
/// is given a name, which identifies it as well as a object (typically an anonymous type)
@@ -31,6 +34,7 @@ public abstract partial class DiagnosticSource
3134
/// <param name="name">The name of the event being written.</param>
3235
/// <param name="value">An object that represent the value being passed as a payload for the event.
3336
/// This is often an anonymous type which contains several sub-values.</param>
37+
[RequiresUnreferencedCode(WriteRequiresUnreferencedCode)]
3438
public abstract void Write(string name, object? value);
3539

3640
/// <summary>

Diff for: src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/DiagnosticSourceActivity.cs

+3
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
// The .NET Foundation licenses this file to you under the MIT license.
33

44
using System.Collections.Generic;
5+
using System.Diagnostics.CodeAnalysis;
56

67
namespace System.Diagnostics
78
{
@@ -24,6 +25,7 @@ public abstract partial class DiagnosticSource
2425
/// <param name="args">An object that represent the value being passed as a payload for the event.</param>
2526
/// <returns>Started Activity for convenient chaining</returns>
2627
/// <seealso cref="Activity"/>
28+
[RequiresUnreferencedCode(WriteRequiresUnreferencedCode)]
2729
public Activity StartActivity(Activity activity, object? args)
2830
{
2931
activity.Start();
@@ -41,6 +43,7 @@ public Activity StartActivity(Activity activity, object? args)
4143
/// <param name="activity">Activity to be stopped</param>
4244
/// <param name="args">An object that represent the value being passed as a payload for the event.</param>
4345
/// <seealso cref="Activity"/>
46+
[RequiresUnreferencedCode(WriteRequiresUnreferencedCode)]
4447
public void StopActivity(Activity activity, object? args)
4548
{
4649
// Stop sets the end time if it was unset, but we want it set before we issue the write

Diff for: src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/DiagnosticSourceEventSource.cs

+58-34
Original file line numberDiff line numberDiff line change
@@ -717,7 +717,9 @@ public FilterAndTransform(string filterAndPayloadSpec, int startIdx, int endIdx,
717717
if (eventNameFilter != null)
718718
eventNameFilterPredicate = (string eventName) => eventNameFilter == eventName;
719719

720-
var subscription = newListener.Subscribe(new CallbackObserver<KeyValuePair<string, object?>>(delegate (KeyValuePair<string, object?> evnt)
720+
[UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode",
721+
Justification = "DiagnosticSource.Write is marked with RequiresUnreferencedCode.")]
722+
void OnEventWritten(KeyValuePair<string, object?> evnt)
721723
{
722724
// The filter given to the DiagnosticSource may not work if users don't is 'IsEnabled' as expected.
723725
// Thus we look for any events that may have snuck through and filter them out before forwarding.
@@ -727,7 +729,9 @@ public FilterAndTransform(string filterAndPayloadSpec, int startIdx, int endIdx,
727729
var outputArgs = this.Morph(evnt.Value);
728730
var eventName = evnt.Key;
729731
writeEvent(newListener.Name, eventName, outputArgs);
730-
}), eventNameFilterPredicate);
732+
}
733+
734+
var subscription = newListener.Subscribe(new CallbackObserver<KeyValuePair<string, object?>>(OnEventWritten), eventNameFilterPredicate);
731735
_liveSubscriptions = new Subscriptions(subscription, _liveSubscriptions);
732736
}
733737
}));
@@ -948,41 +952,55 @@ internal static void CreateActivityListener(DiagnosticSourceEventSource eventSou
948952
return false;
949953
};
950954

951-
eventSource._activityListener.ActivityStarted = activity =>
955+
eventSource._activityListener.ActivityStarted = activity => OnActivityStarted(eventSource, activity);
956+
957+
eventSource._activityListener.ActivityStopped = activity => OnActivityStopped(eventSource, activity);
958+
959+
ActivitySource.AddActivityListener(eventSource._activityListener);
960+
}
961+
962+
[DynamicDependency(DynamicallyAccessedMemberTypes.PublicProperties, typeof(Activity))]
963+
[DynamicDependency(DynamicallyAccessedMemberTypes.PublicProperties, typeof(ActivityContext))]
964+
[DynamicDependency(DynamicallyAccessedMemberTypes.PublicProperties, typeof(ActivityEvent))]
965+
[DynamicDependency(DynamicallyAccessedMemberTypes.PublicProperties, typeof(ActivityLink))]
966+
[DynamicDependency(nameof(DateTime.Ticks), typeof(DateTime))]
967+
[DynamicDependency(nameof(TimeSpan.Ticks), typeof(TimeSpan))]
968+
[UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode",
969+
Justification = "Activity's properties are being preserved with the DynamicDependencies on OnActivityStarted.")]
970+
private static void OnActivityStarted(DiagnosticSourceEventSource eventSource, Activity activity)
971+
{
972+
FilterAndTransform? list = eventSource._activitySourceSpecs;
973+
while (list != null)
952974
{
953-
FilterAndTransform? list = eventSource._activitySourceSpecs;
954-
while (list != null)
975+
if ((list.Events & ActivityEvents.ActivityStart) != 0 &&
976+
(activity.Source.Name == list.SourceName || list.SourceName == "*") &&
977+
(list.ActivityName == null || list.ActivityName == activity.OperationName))
955978
{
956-
if ((list.Events & ActivityEvents.ActivityStart) != 0 &&
957-
(activity.Source.Name == list.SourceName || list.SourceName == "*") &&
958-
(list.ActivityName == null || list.ActivityName == activity.OperationName))
959-
{
960-
eventSource.ActivityStart(activity.Source.Name, activity.OperationName, list.Morph(activity));
961-
return;
962-
}
963-
964-
list = list.Next;
979+
eventSource.ActivityStart(activity.Source.Name, activity.OperationName, list.Morph(activity));
980+
return;
965981
}
966-
};
967982

968-
eventSource._activityListener.ActivityStopped = activity =>
983+
list = list.Next;
984+
}
985+
}
986+
987+
[UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode",
988+
Justification = "Activity's properties are being preserved with the DynamicDependencies on OnActivityStarted.")]
989+
private static void OnActivityStopped(DiagnosticSourceEventSource eventSource, Activity activity)
990+
{
991+
FilterAndTransform? list = eventSource._activitySourceSpecs;
992+
while (list != null)
969993
{
970-
FilterAndTransform? list = eventSource._activitySourceSpecs;
971-
while (list != null)
994+
if ((list.Events & ActivityEvents.ActivityStop) != 0 &&
995+
(activity.Source.Name == list.SourceName || list.SourceName == "*") &&
996+
(list.ActivityName == null || list.ActivityName == activity.OperationName))
972997
{
973-
if ((list.Events & ActivityEvents.ActivityStop) != 0 &&
974-
(activity.Source.Name == list.SourceName || list.SourceName == "*") &&
975-
(list.ActivityName == null || list.ActivityName == activity.OperationName))
976-
{
977-
eventSource.ActivityStop(activity.Source.Name, activity.OperationName, list.Morph(activity));
978-
return;
979-
}
980-
981-
list = list.Next;
998+
eventSource.ActivityStop(activity.Source.Name, activity.OperationName, list.Morph(activity));
999+
return;
9821000
}
983-
};
9841001

985-
ActivitySource.AddActivityListener(eventSource._activityListener);
1002+
list = list.Next;
1003+
}
9861004
}
9871005

9881006
// Move all wildcard nodes at the end of the list.
@@ -1067,6 +1085,7 @@ private void Dispose()
10671085
}
10681086
}
10691087

1088+
[RequiresUnreferencedCode(DiagnosticSource.WriteRequiresUnreferencedCode)]
10701089
public List<KeyValuePair<string, string?>> Morph(object? args)
10711090
{
10721091
// Transform the args into a bag of key-value strings.
@@ -1105,7 +1124,11 @@ private void Dispose()
11051124
Interlocked.CompareExchange(ref _implicitTransformsTable,
11061125
new ConcurrentDictionary<Type, TransformSpec?>(1, 8), null);
11071126
}
1108-
implicitTransforms = _implicitTransformsTable.GetOrAdd(argType, type => MakeImplicitTransforms(type));
1127+
implicitTransforms = _implicitTransformsTable.GetOrAdd(argType, type => MakeImplicitTransformsWrapper(type));
1128+
1129+
[UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode",
1130+
Justification = "The Morph method has RequiresUnreferencedCode, but the trimmer can't see through lamdba calls.")]
1131+
static TransformSpec? MakeImplicitTransformsWrapper(Type transformType) => MakeImplicitTransforms(transformType);
11091132
}
11101133

11111134
// implicitTransformas now fetched from cache or constructed, use it to Fetch all the implicit fields.
@@ -1145,6 +1168,7 @@ private void Dispose()
11451168

11461169
// Given a type generate all the implicit transforms for type (that is for every field
11471170
// generate the spec that fetches it).
1171+
[RequiresUnreferencedCode(DiagnosticSource.WriteRequiresUnreferencedCode)]
11481172
private static TransformSpec? MakeImplicitTransforms(Type type)
11491173
{
11501174
TransformSpec? newSerializableArgs = null;
@@ -1239,6 +1263,7 @@ public TransformSpec(string transformSpec, int startIdx, int endIdx, TransformSp
12391263
/// if the spec is OUTSTR=EVENT_VALUE.PROP1.PROP2.PROP3 and the ultimate value of PROP3 is
12401264
/// 10 then the return key value pair is KeyValuePair("OUTSTR","10")
12411265
/// </summary>
1266+
[RequiresUnreferencedCode(DiagnosticSource.WriteRequiresUnreferencedCode)]
12421267
public KeyValuePair<string, string?> Morph(object? obj)
12431268
{
12441269
for (PropertySpec? cur = _fetches; cur != null; cur = cur.Next)
@@ -1289,6 +1314,7 @@ public PropertySpec(string propertyName, PropertySpec? next)
12891314
/// Given an object fetch the property that this PropertySpec represents.
12901315
/// obj may be null when IsStatic is true, otherwise it must be non-null.
12911316
/// </summary>
1317+
[RequiresUnreferencedCode(DiagnosticSource.WriteRequiresUnreferencedCode)]
12921318
public object? Fetch(object? obj)
12931319
{
12941320
PropertyFetch? fetch = _fetchForExpectedType;
@@ -1331,9 +1357,7 @@ public PropertyFetch(Type? type)
13311357
/// <summary>
13321358
/// Create a property fetcher for a propertyName
13331359
/// </summary>
1334-
[DynamicDependency("#ctor(System.Type)", typeof(EnumeratePropertyFetch<>))]
1335-
[DynamicDependency("#ctor(System.Type,System.Reflection.PropertyInfo)", typeof(RefTypedFetchProperty<,>))]
1336-
[DynamicDependency("#ctor(System.Type,System.Reflection.PropertyInfo)", typeof(ValueTypedFetchProperty<,>))]
1360+
[RequiresUnreferencedCode(DiagnosticSource.WriteRequiresUnreferencedCode)]
13371361
public static PropertyFetch FetcherForProperty(Type? type, string propertyName)
13381362
{
13391363
if (propertyName == null)
@@ -1385,7 +1409,7 @@ public static PropertyFetch FetcherForProperty(Type? type, string propertyName)
13851409
PropertyInfo? propertyInfo = typeInfo.GetDeclaredProperty(propertyName);
13861410
if (propertyInfo == null)
13871411
{
1388-
Logger.Message($"Property {propertyName} not found on {type}");
1412+
Logger.Message($"Property {propertyName} not found on {type}. Ensure the name is spelled correctly. If you published the application with PublishTrimmed=true, ensure the property was not trimmed away.");
13891413
return new PropertyFetch(type);
13901414
}
13911415
// Delegate creation below is incompatible with static properties.

0 commit comments

Comments
 (0)