Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix trimming for DiagnosticSource #106680

Merged
merged 2 commits into from
Aug 22, 2024
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -224,6 +224,65 @@ internal sealed class EventSourceAutoGenerateAttribute : Attribute
[DynamicallyAccessedMembers(ManifestMemberTypes)]
public partial class EventSource : IDisposable
{
// private instance state
private string m_name = null!; // My friendly name (privided in ctor)
internal int m_id; // A small integer that is unique to this instance.
private Guid m_guid; // GUID representing the ETW eventSource to the OS.
internal volatile EventMetadata[]? m_eventData; // None per-event data
private volatile byte[]? m_rawManifest; // Bytes to send out representing the event schema

private EventHandler<EventCommandEventArgs>? m_eventCommandExecuted;

private readonly EventSourceSettings m_config; // configuration information

private bool m_eventSourceDisposed; // has Dispose been called.

// Enabling bits
private bool m_eventSourceEnabled; // am I enabled (any of my events are enabled for any dispatcher)
internal EventLevel m_level; // highest level enabled by any output dispatcher
internal EventKeywords m_matchAnyKeyword; // the logical OR of all levels enabled by any output dispatcher (zero is a special case) meaning 'all keywords'

// Dispatching state
internal volatile EventDispatcher? m_Dispatchers; // Linked list of code:EventDispatchers we write the data to (we also do ETW specially)
private volatile OverrideEventProvider m_etwProvider = null!; // This hooks up ETW commands to our 'OnEventCommand' callback
#if FEATURE_PERFTRACING
private object? m_createEventLock;
private IntPtr m_writeEventStringEventHandle = IntPtr.Zero;
private volatile OverrideEventProvider m_eventPipeProvider = null!;
#endif
private bool m_completelyInited; // The EventSource constructor has returned without exception.
private Exception? m_constructionException; // If there was an exception construction, this is it
private byte m_outOfBandMessageCount; // The number of out of band messages sent (we throttle them
private EventCommandEventArgs? m_deferredCommands; // If we get commands before we are fully we store them here and run the when we are fully inited.

private string[]? m_traits; // Used to implement GetTraits

[ThreadStatic]
private static byte m_EventSourceExceptionRecurenceCount; // current recursion count inside ThrowEventSourceException

internal volatile ulong[]? m_channelData;

// We use a single instance of ActivityTracker for all EventSources instances to allow correlation between multiple event providers.
// We have m_activityTracker field simply because instance field is more efficient than static field fetch.
private ActivityTracker m_activityTracker = null!;
internal const string ActivityStartSuffix = "Start";
internal const string ActivityStopSuffix = "Stop";

// This switch controls an opt-in, off-by-default mechanism for allowing multiple EventSources to have the same
// name and by extension GUID. This is not considered a mainline scenario and is explicitly intended as a release
// valve for users that make heavy use of AssemblyLoadContext and experience exceptions from EventSource.
// This does not solve any issues that might arise from this configuration. For instance:
//
// * If multiple manifest-mode EventSources have the same name/GUID, it is ambiguous which manifest should be used by an ETW parser.
// This can result in events being incorrectly parse. The data will still be there, but EventTrace (or other libraries) won't
// know how to parse it.
// * Potential issues in parsing self-describing EventSources that use the same name/GUID, event name, and payload type from the same AssemblyLoadContext
// but have different event IDs set.
//
// Most users should not turn this on.
internal const string DuplicateSourceNamesSwitch = "System.Diagnostics.Tracing.EventSource.AllowDuplicateSourceNames";
private static readonly bool AllowDuplicateSourceNames = AppContext.TryGetSwitch(DuplicateSourceNamesSwitch, out bool isEnabled) ? isEnabled : false;


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

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

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

#if FEATURE_PERFTRACING
// Register the provider with EventPipe
OverrideEventProvider? eventPipeProvider = TryGetPreregisteredEventPipeProvider(eventSourceName);
OverrideEventProvider? eventPipeProvider = EventSourceInitHelper.TryGetPreregisteredEventPipeProvider(eventSourceName);
if (eventPipeProvider == null)
{
eventPipeProvider = new OverrideEventProvider(eventSourceFactory, EventProviderType.EventPipe);
Expand Down Expand Up @@ -2407,7 +2466,7 @@ internal static EventOpcode GetOpcodeWithDefault(EventOpcode opcode, string? eve
/// <summary>
/// This class lets us hook the 'OnEventCommand' from the eventSource.
/// </summary>
private sealed class OverrideEventProvider : EventProvider
internal sealed class OverrideEventProvider : EventProvider
{
public OverrideEventProvider(Func<EventSource?> eventSourceFactory, EventProviderType providerType)
: base(providerType)
Expand Down Expand Up @@ -3840,11 +3899,24 @@ internal static void InitializeDefaultEventSources()
{
const string name = "System.Diagnostics.Metrics";
Guid id = new Guid("20752bc4-c151-50f5-f27b-df92d8af5a61");
PreregisterEventProviders(id, name, GetMetricsEventSource);
EventSourceInitHelper.PreregisterEventProviders(id, name, EventSourceInitHelper.GetMetricsEventSource);
}
}
#endregion
}

// This type is logically just more static EventSource functionality but it needs to be a separate class
// to ensure that the IL linker can remove unused methods in it. Methods defined within the EventSource type
// are never removed because EventSource has the potential to reflect over its own members.
internal static class EventSourceInitHelper
{
private static List<Func<EventSource?>> s_preregisteredEventSourceFactories = new List<Func<EventSource?>>();
private static readonly Dictionary<Guid, EventSource.OverrideEventProvider> s_preregisteredEtwProviders = new Dictionary<Guid, EventSource.OverrideEventProvider>();
#if FEATURE_PERFTRACING
private static readonly Dictionary<string, EventSource.OverrideEventProvider> s_preregisteredEventPipeProviders = new Dictionary<string, EventSource.OverrideEventProvider>();
#endif

private static EventSource? GetMetricsEventSource()
internal static EventSource? GetMetricsEventSource()
{
Type? metricsEventSourceType = Type.GetType(
"System.Diagnostics.Metrics.MetricsEventSource, System.Diagnostics.DiagnosticSource",
Expand All @@ -3864,7 +3936,7 @@ internal static void InitializeDefaultEventSources()

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

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

#if FEATURE_PERFTRACING
OverrideEventProvider eventPipeProvider = new OverrideEventProvider(eventSourceFactory, EventProviderType.EventPipe);
EventSource.OverrideEventProvider eventPipeProvider = new EventSource.OverrideEventProvider(eventSourceFactory, EventProviderType.EventPipe);
eventPipeProvider.Register(id, name);
lock (s_preregisteredEventPipeProviders)
{
Expand Down Expand Up @@ -3928,7 +4000,7 @@ internal static void EnsurePreregisteredEventSourcesExist()
// the list as long as we still guarantee they get initialized in the near future and reported to the
// same EventListener.OnEventSourceCreated() callback.
Func<EventSource?>[] factories;
lock(s_preregisteredEventSourceFactories)
lock (s_preregisteredEventSourceFactories)
{
factories = s_preregisteredEventSourceFactories.ToArray();
s_preregisteredEventSourceFactories.Clear();
Expand All @@ -3939,92 +4011,25 @@ internal static void EnsurePreregisteredEventSourcesExist()
}
}

private static List<Func<EventSource?>> s_preregisteredEventSourceFactories = new List<Func<EventSource?>>();

private static OverrideEventProvider? TryGetPreregisteredEtwProvider(Guid id)
internal static EventSource.OverrideEventProvider? TryGetPreregisteredEtwProvider(Guid id)
{
lock (s_preregisteredEtwProviders)
{
s_preregisteredEtwProviders.Remove(id, out OverrideEventProvider? provider);
s_preregisteredEtwProviders.Remove(id, out EventSource.OverrideEventProvider? provider);
return provider;
}
}

private static readonly Dictionary<Guid, OverrideEventProvider> s_preregisteredEtwProviders = new Dictionary<Guid, OverrideEventProvider>();

#if FEATURE_PERFTRACING
private static OverrideEventProvider? TryGetPreregisteredEventPipeProvider(string name)
internal static EventSource.OverrideEventProvider? TryGetPreregisteredEventPipeProvider(string name)
{
lock (s_preregisteredEventPipeProviders)
{
s_preregisteredEventPipeProviders.Remove(name, out OverrideEventProvider? provider);
s_preregisteredEventPipeProviders.Remove(name, out EventSource.OverrideEventProvider? provider);
return provider;
}
}

private static readonly Dictionary<string, OverrideEventProvider> s_preregisteredEventPipeProviders = new Dictionary<string, OverrideEventProvider>();
#endif

// private instance state
private string m_name = null!; // My friendly name (privided in ctor)
internal int m_id; // A small integer that is unique to this instance.
private Guid m_guid; // GUID representing the ETW eventSource to the OS.
internal volatile EventMetadata[]? m_eventData; // None per-event data
private volatile byte[]? m_rawManifest; // Bytes to send out representing the event schema

private EventHandler<EventCommandEventArgs>? m_eventCommandExecuted;

private readonly EventSourceSettings m_config; // configuration information

private bool m_eventSourceDisposed; // has Dispose been called.

// Enabling bits
private bool m_eventSourceEnabled; // am I enabled (any of my events are enabled for any dispatcher)
internal EventLevel m_level; // highest level enabled by any output dispatcher
internal EventKeywords m_matchAnyKeyword; // the logical OR of all levels enabled by any output dispatcher (zero is a special case) meaning 'all keywords'

// Dispatching state
internal volatile EventDispatcher? m_Dispatchers; // Linked list of code:EventDispatchers we write the data to (we also do ETW specially)
private volatile OverrideEventProvider m_etwProvider = null!; // This hooks up ETW commands to our 'OnEventCommand' callback
#if FEATURE_PERFTRACING
private object? m_createEventLock;
private IntPtr m_writeEventStringEventHandle = IntPtr.Zero;
private volatile OverrideEventProvider m_eventPipeProvider = null!;
#endif
private bool m_completelyInited; // The EventSource constructor has returned without exception.
private Exception? m_constructionException; // If there was an exception construction, this is it
private byte m_outOfBandMessageCount; // The number of out of band messages sent (we throttle them
private EventCommandEventArgs? m_deferredCommands; // If we get commands before we are fully we store them here and run the when we are fully inited.

private string[]? m_traits; // Used to implement GetTraits

[ThreadStatic]
private static byte m_EventSourceExceptionRecurenceCount; // current recursion count inside ThrowEventSourceException

internal volatile ulong[]? m_channelData;

// We use a single instance of ActivityTracker for all EventSources instances to allow correlation between multiple event providers.
// We have m_activityTracker field simply because instance field is more efficient than static field fetch.
private ActivityTracker m_activityTracker = null!;
internal const string ActivityStartSuffix = "Start";
internal const string ActivityStopSuffix = "Stop";

// This switch controls an opt-in, off-by-default mechanism for allowing multiple EventSources to have the same
// name and by extension GUID. This is not considered a mainline scenario and is explicitly intended as a release
// valve for users that make heavy use of AssemblyLoadContext and experience exceptions from EventSource.
// This does not solve any issues that might arise from this configuration. For instance:
//
// * If multiple manifest-mode EventSources have the same name/GUID, it is ambiguous which manifest should be used by an ETW parser.
// This can result in events being incorrectly parse. The data will still be there, but EventTrace (or other libraries) won't
// know how to parse it.
// * Potential issues in parsing self-describing EventSources that use the same name/GUID, event name, and payload type from the same AssemblyLoadContext
// but have different event IDs set.
//
// Most users should not turn this on.
internal const string DuplicateSourceNamesSwitch = "System.Diagnostics.Tracing.EventSource.AllowDuplicateSourceNames";
private static readonly bool AllowDuplicateSourceNames = AppContext.TryGetSwitch(DuplicateSourceNamesSwitch, out bool isEnabled) ? isEnabled : false;

#endregion
}

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

lock (EventListenersLock)
{
Expand Down
Loading