diff --git a/src/coreclr/System.Private.CoreLib/CompatibilitySuppressions.xml b/src/coreclr/System.Private.CoreLib/CompatibilitySuppressions.xml index 6a201651f126a8..276be3a543e343 100644 --- a/src/coreclr/System.Private.CoreLib/CompatibilitySuppressions.xml +++ b/src/coreclr/System.Private.CoreLib/CompatibilitySuppressions.xml @@ -1,3 +1,45 @@  + + CP0015 + M:System.Diagnostics.Tracing.EventSource.Write``1(System.String,``0):[T:System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute] + ref/net8.0/System.Private.CoreLib.dll + lib/net8.0/System.Private.CoreLib.dll + + + CP0015 + M:System.Diagnostics.Tracing.EventSource.Write``1(System.String,System.Diagnostics.Tracing.EventSourceOptions@,``0@):[T:System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute] + ref/net8.0/System.Private.CoreLib.dll + lib/net8.0/System.Private.CoreLib.dll + + + CP0015 + M:System.Diagnostics.Tracing.EventSource.Write``1(System.String,System.Diagnostics.Tracing.EventSourceOptions@,System.Guid@,System.Guid@,``0@):[T:System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute] + ref/net8.0/System.Private.CoreLib.dll + lib/net8.0/System.Private.CoreLib.dll + + + CP0015 + M:System.Diagnostics.Tracing.EventSource.WriteEvent(System.Int32,System.Object[]):[T:System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute] + ref/net8.0/System.Private.CoreLib.dll + lib/net8.0/System.Private.CoreLib.dll + + + CP0015 + M:System.Diagnostics.Tracing.EventSource.WriteEventCore(System.Int32,System.Int32,System.Diagnostics.Tracing.EventSource.EventData*):[T:System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute] + ref/net8.0/System.Private.CoreLib.dll + lib/net8.0/System.Private.CoreLib.dll + + + CP0015 + M:System.Diagnostics.Tracing.EventSource.WriteEventWithRelatedActivityId(System.Int32,System.Guid,System.Object[]):[T:System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute] + ref/net8.0/System.Private.CoreLib.dll + lib/net8.0/System.Private.CoreLib.dll + + + CP0015 + M:System.Diagnostics.Tracing.EventSource.WriteEventWithRelatedActivityIdCore(System.Int32,System.Guid*,System.Int32,System.Diagnostics.Tracing.EventSource.EventData*):[T:System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute] + ref/net8.0/System.Private.CoreLib.dll + lib/net8.0/System.Private.CoreLib.dll + \ No newline at end of file diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/CompatibilitySuppressions.xml b/src/coreclr/nativeaot/System.Private.CoreLib/src/CompatibilitySuppressions.xml index 0883ff244559c5..c7905dd7141c63 100644 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/CompatibilitySuppressions.xml +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/CompatibilitySuppressions.xml @@ -964,8 +964,50 @@ CP0014 M:System.Runtime.CompilerServices.RuntimeHelpers.GetObjectValue(System.Object)->object?:[T:System.Diagnostics.CodeAnalysis.NotNullIfNotNullAttribute] + + CP0015 + M:System.Diagnostics.Tracing.EventSource.Write``1(System.String,``0):[T:System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute] + ref/net8.0/System.Private.CoreLib.dll + lib/net8.0/System.Private.CoreLib.dll + + + CP0015 + M:System.Diagnostics.Tracing.EventSource.Write``1(System.String,System.Diagnostics.Tracing.EventSourceOptions@,``0@):[T:System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute] + ref/net8.0/System.Private.CoreLib.dll + lib/net8.0/System.Private.CoreLib.dll + + + CP0015 + M:System.Diagnostics.Tracing.EventSource.Write``1(System.String,System.Diagnostics.Tracing.EventSourceOptions@,System.Guid@,System.Guid@,``0@):[T:System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute] + ref/net8.0/System.Private.CoreLib.dll + lib/net8.0/System.Private.CoreLib.dll + + + CP0015 + M:System.Diagnostics.Tracing.EventSource.WriteEvent(System.Int32,System.Object[]):[T:System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute] + ref/net8.0/System.Private.CoreLib.dll + lib/net8.0/System.Private.CoreLib.dll + + + CP0015 + M:System.Diagnostics.Tracing.EventSource.WriteEventCore(System.Int32,System.Int32,System.Diagnostics.Tracing.EventSource.EventData*):[T:System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute] + ref/net8.0/System.Private.CoreLib.dll + lib/net8.0/System.Private.CoreLib.dll + + + CP0015 + M:System.Diagnostics.Tracing.EventSource.WriteEventWithRelatedActivityId(System.Int32,System.Guid,System.Object[]):[T:System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute] + ref/net8.0/System.Private.CoreLib.dll + lib/net8.0/System.Private.CoreLib.dll + + + CP0015 + M:System.Diagnostics.Tracing.EventSource.WriteEventWithRelatedActivityIdCore(System.Int32,System.Guid*,System.Int32,System.Diagnostics.Tracing.EventSource.EventData*):[T:System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute] + ref/net8.0/System.Private.CoreLib.dll + lib/net8.0/System.Private.CoreLib.dll + CP0016 M:System.Runtime.InteropServices.Marshal.GetObjectForNativeVariant``1(System.IntPtr)->T?:[T:System.Diagnostics.CodeAnalysis.MaybeNullAttribute] - + \ No newline at end of file diff --git a/src/libraries/Common/src/System/Net/Security/NetEventSource.Security.Windows.cs b/src/libraries/Common/src/System/Net/Security/NetEventSource.Security.Windows.cs index ba669fa717a1a9..b5a91ab8037fe8 100644 --- a/src/libraries/Common/src/System/Net/Security/NetEventSource.Security.Windows.cs +++ b/src/libraries/Common/src/System/Net/Security/NetEventSource.Security.Windows.cs @@ -28,8 +28,6 @@ public void EnumerateSecurityPackages(string? securityPackage) => public void SspiPackageNotFound(string packageName) => WriteEvent(SspiPackageNotFoundId, packageName); - [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode", - Justification = "parameter intent is an enum and is trimmer safe")] [Event(AcquireDefaultCredentialId, Keywords = Keywords.Default, Level = EventLevel.Informational)] public void AcquireDefaultCredential(string packageName, Interop.SspiCli.CredentialUse intent) => WriteEvent(AcquireDefaultCredentialId, packageName, intent); @@ -58,8 +56,6 @@ public void AcceptSecurityContext(SafeFreeCredentials? credential, SafeDeleteCon private void AcceptSecurityContext(string credential, string context, Interop.SspiCli.ContextFlags inFlags) => WriteEvent(AcceptSecuritContextId, credential, context, (int)inFlags); - [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode", - Justification = "parameter errorCode is an enum and is trimmer safe")] [Event(OperationReturnedSomethingId, Keywords = Keywords.Default, Level = EventLevel.Informational, Message = "{0} returned {1}.")] public void OperationReturnedSomething(string operation, Interop.SECURITY_STATUS errorCode) => WriteEvent(OperationReturnedSomethingId, operation, errorCode); diff --git a/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/Metrics/MetricsEventSource.cs b/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/Metrics/MetricsEventSource.cs index 245a1e6e76ba79..85231812401a3b 100644 --- a/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/Metrics/MetricsEventSource.cs +++ b/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/Metrics/MetricsEventSource.cs @@ -87,40 +87,50 @@ public void Message(string? Message) } [Event(2, Keywords = Keywords.TimeSeriesValues)] +#if !NET8_0_OR_GREATER [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode", - Justification = "This calls WriteEvent with all primitive arguments which is safe. Primitives are always serialized properly.")] + Justification = "This calls WriteEvent with all primitive arguments which is safe. Primitives are always serialized properly.")] +#endif public void CollectionStart(string sessionId, DateTime intervalStartTime, DateTime intervalEndTime) { WriteEvent(2, sessionId, intervalStartTime, intervalEndTime); } [Event(3, Keywords = Keywords.TimeSeriesValues)] +#if !NET8_0_OR_GREATER [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode", - Justification = "This calls WriteEvent with all primitive arguments which is safe. Primitives are always serialized properly.")] + Justification = "This calls WriteEvent with all primitive arguments which is safe. Primitives are always serialized properly.")] +#endif public void CollectionStop(string sessionId, DateTime intervalStartTime, DateTime intervalEndTime) { WriteEvent(3, sessionId, intervalStartTime, intervalEndTime); } [Event(4, Keywords = Keywords.TimeSeriesValues, Version=1)] +#if !NET8_0_OR_GREATER [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode", - Justification = "This calls WriteEvent with all primitive arguments which is safe. Primitives are always serialized properly.")] + Justification = "This calls WriteEvent with all primitive arguments which is safe. Primitives are always serialized properly.")] +#endif public void CounterRateValuePublished(string sessionId, string meterName, string? meterVersion, string instrumentName, string? unit, string tags, string rate, string value) { WriteEvent(4, sessionId, meterName, meterVersion ?? "", instrumentName, unit ?? "", tags, rate, value); } [Event(5, Keywords = Keywords.TimeSeriesValues)] +#if !NET8_0_OR_GREATER [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode", - Justification = "This calls WriteEvent with all primitive arguments which is safe. Primitives are always serialized properly.")] + Justification = "This calls WriteEvent with all primitive arguments which is safe. Primitives are always serialized properly.")] +#endif public void GaugeValuePublished(string sessionId, string meterName, string? meterVersion, string instrumentName, string? unit, string tags, string lastValue) { WriteEvent(5, sessionId, meterName, meterVersion ?? "", instrumentName, unit ?? "", tags, lastValue); } [Event(6, Keywords = Keywords.TimeSeriesValues, Version=1)] +#if !NET8_0_OR_GREATER [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode", - Justification = "This calls WriteEvent with all primitive arguments which is safe. Primitives are always serialized properly.")] + Justification = "This calls WriteEvent with all primitive arguments which is safe. Primitives are always serialized properly.")] +#endif public void HistogramValuePublished(string sessionId, string meterName, string? meterVersion, string instrumentName, string? unit, string tags, string quantiles, int count, double sum) { WriteEvent(6, sessionId, meterName, meterVersion ?? "", instrumentName, unit ?? "", tags, quantiles, count, sum); @@ -130,8 +140,10 @@ public void HistogramValuePublished(string sessionId, string meterName, string? // or because an instrument matching the pre-existing filter has just been created. This event precedes all *MetricPublished events // for the same named instrument. [Event(7, Keywords = Keywords.TimeSeriesValues)] +#if !NET8_0_OR_GREATER [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode", - Justification = "This calls WriteEvent with all primitive arguments which is safe. Primitives are always serialized properly.")] + Justification = "This calls WriteEvent with all primitive arguments which is safe. Primitives are always serialized properly.")] +#endif public void BeginInstrumentReporting(string sessionId, string meterName, string? meterVersion, string instrumentName, string instrumentType, string? unit, string? description) { WriteEvent(7, sessionId, meterName, meterVersion ?? "", instrumentName, instrumentType, unit ?? "", description ?? ""); @@ -140,8 +152,10 @@ public void BeginInstrumentReporting(string sessionId, string meterName, string? // Sent when we stop monitoring the value of a intrument, either because new session filter arguments changed subscriptions // or because the Meter has been disposed. [Event(8, Keywords = Keywords.TimeSeriesValues)] +#if !NET8_0_OR_GREATER [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode", - Justification = "This calls WriteEvent with all primitive arguments which is safe. Primitives are always serialized properly.")] + Justification = "This calls WriteEvent with all primitive arguments which is safe. Primitives are always serialized properly.")] +#endif public void EndInstrumentReporting(string sessionId, string meterName, string? meterVersion, string instrumentName, string instrumentType, string? unit, string? description) { WriteEvent(8, sessionId, meterName, meterVersion ?? "", instrumentName, instrumentType, unit ?? "", description ?? ""); @@ -160,8 +174,10 @@ public void InitialInstrumentEnumerationComplete(string sessionId) } [Event(11, Keywords = Keywords.InstrumentPublishing)] +#if !NET8_0_OR_GREATER [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode", - Justification = "This calls WriteEvent with all primitive arguments which is safe. Primitives are always serialized properly.")] + Justification = "This calls WriteEvent with all primitive arguments which is safe. Primitives are always serialized properly.")] +#endif public void InstrumentPublished(string sessionId, string meterName, string? meterVersion, string instrumentName, string instrumentType, string? unit, string? description) { WriteEvent(11, sessionId, meterName, meterVersion ?? "", instrumentName, instrumentType, unit ?? "", description ?? ""); @@ -192,8 +208,10 @@ public void MultipleSessionsNotSupportedError(string runningSessionId) } [Event(16, Keywords = Keywords.TimeSeriesValues, Version=1)] +#if !NET8_0_OR_GREATER [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode", - Justification = "This calls WriteEvent with all primitive arguments which is safe. Primitives are always serialized properly.")] + Justification = "This calls WriteEvent with all primitive arguments which is safe. Primitives are always serialized properly.")] +#endif public void UpDownCounterRateValuePublished(string sessionId, string meterName, string? meterVersion, string instrumentName, string? unit, string tags, string rate, string value) { WriteEvent(16, sessionId, meterName, meterVersion ?? "", instrumentName, unit ?? "", tags, rate, value); diff --git a/src/libraries/System.Diagnostics.Tracing/ref/System.Diagnostics.Tracing.cs b/src/libraries/System.Diagnostics.Tracing/ref/System.Diagnostics.Tracing.cs index dacf67b8841e3b..f4a06e51691b0c 100644 --- a/src/libraries/System.Diagnostics.Tracing/ref/System.Diagnostics.Tracing.cs +++ b/src/libraries/System.Diagnostics.Tracing/ref/System.Diagnostics.Tracing.cs @@ -191,6 +191,7 @@ protected void WriteEvent(int eventId, long arg1, byte[]? arg2) { } protected void WriteEvent(int eventId, long arg1, long arg2) { } protected void WriteEvent(int eventId, long arg1, long arg2, long arg3) { } protected void WriteEvent(int eventId, long arg1, string? arg2) { } + protected void WriteEvent(int eventId, params EventSourcePrimitive[] args) { } [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCode("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")] protected void WriteEvent(int eventId, params object?[] args) { } protected void WriteEvent(int eventId, string? arg1) { } @@ -216,6 +217,41 @@ protected unsafe void WriteEventWithRelatedActivityIdCore(int eventId, System.Gu [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCode("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")] public void Write<[System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties)] T>(string? eventName, T data) { } [System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential)] + public readonly struct EventSourcePrimitive + { + public static implicit operator EventSourcePrimitive(bool value) => throw null; + public static implicit operator EventSourcePrimitive(byte value) => throw null; + public static implicit operator EventSourcePrimitive(short value) => throw null; + public static implicit operator EventSourcePrimitive(int value) => throw null; + public static implicit operator EventSourcePrimitive(long value) => throw null; + + [CLSCompliant(false)] + public static implicit operator EventSourcePrimitive(sbyte value) => throw null; + [CLSCompliant(false)] + public static implicit operator EventSourcePrimitive(ushort value) => throw null; + [CLSCompliant(false)] + public static implicit operator EventSourcePrimitive(uint value) => throw null; + [CLSCompliant(false)] + public static implicit operator EventSourcePrimitive(ulong value) => throw null; + [CLSCompliant(false)] + // Added to prevent going through the nuint -> ulong conversion + public static implicit operator EventSourcePrimitive(nuint value) => throw null; + + public static implicit operator EventSourcePrimitive(float value) => throw null; + public static implicit operator EventSourcePrimitive(double value) => throw null; + public static implicit operator EventSourcePrimitive(decimal value) => throw null; + + public static implicit operator EventSourcePrimitive(string? value) => throw null; + public static implicit operator EventSourcePrimitive(byte[]? value) => throw null; + + public static implicit operator EventSourcePrimitive(Guid value) => throw null; + public static implicit operator EventSourcePrimitive(DateTime value) => throw null; + public static implicit operator EventSourcePrimitive(nint value) => throw null; + public static implicit operator EventSourcePrimitive(char value) => throw null; + + public static implicit operator EventSourcePrimitive(Enum value) => throw null; + } + [System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential)] protected internal partial struct EventData { private int _dummyPrimitive; diff --git a/src/libraries/System.Diagnostics.Tracing/tests/TrimmingTests/EventSourcePropertyValueTest.cs b/src/libraries/System.Diagnostics.Tracing/tests/TrimmingTests/EventSourcePropertyValueTest.cs index a7e0db64cb1ae4..6682cb0fd45eb8 100644 --- a/src/libraries/System.Diagnostics.Tracing/tests/TrimmingTests/EventSourcePropertyValueTest.cs +++ b/src/libraries/System.Diagnostics.Tracing/tests/TrimmingTests/EventSourcePropertyValueTest.cs @@ -40,15 +40,37 @@ public void LogData(TestData data) } } + [EventSource(Name = EventSourceName)] + private class PrimitiveOnlyEventSource : EventSource + { + public const string EventSourceName = "PrimitiveOnly"; + public readonly static PrimitiveOnlyEventSource Log = new PrimitiveOnlyEventSource(); + + [Event(1)] + public void LogBoolInt(bool b1, int i1) + { + WriteEvent(1, b1, i1); + } + + [Event(2)] + public void LogStringIntStringInt(string s1, int i1, string s2, int i2) + { + WriteEvent(2, s1, i1, s2, i2); + } + } + private class TestEventListener : EventListener { public ReadOnlyCollection LogDataPayload { get; set; } protected override void OnEventSourceCreated(EventSource eventSource) { - if (eventSource.Name == TestEventSource.EventSourceName) + switch (eventSource.Name) { - EnableEvents(eventSource, EventLevel.Verbose); + case TestEventSource.EventSourceName: + case PrimitiveOnlyEventSource.EventSourceName: + EnableEvents(eventSource, EventLevel.Verbose); + break; } base.OnEventSourceCreated(eventSource); @@ -56,11 +78,7 @@ protected override void OnEventSourceCreated(EventSource eventSource) protected override void OnEventWritten(EventWrittenEventArgs eventData) { - if (eventData.EventName == "LogData") - { - LogDataPayload = eventData.Payload; - } - + LogDataPayload = eventData.Payload; base.OnEventWritten(eventData); } } @@ -79,14 +97,23 @@ public static int Main() }; TestEventSource.Log.LogData(testData); - if (listener.LogDataPayload?.Count == 2 && - (int)listener.LogDataPayload[0] == testData.TestInt && - (int)((IDictionary)listener.LogDataPayload[1])["SubInt"] == testData.SubData.SubInt) + if (listener.LogDataPayload?.Count != 2 || + (int)listener.LogDataPayload[0] != testData.TestInt || + (int)((IDictionary)listener.LogDataPayload[1])["SubInt"] != testData.SubData.SubInt) { - return 100; + return -1; } - return -1; + PrimitiveOnlyEventSource.Log.LogStringIntStringInt("a", 1, "b", 2); + if (listener.LogDataPayload?.Count != 4 || + (string)listener.LogDataPayload[0] != "a" || + (int)listener.LogDataPayload[1] != 1 || + (string)listener.LogDataPayload[2] != "b" || + (int)listener.LogDataPayload[3] != 2) + { + return -2; + } } + return 100; } } diff --git a/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/EventSource.cs b/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/EventSource.cs index b36fca3489f17e..759dea69f029ba 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/EventSource.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/EventSource.cs @@ -165,6 +165,7 @@ using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Globalization; +using System.IO; using System.Numerics; using System.Reflection; using System.Resources; @@ -1127,6 +1128,52 @@ protected unsafe void WriteEvent(int eventId, long arg1, byte[]? arg2) #pragma warning restore 1591 + /// + /// A wrapper type for separating primitive types (int, long, string, etc) from other types + /// in the EventSource API. This type shouldn't be used directly, but just as implicit conversions + /// when using the WriteEvent API. + /// + public readonly struct EventSourcePrimitive + { + internal readonly object? Value; + + private EventSourcePrimitive(object? value) + { + Value = value; + } + public static implicit operator EventSourcePrimitive(bool value) => new(value); + public static implicit operator EventSourcePrimitive(byte value) => new(value); + public static implicit operator EventSourcePrimitive(short value) => new(value); + public static implicit operator EventSourcePrimitive(int value) => new(value); + public static implicit operator EventSourcePrimitive(long value) => new(value); + + [CLSCompliant(false)] + public static implicit operator EventSourcePrimitive(sbyte value) => new(value); + [CLSCompliant(false)] + public static implicit operator EventSourcePrimitive(ushort value) => new(value); + [CLSCompliant(false)] + public static implicit operator EventSourcePrimitive(uint value) => new(value); + [CLSCompliant(false)] + public static implicit operator EventSourcePrimitive(ulong value) => new(value); + [CLSCompliant(false)] + // Added to prevent going through the nuint -> ulong conversion + public static implicit operator EventSourcePrimitive(nuint value) => new(value); + + public static implicit operator EventSourcePrimitive(float value) => new(value); + public static implicit operator EventSourcePrimitive(double value) => new(value); + public static implicit operator EventSourcePrimitive(decimal value) => new(value); + + public static implicit operator EventSourcePrimitive(string? value) => new(value); + public static implicit operator EventSourcePrimitive(byte[]? value) => new(value); + + public static implicit operator EventSourcePrimitive(Guid value) => new(value); + public static implicit operator EventSourcePrimitive(DateTime value) => new(value); + public static implicit operator EventSourcePrimitive(nint value) => new(value); + public static implicit operator EventSourcePrimitive(char value) => new(value); + + public static implicit operator EventSourcePrimitive(Enum value) => new(value); + } + /// /// Used to construct the data structure to be passed to the native ETW APIs - EventWrite and EventWriteTransfer. /// @@ -1335,6 +1382,28 @@ protected unsafe void WriteEventWithRelatedActivityIdCore(int eventId, Guid* rel } } + /// + /// This is a varargs helper for writing an event. It does create an array and box all the arguments so it is + /// relatively inefficient and should only be used for relatively rare events (e.g. less than 100 / sec). If your + /// rates are faster than that you should use to create fast helpers for your particular + /// method signature. Even if you use this for rare events, this call should be guarded by an + /// check so that the varargs call is not made when the EventSource is not active. + /// + [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:UnrecognizedReflectionPattern", + Justification = EventSourceSuppressMessage)] + protected void WriteEvent(int eventId, params EventSourcePrimitive[] args) + { + var argValues = new object?[args.Length]; + for (int i = 0; i < args.Length; i++) + { + argValues[i] = args[i].Value; + } + unsafe + { + WriteEventVarargs(eventId, null, argValues); + } + } + // fallback varags helpers. /// /// This is the varargs helper for writing an event. It does create an array and box all the arguments so it is diff --git a/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/TraceLogging/TraceLoggingEventSource.cs b/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/TraceLogging/TraceLoggingEventSource.cs index 6619ca770bbd1d..1c0f692631174e 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/TraceLogging/TraceLoggingEventSource.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/TraceLogging/TraceLoggingEventSource.cs @@ -24,7 +24,7 @@ public partial class EventSource #if FEATURE_MANAGED_ETW private byte[]? m_providerMetadata; private protected virtual ReadOnlySpan ProviderMetadata => m_providerMetadata; - private const string EventSourceRequiresUnreferenceMessage = "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"; + private const string EventSourceRequiresUnreferenceMessage = "EventSource will serialize the whole object graph. Trimmer will not safely handle this case because properties may be trimmed."; private const string EventSourceSuppressMessage = "Parameters to this method are primitive and are trimmer safe"; #endif diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/Tasks/TplEventSource.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/Tasks/TplEventSource.cs index deb8e283da6896..8239fcd57e5cc0 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/Tasks/TplEventSource.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/Tasks/TplEventSource.cs @@ -526,8 +526,6 @@ public void RunningContinuationList(int TaskID, int Index, long Object) [Event(24, Keywords = Keywords.Debug)] public void DebugFacilityMessage1(string Facility, string Message, string Value1) { WriteEvent(24, Facility, Message, Value1); } - [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:UnrecognizedReflectionPattern", - Justification = "Guid parameter is safe with WriteEvent")] [Event(25, Keywords = Keywords.DebugActivityId)] public void SetActivityId(Guid NewId) { diff --git a/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/OpenSslX509ChainEventSource.cs b/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/OpenSslX509ChainEventSource.cs index b8b105197980a5..0e1cdc25efb832 100644 --- a/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/OpenSslX509ChainEventSource.cs +++ b/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/OpenSslX509ChainEventSource.cs @@ -478,8 +478,6 @@ internal void CrlCacheDecodeError() EventId_CrlCacheExpired, Level = EventLevel.Verbose, Message = "The cached CRL's nextUpdate value ({1:O}) is not after the verification time ({0:O}).")] - [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode", - Justification = "verificationTime and nextUpdate are DateTime values, which are trimmer safe")] internal void CrlCacheExpired(DateTime verificationTime, DateTime nextUpdate) { if (IsEnabled()) @@ -504,8 +502,6 @@ internal void CrlCacheFileBasedExpiry() EventId_CrlCacheAcceptedFile, Level = EventLevel.Verbose, Message = "The cached crl nextUpdate value ({0:O}) is acceptable, using the cached file.")] - [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode", - Justification = "nextUpdate is a DateTime value, which is trimmer safe")] internal void CrlCacheAcceptedFile(DateTime nextUpdate) { if (IsEnabled()) @@ -707,8 +703,6 @@ private void CachingIntermediateFailed() Message = "Starting revocation check in mode '{0}' with scope '{1}' on a {2}-element chain.", Opcode = EventOpcode.Start, Level = EventLevel.Informational)] - [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode", - Justification = "revocationMode and revocationFlag are enums, and are trimmer safe")] internal void RevocationCheckStart(X509RevocationMode revocationMode, X509RevocationFlag revocationFlag, int chainSize) { if (IsEnabled()) diff --git a/src/libraries/System.Threading.Tasks.Dataflow/src/Internal/DataflowEtwProvider.cs b/src/libraries/System.Threading.Tasks.Dataflow/src/Internal/DataflowEtwProvider.cs index 5ce423b37d3391..eafe195d487177 100644 --- a/src/libraries/System.Threading.Tasks.Dataflow/src/Internal/DataflowEtwProvider.cs +++ b/src/libraries/System.Threading.Tasks.Dataflow/src/Internal/DataflowEtwProvider.cs @@ -101,9 +101,11 @@ internal void TaskLaunchedForMessageHandling( } } - [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode", - Justification = "WriteEvent Parameters are trimmer safe")] [Event(TASKLAUNCHED_EVENTID, Level = EventLevel.Informational)] +#if !NET8_0_OR_GREATER + [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode", + Justification = "This calls WriteEvent with all primitive arguments which is safe. Primitives are always serialized properly.")] +#endif private void TaskLaunchedForMessageHandling(int blockId, TaskLaunchedReason reason, int availableMessages, int taskId) { WriteEvent(TASKLAUNCHED_EVENTID, blockId, reason, availableMessages, taskId); @@ -158,9 +160,11 @@ internal enum BlockCompletionReason Canceled = (int)TaskStatus.Canceled } - [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode", - Justification = "WriteEvent Parameters are trimmer safe")] [Event(BLOCKCOMPLETED_EVENTID, Level = EventLevel.Informational)] +#if !NET8_0_OR_GREATER + [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode", + Justification = "This calls WriteEvent with all primitive arguments which is safe. Primitives are always serialized properly.")] +#endif private void DataflowBlockCompleted(int blockId, BlockCompletionReason reason, string exceptionData) { WriteEvent(BLOCKCOMPLETED_EVENTID, blockId, reason, exceptionData); diff --git a/src/libraries/System.Transactions.Local/src/System/Transactions/TransactionsEtwProvider.cs b/src/libraries/System.Transactions.Local/src/System/Transactions/TransactionsEtwProvider.cs index d70490b9a72879..6a681c5552d68f 100644 --- a/src/libraries/System.Transactions.Local/src/System/Transactions/TransactionsEtwProvider.cs +++ b/src/libraries/System.Transactions.Local/src/System/Transactions/TransactionsEtwProvider.cs @@ -572,7 +572,6 @@ internal void EnlistmentCreated(TraceSourceType traceSource, EnlistmentTraceIden } [Event(ENLISTMENT_CREATED_LTM_EVENTID, Keywords = Keywords.TraceLtm, Level = EventLevel.Informational, Task = Tasks.Enlistment, Opcode = Opcodes.Create, Message = "Enlistment Created (LTM). ID is {0}, type is {1}, options is {2}")] - [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode", Justification = "Only string/int are passed")] private void EnlistmentCreatedLtm(int enlistmentIdentifier, string enlistmentType, string enlistmentOptions) { SetActivityId(string.Empty); @@ -580,7 +579,6 @@ private void EnlistmentCreatedLtm(int enlistmentIdentifier, string enlistmentTyp } [Event(ENLISTMENT_CREATED_OLETX_EVENTID, Keywords = Keywords.TraceOleTx, Level = EventLevel.Informational, Task = Tasks.Enlistment, Opcode = Opcodes.Create, Message = "Enlistment Created (OLETX). ID is {0}, type is {1}, options is {2}")] - [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode", Justification = "Only string/int are passed")] private void EnlistmentCreatedOleTx(int enlistmentIdentifier, string enlistmentType, string enlistmentOptions) { SetActivityId(string.Empty); @@ -1056,8 +1054,6 @@ internal void TransactionScopeCreated(TransactionTraceIdentifier transactionID, } } - [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode", - Justification = "TransactionScopeResult parameter is an enum and is trimmer safe")] [Event(TRANSACTIONSCOPE_CREATED_EVENTID, Keywords = Keywords.TraceBase, Level = EventLevel.Informational, Task = Tasks.TransactionScope, Opcode = Opcodes.Created, Message = "Transactionscope was created: Transaction ID is {0}, TransactionScope Result is {1}")] private void TransactionScopeCreated(string transactionID, TransactionScopeResult transactionScopeResult) { diff --git a/src/mono/System.Private.CoreLib/CompatibilitySuppressions.xml b/src/mono/System.Private.CoreLib/CompatibilitySuppressions.xml index 6836172aa2e687..ed9aefadda0057 100644 --- a/src/mono/System.Private.CoreLib/CompatibilitySuppressions.xml +++ b/src/mono/System.Private.CoreLib/CompatibilitySuppressions.xml @@ -4,4 +4,46 @@ CP0014 M:System.Runtime.CompilerServices.RuntimeHelpers.GetObjectValue(System.Object)->object?:[T:System.Diagnostics.CodeAnalysis.NotNullIfNotNullAttribute] + + CP0015 + M:System.Diagnostics.Tracing.EventSource.Write``1(System.String,``0):[T:System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute] + ref/net8.0/System.Private.CoreLib.dll + lib/net8.0/System.Private.CoreLib.dll + + + CP0015 + M:System.Diagnostics.Tracing.EventSource.Write``1(System.String,System.Diagnostics.Tracing.EventSourceOptions@,``0@):[T:System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute] + ref/net8.0/System.Private.CoreLib.dll + lib/net8.0/System.Private.CoreLib.dll + + + CP0015 + M:System.Diagnostics.Tracing.EventSource.Write``1(System.String,System.Diagnostics.Tracing.EventSourceOptions@,System.Guid@,System.Guid@,``0@):[T:System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute] + ref/net8.0/System.Private.CoreLib.dll + lib/net8.0/System.Private.CoreLib.dll + + + CP0015 + M:System.Diagnostics.Tracing.EventSource.WriteEvent(System.Int32,System.Object[]):[T:System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute] + ref/net8.0/System.Private.CoreLib.dll + lib/net8.0/System.Private.CoreLib.dll + + + CP0015 + M:System.Diagnostics.Tracing.EventSource.WriteEventCore(System.Int32,System.Int32,System.Diagnostics.Tracing.EventSource.EventData*):[T:System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute] + ref/net8.0/System.Private.CoreLib.dll + lib/net8.0/System.Private.CoreLib.dll + + + CP0015 + M:System.Diagnostics.Tracing.EventSource.WriteEventWithRelatedActivityId(System.Int32,System.Guid,System.Object[]):[T:System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute] + ref/net8.0/System.Private.CoreLib.dll + lib/net8.0/System.Private.CoreLib.dll + + + CP0015 + M:System.Diagnostics.Tracing.EventSource.WriteEventWithRelatedActivityIdCore(System.Int32,System.Guid*,System.Int32,System.Diagnostics.Tracing.EventSource.EventData*):[T:System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute] + ref/net8.0/System.Private.CoreLib.dll + lib/net8.0/System.Private.CoreLib.dll + \ No newline at end of file