Skip to content

Commit 6177a9f

Browse files
authored
Fix trimming for DiagnosticSource (#106680)
Recent changes to EventSource startup caused the IL trimmer to include portions of the DiagnosticSource assembly. Adding the IsMeterSupported feature check wasn't sufficient because EventSource also roots all of its methods via a DynamicallyAccessedMembers attribute. To ensure that the new methods can be removed when needed I refactored them into a separate EventSourceInitHelper class that won't be rooted by the existing DAM attribute. When EventSouce.IsSupported is false I'd expect the entire EventSourceInitHelper class to be unreachable. If only EventSource.IsMeterSupported is false then I'd expect PreregisterEventProviders and GetMetricsEventSource() to be unreachable but the rest of the class will remain. Hopefully this really fixes #106265 this time.
1 parent 279d5ca commit 6177a9f

File tree

1 file changed

+86
-81
lines changed
  • src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing

1 file changed

+86
-81
lines changed

src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/EventSource.cs

Lines changed: 86 additions & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -224,6 +224,65 @@ internal sealed class EventSourceAutoGenerateAttribute : Attribute
224224
[DynamicallyAccessedMembers(ManifestMemberTypes)]
225225
public partial class EventSource : IDisposable
226226
{
227+
// private instance state
228+
private string m_name = null!; // My friendly name (privided in ctor)
229+
internal int m_id; // A small integer that is unique to this instance.
230+
private Guid m_guid; // GUID representing the ETW eventSource to the OS.
231+
internal volatile EventMetadata[]? m_eventData; // None per-event data
232+
private volatile byte[]? m_rawManifest; // Bytes to send out representing the event schema
233+
234+
private EventHandler<EventCommandEventArgs>? m_eventCommandExecuted;
235+
236+
private readonly EventSourceSettings m_config; // configuration information
237+
238+
private bool m_eventSourceDisposed; // has Dispose been called.
239+
240+
// Enabling bits
241+
private bool m_eventSourceEnabled; // am I enabled (any of my events are enabled for any dispatcher)
242+
internal EventLevel m_level; // highest level enabled by any output dispatcher
243+
internal EventKeywords m_matchAnyKeyword; // the logical OR of all levels enabled by any output dispatcher (zero is a special case) meaning 'all keywords'
244+
245+
// Dispatching state
246+
internal volatile EventDispatcher? m_Dispatchers; // Linked list of code:EventDispatchers we write the data to (we also do ETW specially)
247+
private volatile OverrideEventProvider m_etwProvider = null!; // This hooks up ETW commands to our 'OnEventCommand' callback
248+
#if FEATURE_PERFTRACING
249+
private object? m_createEventLock;
250+
private IntPtr m_writeEventStringEventHandle = IntPtr.Zero;
251+
private volatile OverrideEventProvider m_eventPipeProvider = null!;
252+
#endif
253+
private bool m_completelyInited; // The EventSource constructor has returned without exception.
254+
private Exception? m_constructionException; // If there was an exception construction, this is it
255+
private byte m_outOfBandMessageCount; // The number of out of band messages sent (we throttle them
256+
private EventCommandEventArgs? m_deferredCommands; // If we get commands before we are fully we store them here and run the when we are fully inited.
257+
258+
private string[]? m_traits; // Used to implement GetTraits
259+
260+
[ThreadStatic]
261+
private static byte m_EventSourceExceptionRecurenceCount; // current recursion count inside ThrowEventSourceException
262+
263+
internal volatile ulong[]? m_channelData;
264+
265+
// We use a single instance of ActivityTracker for all EventSources instances to allow correlation between multiple event providers.
266+
// We have m_activityTracker field simply because instance field is more efficient than static field fetch.
267+
private ActivityTracker m_activityTracker = null!;
268+
internal const string ActivityStartSuffix = "Start";
269+
internal const string ActivityStopSuffix = "Stop";
270+
271+
// This switch controls an opt-in, off-by-default mechanism for allowing multiple EventSources to have the same
272+
// name and by extension GUID. This is not considered a mainline scenario and is explicitly intended as a release
273+
// valve for users that make heavy use of AssemblyLoadContext and experience exceptions from EventSource.
274+
// This does not solve any issues that might arise from this configuration. For instance:
275+
//
276+
// * If multiple manifest-mode EventSources have the same name/GUID, it is ambiguous which manifest should be used by an ETW parser.
277+
// This can result in events being incorrectly parse. The data will still be there, but EventTrace (or other libraries) won't
278+
// know how to parse it.
279+
// * Potential issues in parsing self-describing EventSources that use the same name/GUID, event name, and payload type from the same AssemblyLoadContext
280+
// but have different event IDs set.
281+
//
282+
// Most users should not turn this on.
283+
internal const string DuplicateSourceNamesSwitch = "System.Diagnostics.Tracing.EventSource.AllowDuplicateSourceNames";
284+
private static readonly bool AllowDuplicateSourceNames = AppContext.TryGetSwitch(DuplicateSourceNamesSwitch, out bool isEnabled) ? isEnabled : false;
285+
227286

228287
internal static bool IsSupported { get; } = InitializeIsSupported();
229288

@@ -1608,7 +1667,7 @@ private unsafe void Initialize(Guid eventSourceGuid, string eventSourceName, str
16081667

16091668
// Register the provider with ETW
16101669
Func<EventSource?> eventSourceFactory = () => this;
1611-
OverrideEventProvider? etwProvider = TryGetPreregisteredEtwProvider(eventSourceGuid);
1670+
OverrideEventProvider? etwProvider = EventSourceInitHelper.TryGetPreregisteredEtwProvider(eventSourceGuid);
16121671
if(etwProvider == null)
16131672
{
16141673
etwProvider = new OverrideEventProvider(eventSourceFactory, EventProviderType.ETW);
@@ -1632,7 +1691,7 @@ private unsafe void Initialize(Guid eventSourceGuid, string eventSourceName, str
16321691

16331692
#if FEATURE_PERFTRACING
16341693
// Register the provider with EventPipe
1635-
OverrideEventProvider? eventPipeProvider = TryGetPreregisteredEventPipeProvider(eventSourceName);
1694+
OverrideEventProvider? eventPipeProvider = EventSourceInitHelper.TryGetPreregisteredEventPipeProvider(eventSourceName);
16361695
if (eventPipeProvider == null)
16371696
{
16381697
eventPipeProvider = new OverrideEventProvider(eventSourceFactory, EventProviderType.EventPipe);
@@ -2407,7 +2466,7 @@ internal static EventOpcode GetOpcodeWithDefault(EventOpcode opcode, string? eve
24072466
/// <summary>
24082467
/// This class lets us hook the 'OnEventCommand' from the eventSource.
24092468
/// </summary>
2410-
private sealed class OverrideEventProvider : EventProvider
2469+
internal sealed class OverrideEventProvider : EventProvider
24112470
{
24122471
public OverrideEventProvider(Func<EventSource?> eventSourceFactory, EventProviderType providerType)
24132472
: base(providerType)
@@ -3840,11 +3899,24 @@ internal static void InitializeDefaultEventSources()
38403899
{
38413900
const string name = "System.Diagnostics.Metrics";
38423901
Guid id = new Guid("20752bc4-c151-50f5-f27b-df92d8af5a61");
3843-
PreregisterEventProviders(id, name, GetMetricsEventSource);
3902+
EventSourceInitHelper.PreregisterEventProviders(id, name, EventSourceInitHelper.GetMetricsEventSource);
38443903
}
38453904
}
3905+
#endregion
3906+
}
3907+
3908+
// This type is logically just more static EventSource functionality but it needs to be a separate class
3909+
// to ensure that the IL linker can remove unused methods in it. Methods defined within the EventSource type
3910+
// are never removed because EventSource has the potential to reflect over its own members.
3911+
internal static class EventSourceInitHelper
3912+
{
3913+
private static List<Func<EventSource?>> s_preregisteredEventSourceFactories = new List<Func<EventSource?>>();
3914+
private static readonly Dictionary<Guid, EventSource.OverrideEventProvider> s_preregisteredEtwProviders = new Dictionary<Guid, EventSource.OverrideEventProvider>();
3915+
#if FEATURE_PERFTRACING
3916+
private static readonly Dictionary<string, EventSource.OverrideEventProvider> s_preregisteredEventPipeProviders = new Dictionary<string, EventSource.OverrideEventProvider>();
3917+
#endif
38463918

3847-
private static EventSource? GetMetricsEventSource()
3919+
internal static EventSource? GetMetricsEventSource()
38483920
{
38493921
Type? metricsEventSourceType = Type.GetType(
38503922
"System.Diagnostics.Metrics.MetricsEventSource, System.Diagnostics.DiagnosticSource",
@@ -3864,7 +3936,7 @@ internal static void InitializeDefaultEventSources()
38643936

38653937
// Pre-registration creates and registers an EventProvider prior to the EventSource being constructed.
38663938
// If a tracing session is started using the provider then the EventSource will be constructed on demand.
3867-
private static unsafe void PreregisterEventProviders(Guid id, string name, Func<EventSource?> eventSourceFactory)
3939+
internal static unsafe void PreregisterEventProviders(Guid id, string name, Func<EventSource?> eventSourceFactory)
38683940
{
38693941
// NOTE: Pre-registration has some minor limitations and variations to normal EventSource behavior:
38703942
// 1. Instead of delivering OnEventCommand callbacks during the EventSource constructor it may deliver them after
@@ -3882,7 +3954,7 @@ private static unsafe void PreregisterEventProviders(Guid id, string name, Func<
38823954
{
38833955
s_preregisteredEventSourceFactories.Add(eventSourceFactory);
38843956

3885-
OverrideEventProvider etwProvider = new OverrideEventProvider(eventSourceFactory, EventProviderType.ETW);
3957+
EventSource.OverrideEventProvider etwProvider = new EventSource.OverrideEventProvider(eventSourceFactory, EventProviderType.ETW);
38863958
etwProvider.Register(id, name);
38873959
#if TARGET_WINDOWS
38883960
byte[] providerMetadata = Statics.MetadataForString(name, 0, 0, 0);
@@ -3900,7 +3972,7 @@ private static unsafe void PreregisterEventProviders(Guid id, string name, Func<
39003972
}
39013973

39023974
#if FEATURE_PERFTRACING
3903-
OverrideEventProvider eventPipeProvider = new OverrideEventProvider(eventSourceFactory, EventProviderType.EventPipe);
3975+
EventSource.OverrideEventProvider eventPipeProvider = new EventSource.OverrideEventProvider(eventSourceFactory, EventProviderType.EventPipe);
39043976
eventPipeProvider.Register(id, name);
39053977
lock (s_preregisteredEventPipeProviders)
39063978
{
@@ -3928,7 +4000,7 @@ internal static void EnsurePreregisteredEventSourcesExist()
39284000
// the list as long as we still guarantee they get initialized in the near future and reported to the
39294001
// same EventListener.OnEventSourceCreated() callback.
39304002
Func<EventSource?>[] factories;
3931-
lock(s_preregisteredEventSourceFactories)
4003+
lock (s_preregisteredEventSourceFactories)
39324004
{
39334005
factories = s_preregisteredEventSourceFactories.ToArray();
39344006
s_preregisteredEventSourceFactories.Clear();
@@ -3939,92 +4011,25 @@ internal static void EnsurePreregisteredEventSourcesExist()
39394011
}
39404012
}
39414013

3942-
private static List<Func<EventSource?>> s_preregisteredEventSourceFactories = new List<Func<EventSource?>>();
3943-
3944-
private static OverrideEventProvider? TryGetPreregisteredEtwProvider(Guid id)
4014+
internal static EventSource.OverrideEventProvider? TryGetPreregisteredEtwProvider(Guid id)
39454015
{
39464016
lock (s_preregisteredEtwProviders)
39474017
{
3948-
s_preregisteredEtwProviders.Remove(id, out OverrideEventProvider? provider);
4018+
s_preregisteredEtwProviders.Remove(id, out EventSource.OverrideEventProvider? provider);
39494019
return provider;
39504020
}
39514021
}
39524022

3953-
private static readonly Dictionary<Guid, OverrideEventProvider> s_preregisteredEtwProviders = new Dictionary<Guid, OverrideEventProvider>();
3954-
39554023
#if FEATURE_PERFTRACING
3956-
private static OverrideEventProvider? TryGetPreregisteredEventPipeProvider(string name)
4024+
internal static EventSource.OverrideEventProvider? TryGetPreregisteredEventPipeProvider(string name)
39574025
{
39584026
lock (s_preregisteredEventPipeProviders)
39594027
{
3960-
s_preregisteredEventPipeProviders.Remove(name, out OverrideEventProvider? provider);
4028+
s_preregisteredEventPipeProviders.Remove(name, out EventSource.OverrideEventProvider? provider);
39614029
return provider;
39624030
}
39634031
}
3964-
3965-
private static readonly Dictionary<string, OverrideEventProvider> s_preregisteredEventPipeProviders = new Dictionary<string, OverrideEventProvider>();
39664032
#endif
3967-
3968-
// private instance state
3969-
private string m_name = null!; // My friendly name (privided in ctor)
3970-
internal int m_id; // A small integer that is unique to this instance.
3971-
private Guid m_guid; // GUID representing the ETW eventSource to the OS.
3972-
internal volatile EventMetadata[]? m_eventData; // None per-event data
3973-
private volatile byte[]? m_rawManifest; // Bytes to send out representing the event schema
3974-
3975-
private EventHandler<EventCommandEventArgs>? m_eventCommandExecuted;
3976-
3977-
private readonly EventSourceSettings m_config; // configuration information
3978-
3979-
private bool m_eventSourceDisposed; // has Dispose been called.
3980-
3981-
// Enabling bits
3982-
private bool m_eventSourceEnabled; // am I enabled (any of my events are enabled for any dispatcher)
3983-
internal EventLevel m_level; // highest level enabled by any output dispatcher
3984-
internal EventKeywords m_matchAnyKeyword; // the logical OR of all levels enabled by any output dispatcher (zero is a special case) meaning 'all keywords'
3985-
3986-
// Dispatching state
3987-
internal volatile EventDispatcher? m_Dispatchers; // Linked list of code:EventDispatchers we write the data to (we also do ETW specially)
3988-
private volatile OverrideEventProvider m_etwProvider = null!; // This hooks up ETW commands to our 'OnEventCommand' callback
3989-
#if FEATURE_PERFTRACING
3990-
private object? m_createEventLock;
3991-
private IntPtr m_writeEventStringEventHandle = IntPtr.Zero;
3992-
private volatile OverrideEventProvider m_eventPipeProvider = null!;
3993-
#endif
3994-
private bool m_completelyInited; // The EventSource constructor has returned without exception.
3995-
private Exception? m_constructionException; // If there was an exception construction, this is it
3996-
private byte m_outOfBandMessageCount; // The number of out of band messages sent (we throttle them
3997-
private EventCommandEventArgs? m_deferredCommands; // If we get commands before we are fully we store them here and run the when we are fully inited.
3998-
3999-
private string[]? m_traits; // Used to implement GetTraits
4000-
4001-
[ThreadStatic]
4002-
private static byte m_EventSourceExceptionRecurenceCount; // current recursion count inside ThrowEventSourceException
4003-
4004-
internal volatile ulong[]? m_channelData;
4005-
4006-
// We use a single instance of ActivityTracker for all EventSources instances to allow correlation between multiple event providers.
4007-
// We have m_activityTracker field simply because instance field is more efficient than static field fetch.
4008-
private ActivityTracker m_activityTracker = null!;
4009-
internal const string ActivityStartSuffix = "Start";
4010-
internal const string ActivityStopSuffix = "Stop";
4011-
4012-
// This switch controls an opt-in, off-by-default mechanism for allowing multiple EventSources to have the same
4013-
// name and by extension GUID. This is not considered a mainline scenario and is explicitly intended as a release
4014-
// valve for users that make heavy use of AssemblyLoadContext and experience exceptions from EventSource.
4015-
// This does not solve any issues that might arise from this configuration. For instance:
4016-
//
4017-
// * If multiple manifest-mode EventSources have the same name/GUID, it is ambiguous which manifest should be used by an ETW parser.
4018-
// This can result in events being incorrectly parse. The data will still be there, but EventTrace (or other libraries) won't
4019-
// know how to parse it.
4020-
// * Potential issues in parsing self-describing EventSources that use the same name/GUID, event name, and payload type from the same AssemblyLoadContext
4021-
// but have different event IDs set.
4022-
//
4023-
// Most users should not turn this on.
4024-
internal const string DuplicateSourceNamesSwitch = "System.Diagnostics.Tracing.EventSource.AllowDuplicateSourceNames";
4025-
private static readonly bool AllowDuplicateSourceNames = AppContext.TryGetSwitch(DuplicateSourceNamesSwitch, out bool isEnabled) ? isEnabled : false;
4026-
4027-
#endregion
40284033
}
40294034

40304035
/// <summary>
@@ -4585,7 +4590,7 @@ private void CallBackForExistingEventSources(bool addToListenersList, EventHandl
45854590
{
45864591
// Pre-registered EventSources may not have been constructed yet but we need to do so now to ensure they are
45874592
// reported to the EventListener.
4588-
EventSource.EnsurePreregisteredEventSourcesExist();
4593+
EventSourceInitHelper.EnsurePreregisteredEventSourcesExist();
45894594

45904595
lock (EventListenersLock)
45914596
{

0 commit comments

Comments
 (0)