diff options
author | Zoltan Varga <vargaz@gmail.com> | 2020-09-09 00:52:33 +0300 |
---|---|---|
committer | GitHub <noreply@github.com> | 2020-09-09 00:52:33 +0300 |
commit | 6e103e7191dcb17120032f2516cc7f8e9e04c32e (patch) | |
tree | a3ee2ea7450809dac229fc084683df3f335942ad /netcore/System.Private.CoreLib/shared/System/Diagnostics/Tracing | |
parent | 427395f4c6a18466c26d0cf782bed106d05e77ae (diff) |
Remove netcore/ directory from the mono/mono repository. (#20361)
Diffstat (limited to 'netcore/System.Private.CoreLib/shared/System/Diagnostics/Tracing')
47 files changed, 0 insertions, 16018 deletions
diff --git a/netcore/System.Private.CoreLib/shared/System/Diagnostics/Tracing/ActivityTracker.cs b/netcore/System.Private.CoreLib/shared/System/Diagnostics/Tracing/ActivityTracker.cs deleted file mode 100644 index 07c2112878f..00000000000 --- a/netcore/System.Private.CoreLib/shared/System/Diagnostics/Tracing/ActivityTracker.cs +++ /dev/null @@ -1,663 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -#if ES_BUILD_STANDALONE -using System; -using System.Diagnostics; -#else -using System.Threading.Tasks; -#endif -using System.Threading; - -#if ES_BUILD_STANDALONE -namespace Microsoft.Diagnostics.Tracing -#else -namespace System.Diagnostics.Tracing -#endif -{ - /// <summary> - /// Tracks activities. This is meant to be a singleton (accessed by the ActivityTracer.Instance static property) - /// - /// Logically this is simply holds the m_current variable that holds the async local that holds the current ActivityInfo - /// An ActivityInfo is represents a activity (which knows its creator and thus knows its path). - /// - /// Most of the magic is in the async local (it gets copied to new tasks) - /// - /// On every start event call OnStart - /// - /// Guid activityID; - /// Guid relatedActivityID; - /// if (OnStart(activityName, out activityID, out relatedActivityID, ForceStop, options)) - /// // Log Start event with activityID and relatedActivityID - /// - /// On every stop event call OnStop - /// - /// Guid activityID; - /// if (OnStop(activityName, ref activityID ForceStop)) - /// // Stop event with activityID - /// - /// On any normal event log the event with activityTracker.CurrentActivityId - /// </summary> - internal class ActivityTracker - { - /// <summary> - /// Called on work item begins. The activity name = providerName + activityName without 'Start' suffix. - /// It updates CurrentActivityId to track. - /// - /// It returns true if the Start should be logged, otherwise (if it is illegal recursion) it return false. - /// - /// The start event should use as its activity ID the CurrentActivityId AFTER calling this routine and its - /// RelatedActivityID the CurrentActivityId BEFORE calling this routine (the creator). - /// - /// If activity tracing is not on, then activityId and relatedActivityId are not set - /// </summary> - public void OnStart(string providerName, string activityName, int task, ref Guid activityId, ref Guid relatedActivityId, EventActivityOptions options, bool useTplSource = true) - { - if (m_current == null) // We are not enabled - { - // We used to rely on the TPL provider turning us on, but that has the disadvantage that you don't get Start-Stop tracking - // until you use Tasks for the first time (which you may never do). Thus we change it to pull rather tan push for whether - // we are enabled. - if (m_checkedForEnable) - return; - m_checkedForEnable = true; - if (useTplSource && TplEventSource.Log.IsEnabled(EventLevel.Informational, TplEventSource.Keywords.TasksFlowActivityIds)) - Enable(); - if (m_current == null) - return; - } - - Debug.Assert((options & EventActivityOptions.Disable) == 0); - - ActivityInfo? currentActivity = m_current.Value; - string fullActivityName = NormalizeActivityName(providerName, activityName, task); - - TplEventSource? log = useTplSource ? TplEventSource.Log : null; - bool tplDebug = log != null && log.Debug; - if (tplDebug) - { - log!.DebugFacilityMessage("OnStartEnter", fullActivityName); - log!.DebugFacilityMessage("OnStartEnterActivityState", ActivityInfo.LiveActivities(currentActivity)); - } - - if (currentActivity != null) - { - // Stop activity tracking if we reached the maximum allowed depth - if (currentActivity.m_level >= MAX_ACTIVITY_DEPTH) - { - activityId = Guid.Empty; - relatedActivityId = Guid.Empty; - if (tplDebug) - log!.DebugFacilityMessage("OnStartRET", "Fail"); - return; - } - // Check for recursion, and force-stop any activities if the activity already started. - if ((options & EventActivityOptions.Recursive) == 0) - { - ActivityInfo? existingActivity = FindActiveActivity(fullActivityName, currentActivity); - if (existingActivity != null) - { - OnStop(providerName, activityName, task, ref activityId); - currentActivity = m_current.Value; - } - } - } - - // Get a unique ID for this activity. - long id; - if (currentActivity == null) - id = Interlocked.Increment(ref m_nextId); - else - id = Interlocked.Increment(ref currentActivity.m_lastChildID); - - // The previous ID is my 'causer' and becomes my related activity ID - relatedActivityId = EventSource.CurrentThreadActivityId; - - // Add to the list of started but not stopped activities. - ActivityInfo newActivity = new ActivityInfo(fullActivityName, id, currentActivity, relatedActivityId, options); - m_current.Value = newActivity; - - // Remember the current ID so we can log it - activityId = newActivity.ActivityId; - - if (tplDebug) - { - log!.DebugFacilityMessage("OnStartRetActivityState", ActivityInfo.LiveActivities(newActivity)); - log!.DebugFacilityMessage1("OnStartRet", activityId.ToString(), relatedActivityId.ToString()); - } - } - - /// <summary> - /// Called when a work item stops. The activity name = providerName + activityName without 'Stop' suffix. - /// It updates m_current variable to track this fact. The Stop event associated with stop should log the ActivityID associated with the event. - /// - /// If activity tracing is not on, then activityId and relatedActivityId are not set - /// </summary> - public void OnStop(string providerName, string activityName, int task, ref Guid activityId, bool useTplSource = true) - { - if (m_current == null) // We are not enabled - return; - - string fullActivityName = NormalizeActivityName(providerName, activityName, task); - - TplEventSource? log = useTplSource ? TplEventSource.Log : null; - bool tplDebug = log != null && log.Debug; - if (tplDebug) - { - log!.DebugFacilityMessage("OnStopEnter", fullActivityName); - log!.DebugFacilityMessage("OnStopEnterActivityState", ActivityInfo.LiveActivities(m_current.Value)); - } - - while (true) // This is a retry loop. - { - ActivityInfo? currentActivity = m_current.Value; - ActivityInfo? newCurrentActivity = null; // if we have seen any live activities (orphans), at he first one we have seen. - - // Search to find the activity to stop in one pass. This insures that we don't let one mistake - // (stopping something that was not started) cause all active starts to be stopped - // By first finding the target start to stop we are more robust. - ActivityInfo? activityToStop = FindActiveActivity(fullActivityName, currentActivity); - - // ignore stops where we can't find a start because we may have popped them previously. - if (activityToStop == null) - { - activityId = Guid.Empty; - // TODO add some logging about this. Basically could not find matching start. - if (tplDebug) - log!.DebugFacilityMessage("OnStopRET", "Fail"); - return; - } - - activityId = activityToStop.ActivityId; - - // See if there are any orphans that need to be stopped. - ActivityInfo? orphan = currentActivity; - while (orphan != activityToStop && orphan != null) - { - if (orphan.m_stopped != 0) // Skip dead activities. - { - orphan = orphan.m_creator; - continue; - } - if (orphan.CanBeOrphan()) - { - // We can't pop anything after we see a valid orphan, remember this for later when we update m_current. - newCurrentActivity ??= orphan; - } - else - { - orphan.m_stopped = 1; - Debug.Assert(orphan.m_stopped != 0); - } - orphan = orphan.m_creator; - } - - // try to Stop the activity atomically. Other threads may be trying to do this as well. - if (Interlocked.CompareExchange(ref activityToStop.m_stopped, 1, 0) == 0) - { - // I succeeded stopping this activity. Now we update our m_current pointer - - // If I haven't yet determined the new current activity, it is my creator. - newCurrentActivity ??= activityToStop.m_creator; - - m_current.Value = newCurrentActivity; - - if (tplDebug) - { - log!.DebugFacilityMessage("OnStopRetActivityState", ActivityInfo.LiveActivities(newCurrentActivity)); - log!.DebugFacilityMessage("OnStopRet", activityId.ToString()); - } - return; - } - // We failed to stop it. We must have hit a race to stop it. Just start over and try again. - } - } - - /// <summary> - /// Turns on activity tracking. It is sticky, once on it stays on (race issues otherwise) - /// </summary> - public void Enable() - { - if (m_current == null) - { - // Catch the not Implemented - try - { - m_current = new AsyncLocal<ActivityInfo?>(ActivityChanging); - } - catch (NotImplementedException) - { -#if (!ES_BUILD_PCL && !ES_BUILD_PN) - // send message to debugger without delay - System.Diagnostics.Debugger.Log(0, null, "Activity Enabled() called but AsyncLocals Not Supported (pre V4.6). Ignoring Enable"); -#endif - } - } - } - - /// <summary> - /// An activity tracker is a singleton, this is how you get the one and only instance. - /// </summary> - public static ActivityTracker Instance => s_activityTrackerInstance; - - #region private - - /// <summary> - /// Searched for a active (nonstopped) activity with the given name. Returns null if not found. - /// </summary> - private static ActivityInfo? FindActiveActivity(string name, ActivityInfo? startLocation) - { - ActivityInfo? activity = startLocation; - while (activity != null) - { - if (name == activity.m_name && activity.m_stopped == 0) - return activity; - activity = activity.m_creator; - } - return null; - } - - /// <summary> - /// Strip out "Start" or "End" suffix from activity name and add providerName prefix. - /// If 'task' it does not end in Start or Stop and Task is non-zero use that as the name of the activity - /// </summary> - private static string NormalizeActivityName(string providerName, string activityName, int task) - { - // We use provider name to distinguish between activities from different providers. - - if (activityName.EndsWith(EventSource.s_ActivityStartSuffix, StringComparison.Ordinal)) - { -#if ES_BUILD_STANDALONE - return string.Concat(providerName, activityName.Substring(0, activityName.Length - EventSource.s_ActivityStartSuffix.Length)); -#else - return string.Concat(providerName, activityName.AsSpan(0, activityName.Length - EventSource.s_ActivityStartSuffix.Length)); -#endif - } - else if (activityName.EndsWith(EventSource.s_ActivityStopSuffix, StringComparison.Ordinal)) - { -#if ES_BUILD_STANDALONE - return string.Concat(providerName, activityName.Substring(0, activityName.Length - EventSource.s_ActivityStopSuffix.Length)); -#else - return string.Concat(providerName, activityName.AsSpan(0, activityName.Length - EventSource.s_ActivityStopSuffix.Length)); -#endif - } - else if (task != 0) - { - return providerName + "task" + task.ToString(); - } - else - { - return providerName + activityName; - } - } - - // ******************************************************************************* - /// <summary> - /// An ActivityInfo represents a particular activity. It is almost read-only. The only - /// fields that change after creation are - /// m_lastChildID - used to generate unique IDs for the children activities and for the most part can be ignored. - /// m_stopped - indicates that this activity is dead - /// This read-only-ness is important because an activity's m_creator chain forms the - /// 'Path of creation' for the activity (which is also its unique ID) but is also used as - /// the 'list of live parents' which indicate of those ancestors, which are alive (if they - /// are not marked dead they are alive). - /// </summary> - private class ActivityInfo - { - public ActivityInfo(string name, long uniqueId, ActivityInfo? creator, Guid activityIDToRestore, EventActivityOptions options) - { - m_name = name; - m_eventOptions = options; - m_creator = creator; - m_uniqueId = uniqueId; - m_level = creator != null ? creator.m_level + 1 : 0; - m_activityIdToRestore = activityIDToRestore; - - // Create a nice GUID that encodes the chain of activities that started this one. - CreateActivityPathGuid(out m_guid, out m_activityPathGuidOffset); - } - - public Guid ActivityId => m_guid; - - public static string Path(ActivityInfo? activityInfo) - { - if (activityInfo == null) - return ""; - return Path(activityInfo.m_creator) + "/" + activityInfo.m_uniqueId.ToString(); - } - - public override string ToString() - { - return m_name + "(" + Path(this) + (m_stopped != 0 ? ",DEAD)" : ")"); - } - - public static string LiveActivities(ActivityInfo? list) - { - if (list == null) - return ""; - return list.ToString() + ";" + LiveActivities(list.m_creator); - } - - public bool CanBeOrphan() - { - if ((m_eventOptions & EventActivityOptions.Detachable) != 0) - return true; - return false; - } - - #region private - - #region CreateActivityPathGuid - /// <summary> - /// Logically every activity Path (see Path()) that describes the activities that caused this - /// (rooted in an activity that predates activity tracking. - /// - /// We wish to encode this path in the Guid to the extent that we can. Many of the paths have - /// many small numbers in them and we take advantage of this in the encoding to output as long - /// a path in the GUID as possible. - /// - /// Because of the possibility of GUID collision, we only use 96 of the 128 bits of the GUID - /// for encoding the path. The last 32 bits are a simple checksum (and random number) that - /// identifies this as using the convention defined here. - /// - /// It returns both the GUID which has the path as well as the offset that points just beyond - /// the end of the activity (so it can be appended to). Note that if the end is in a nibble - /// (it uses nibbles instead of bytes as the unit of encoding, then it will point at the unfinished - /// byte (since the top nibble can't be zero you can determine if this is true by seeing if - /// this byte is nonZero. This offset is needed to efficiently create the ID for child activities. - /// </summary> - private unsafe void CreateActivityPathGuid(out Guid idRet, out int activityPathGuidOffset) - { - fixed (Guid* outPtr = &idRet) - { - int activityPathGuidOffsetStart = 0; - if (m_creator != null) - { - activityPathGuidOffsetStart = m_creator.m_activityPathGuidOffset; - idRet = m_creator.m_guid; - } - else - { - // TODO FIXME - differentiate between AD inside PCL - int appDomainID = 0; -#if (!ES_BUILD_STANDALONE && !ES_BUILD_PN) - appDomainID = System.Threading.Thread.GetDomainID(); -#endif - // We start with the appdomain number to make this unique among appdomains. - activityPathGuidOffsetStart = AddIdToGuid(outPtr, activityPathGuidOffsetStart, (uint)appDomainID); - } - - activityPathGuidOffset = AddIdToGuid(outPtr, activityPathGuidOffsetStart, (uint)m_uniqueId); - - // If the path does not fit, Make a GUID by incrementing rather than as a path, keeping as much of the path as possible - if (12 < activityPathGuidOffset) - CreateOverflowGuid(outPtr); - } - } - - /// <summary> - /// If we can't fit the activity Path into the GUID we come here. What we do is simply - /// generate a 4 byte number (s_nextOverflowId). Then look for an ancestor that has - /// sufficient space for this ID. By doing this, we preserve the fact that this activity - /// is a child (of unknown depth) from that ancestor. - /// </summary> - private unsafe void CreateOverflowGuid(Guid* outPtr) - { - // Search backwards for an ancestor that has sufficient space to put the ID. - for (ActivityInfo? ancestor = m_creator; ancestor != null; ancestor = ancestor.m_creator) - { - if (ancestor.m_activityPathGuidOffset <= 10) // we need at least 2 bytes. - { - uint id = unchecked((uint)Interlocked.Increment(ref ancestor.m_lastChildID)); // Get a unique ID - // Try to put the ID into the GUID - *outPtr = ancestor.m_guid; - int endId = AddIdToGuid(outPtr, ancestor.m_activityPathGuidOffset, id, true); - - // Does it fit? - if (endId <= 12) - break; - } - } - } - - /// <summary> - /// The encoding for a list of numbers used to make Activity GUIDs. Basically - /// we operate on nibbles (which are nice because they show up as hex digits). The - /// list is ended with a end nibble (0) and depending on the nibble value (Below) - /// the value is either encoded into nibble itself or it can spill over into the - /// bytes that follow. - /// </summary> - private enum NumberListCodes : byte - { - End = 0x0, // ends the list. No valid value has this prefix. - LastImmediateValue = 0xA, - - PrefixCode = 0xB, // all the 'long' encodings go here. If the next nibble is MultiByte1-4 - // than this is a 'overflow' id. Unlike the hierarchical IDs these are - // allocated densely but don't tell you anything about nesting. we use - // these when we run out of space in the GUID to store the path. - - MultiByte1 = 0xC, // 1 byte follows. If this Nibble is in the high bits, it the high bits of the number are stored in the low nibble. - // commented out because the code does not explicitly reference the names (but they are logically defined). - // MultiByte2 = 0xD, // 2 bytes follow (we don't bother with the nibble optimization) - // MultiByte3 = 0xE, // 3 bytes follow (we don't bother with the nibble optimization) - // MultiByte4 = 0xF, // 4 bytes follow (we don't bother with the nibble optimization) - } - - /// Add the activity id 'id' to the output Guid 'outPtr' starting at the offset 'whereToAddId' - /// Thus if this number is 6 that is where 'id' will be added. This will return 13 (12 - /// is the maximum number of bytes that fit in a GUID) if the path did not fit. - /// If 'overflow' is true, then the number is encoded as an 'overflow number (which has a - /// special (longer prefix) that indicates that this ID is allocated differently - private static unsafe int AddIdToGuid(Guid* outPtr, int whereToAddId, uint id, bool overflow = false) - { - byte* ptr = (byte*)outPtr; - byte* endPtr = ptr + 12; - ptr += whereToAddId; - if (endPtr <= ptr) - return 13; // 12 means we might exactly fit, 13 means we definitely did not fit - - if (0 < id && id <= (uint)NumberListCodes.LastImmediateValue && !overflow) - WriteNibble(ref ptr, endPtr, id); - else - { - uint len = 4; - if (id <= 0xFF) - len = 1; - else if (id <= 0xFFFF) - len = 2; - else if (id <= 0xFFFFFF) - len = 3; - - if (overflow) - { - if (endPtr <= ptr + 2) // I need at least 2 bytes - return 13; - - // Write out the prefix code nibble and the length nibble - WriteNibble(ref ptr, endPtr, (uint)NumberListCodes.PrefixCode); - } - // The rest is the same for overflow and non-overflow case - WriteNibble(ref ptr, endPtr, (uint)NumberListCodes.MultiByte1 + (len - 1)); - - // Do we have an odd nibble? If so flush it or use it for the 12 byte case. - if (ptr < endPtr && *ptr != 0) - { - // If the value < 4096 we can use the nibble we are otherwise just outputting as padding. - if (id < 4096) - { - // Indicate this is a 1 byte multicode with 4 high order bits in the lower nibble. - *ptr = (byte)(((uint)NumberListCodes.MultiByte1 << 4) + (id >> 8)); - id &= 0xFF; // Now we only want the low order bits. - } - ptr++; - } - - // Write out the bytes. - while (0 < len) - { - if (endPtr <= ptr) - { - ptr++; // Indicate that we have overflowed - break; - } - *ptr++ = (byte)id; - id >>= 8; - --len; - } - } - - // Compute the checksum - uint* sumPtr = (uint*)outPtr; - // We set the last DWORD the sum of the first 3 DWORDS in the GUID. This - // This last number is a random number (it identifies us as us) the process ID to make it unique per process. - sumPtr[3] = (sumPtr[0] + sumPtr[1] + sumPtr[2] + 0x599D99AD) ^ EventSource.s_currentPid; - - return (int)(ptr - ((byte*)outPtr)); - } - - /// <summary> - /// Write a single Nible 'value' (must be 0-15) to the byte buffer represented by *ptr. - /// Will not go past 'endPtr'. Also it assumes that we never write 0 so we can detect - /// whether a nibble has already been written to ptr because it will be nonzero. - /// Thus if it is non-zero it adds to the current byte, otherwise it advances and writes - /// the new byte (in the high bits) of the next byte. - /// </summary> - private static unsafe void WriteNibble(ref byte* ptr, byte* endPtr, uint value) - { - Debug.Assert(value < 16); - Debug.Assert(ptr < endPtr); - - if (*ptr != 0) - *ptr++ |= (byte)value; - else - *ptr = (byte)(value << 4); - } - - #endregion // CreateGuidForActivityPath - - internal readonly string m_name; // The name used in the 'start' and 'stop' APIs to help match up - private readonly long m_uniqueId; // a small number that makes this activity unique among its siblings - internal readonly Guid m_guid; // Activity Guid, it is basically an encoding of the Path() (see CreateActivityPathGuid) - internal readonly int m_activityPathGuidOffset; // Keeps track of where in m_guid the causality path stops (used to generated child GUIDs) - internal readonly int m_level; // current depth of the Path() of the activity (used to keep recursion under control) - internal readonly EventActivityOptions m_eventOptions; // Options passed to start. - internal long m_lastChildID; // used to create a unique ID for my children activities - internal int m_stopped; // This work item has stopped - internal readonly ActivityInfo? m_creator; // My parent (creator). Forms the Path() for the activity. - internal readonly Guid m_activityIdToRestore; // The Guid to restore after a stop. - #endregion - } - - // This callback is used to initialize the m_current AsyncLocal Variable. - // Its job is to keep the ETW Activity ID (part of thread local storage) in sync - // with m_current.ActivityID - private void ActivityChanging(AsyncLocalValueChangedArgs<ActivityInfo?> args) - { - ActivityInfo? cur = args.CurrentValue; - ActivityInfo? prev = args.PreviousValue; - - // Are we popping off a value? (we have a prev, and it creator is cur) - // Then check if we should use the GUID at the time of the start event - if (prev != null && prev.m_creator == cur) - { - // If the saved activity ID is not the same as the creator activity - // that takes precedence (it means someone explicitly did a SetActivityID) - // Set it to that and get out - if (cur == null || prev.m_activityIdToRestore != cur.ActivityId) - { - EventSource.SetCurrentThreadActivityId(prev.m_activityIdToRestore); - return; - } - } - - // OK we did not have an explicit SetActivityID set. Then we should be - // setting the activity to current ActivityInfo. However that activity - // might be dead, in which case we should skip it, so we never set - // the ID to dead things. - while (cur != null) - { - // We found a live activity (typically the first time), set it to that. - if (cur.m_stopped == 0) - { - EventSource.SetCurrentThreadActivityId(cur.ActivityId); - return; - } - cur = cur.m_creator; - } - // we can get here if there is no information on our activity stack (everything is dead) - // currently we do nothing, as that seems better than setting to Guid.Emtpy. - } - - /// <summary> - /// Async local variables have the property that the are automatically copied whenever a task is created and used - /// while that task is running. Thus m_current 'flows' to any task that is caused by the current thread that - /// last set it. - /// - /// This variable points to a linked list that represents all Activities that have started but have not stopped. - /// </summary> - private AsyncLocal<ActivityInfo?>? m_current; - private bool m_checkedForEnable; - - // Singleton - private static readonly ActivityTracker s_activityTrackerInstance = new ActivityTracker(); - - // Used to create unique IDs at the top level. Not used for nested Ids (each activity has its own id generator) - private static long m_nextId = 0; - private const ushort MAX_ACTIVITY_DEPTH = 100; // Limit maximum depth of activities to be tracked at 100. - // This will avoid leaking memory in case of activities that are never stopped. - - #endregion - } - -#if ES_BUILD_STANDALONE - /******************************** SUPPORT *****************************/ - /// <summary> - /// This is supplied by the framework. It is has the semantics that the value is copied to any new Tasks that is created - /// by the current task. Thus all causally related code gets this value. Note that reads and writes to this VARIABLE - /// (not what it points it) to this does not need to be protected by locks because it is inherently thread local (you always - /// only get your thread local copy which means that you never have races. - /// </summary> - /// - [EventSource(Name = "Microsoft.Tasks.Nuget")] - internal class TplEventSource : EventSource - { - public class Keywords - { - public const EventKeywords TasksFlowActivityIds = (EventKeywords)0x80; - public const EventKeywords Debug = (EventKeywords)0x20000; - } - - public static TplEventSource Log = new TplEventSource(); - public bool Debug { get { return IsEnabled(EventLevel.Verbose, Keywords.Debug); } } - - public void DebugFacilityMessage(string Facility, string Message) { WriteEvent(1, Facility, Message); } - public void DebugFacilityMessage1(string Facility, string Message, string Arg) { WriteEvent(2, Facility, Message, Arg); } - public void SetActivityId(Guid Id) { WriteEvent(3, Id); } - } -#endif - -#if ES_BUILD_AGAINST_DOTNET_V35 || ES_BUILD_PCL || NO_ASYNC_LOCAL - // In these cases we don't have any Async local support. Do nothing. - internal sealed class AsyncLocalValueChangedArgs<T> - { - public T PreviousValue { get { return default(T); } } - public T CurrentValue { get { return default(T); } } - - } - - internal sealed class AsyncLocal<T> - { - public AsyncLocal(Action<AsyncLocalValueChangedArgs<T>> valueChangedHandler) { - throw new NotImplementedException("AsyncLocal only available on V4.6 and above"); - } - public T Value - { - get { return default(T); } - set { } - } - } -#endif - -} diff --git a/netcore/System.Private.CoreLib/shared/System/Diagnostics/Tracing/CounterGroup.cs b/netcore/System.Private.CoreLib/shared/System/Diagnostics/Tracing/CounterGroup.cs deleted file mode 100644 index e4086d1d6e2..00000000000 --- a/netcore/System.Private.CoreLib/shared/System/Diagnostics/Tracing/CounterGroup.cs +++ /dev/null @@ -1,263 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -#if ES_BUILD_STANDALONE -using System; -using System.Diagnostics; -#endif -using System.Collections.Generic; -using System.Threading; - -#if ES_BUILD_STANDALONE -namespace Microsoft.Diagnostics.Tracing -#else -namespace System.Diagnostics.Tracing -#endif -{ - internal class CounterGroup - { - private readonly EventSource _eventSource; - private readonly List<DiagnosticCounter> _counters; - private static readonly object s_counterGroupLock = new object(); - - internal CounterGroup(EventSource eventSource) - { - _eventSource = eventSource; - _counters = new List<DiagnosticCounter>(); - RegisterCommandCallback(); - } - - internal void Add(DiagnosticCounter eventCounter) - { - lock (s_counterGroupLock) // Lock the CounterGroup - _counters.Add(eventCounter); - } - - internal void Remove(DiagnosticCounter eventCounter) - { - lock (s_counterGroupLock) // Lock the CounterGroup - _counters.Remove(eventCounter); - } - -#region EventSource Command Processing - - private void RegisterCommandCallback() - { - _eventSource.EventCommandExecuted += OnEventSourceCommand; - } - - private void OnEventSourceCommand(object? sender, EventCommandEventArgs e) - { - if (e.Command == EventCommand.Enable || e.Command == EventCommand.Update) - { - Debug.Assert(e.Arguments != null); - - if (e.Arguments.TryGetValue("EventCounterIntervalSec", out string? valueStr) && float.TryParse(valueStr, out float value)) - { - lock (s_counterGroupLock) // Lock the CounterGroup - { - EnableTimer(value); - } - } - } - else if (e.Command == EventCommand.Disable) - { - lock (s_counterGroupLock) - { - DisableTimer(); - } - } - } - -#endregion // EventSource Command Processing - -#region Global CounterGroup Array management - - // We need eventCounters to 'attach' themselves to a particular EventSource. - // this table provides the mapping from EventSource -> CounterGroup - // which represents this 'attached' information. - private static WeakReference<CounterGroup>[]? s_counterGroups; - - private static void EnsureEventSourceIndexAvailable(int eventSourceIndex) - { - Debug.Assert(Monitor.IsEntered(s_counterGroupLock)); - if (CounterGroup.s_counterGroups == null) - { - CounterGroup.s_counterGroups = new WeakReference<CounterGroup>[eventSourceIndex + 1]; - } - else if (eventSourceIndex >= CounterGroup.s_counterGroups.Length) - { - WeakReference<CounterGroup>[] newCounterGroups = new WeakReference<CounterGroup>[eventSourceIndex + 1]; - Array.Copy(CounterGroup.s_counterGroups, newCounterGroups, CounterGroup.s_counterGroups.Length); - CounterGroup.s_counterGroups = newCounterGroups; - } - } - - internal static CounterGroup GetCounterGroup(EventSource eventSource) - { - lock (s_counterGroupLock) - { - int eventSourceIndex = EventListener.EventSourceIndex(eventSource); - EnsureEventSourceIndexAvailable(eventSourceIndex); - Debug.Assert(s_counterGroups != null); - WeakReference<CounterGroup> weakRef = CounterGroup.s_counterGroups[eventSourceIndex]; - CounterGroup? ret = null; - if (weakRef == null || !weakRef.TryGetTarget(out ret)) - { - ret = new CounterGroup(eventSource); - CounterGroup.s_counterGroups[eventSourceIndex] = new WeakReference<CounterGroup>(ret); - } - return ret; - } - } - -#endregion // Global CounterGroup Array management - -#region Timer Processing - - private DateTime _timeStampSinceCollectionStarted; - private int _pollingIntervalInMilliseconds; - private DateTime _nextPollingTimeStamp; - - private void EnableTimer(float pollingIntervalInSeconds) - { - Debug.Assert(Monitor.IsEntered(s_counterGroupLock)); - if (pollingIntervalInSeconds <= 0) - { - _pollingIntervalInMilliseconds = 0; - } - else if (_pollingIntervalInMilliseconds == 0 || pollingIntervalInSeconds * 1000 < _pollingIntervalInMilliseconds) - { - _pollingIntervalInMilliseconds = (int)(pollingIntervalInSeconds * 1000); - ResetCounters(); // Reset statistics for counters before we start the thread. - - _timeStampSinceCollectionStarted = DateTime.UtcNow; - // Don't capture the current ExecutionContext and its AsyncLocals onto the timer causing them to live forever - bool restoreFlow = false; - try - { - if (!ExecutionContext.IsFlowSuppressed()) - { - ExecutionContext.SuppressFlow(); - restoreFlow = true; - } - - _nextPollingTimeStamp = DateTime.UtcNow + new TimeSpan(0, 0, (int)pollingIntervalInSeconds); - - // Create the polling thread and init all the shared state if needed - if (s_pollingThread == null) - { - s_pollingThreadSleepEvent = new AutoResetEvent(false); - s_counterGroupEnabledList = new List<CounterGroup>(); - s_pollingThread = new Thread(PollForValues) { IsBackground = true }; - s_pollingThread.Start(); - } - - if (!s_counterGroupEnabledList!.Contains(this)) - { - s_counterGroupEnabledList.Add(this); - } - - // notify the polling thread that the polling interval may have changed and the sleep should - // be recomputed - s_pollingThreadSleepEvent!.Set(); - } - finally - { - // Restore the current ExecutionContext - if (restoreFlow) - ExecutionContext.RestoreFlow(); - } - } - } - - private void DisableTimer() - { - _pollingIntervalInMilliseconds = 0; - s_counterGroupEnabledList?.Remove(this); - } - - private void ResetCounters() - { - lock (s_counterGroupLock) // Lock the CounterGroup - { - foreach (DiagnosticCounter counter in _counters) - { - if (counter is IncrementingEventCounter ieCounter) - { - ieCounter.UpdateMetric(); - } - else if (counter is IncrementingPollingCounter ipCounter) - { - ipCounter.UpdateMetric(); - } - else if (counter is EventCounter eCounter) - { - eCounter.ResetStatistics(); - } - } - } - } - - private void OnTimer() - { - Debug.Assert(Monitor.IsEntered(s_counterGroupLock)); - if (_eventSource.IsEnabled()) - { - DateTime now = DateTime.UtcNow; - TimeSpan elapsed = now - _timeStampSinceCollectionStarted; - - foreach (DiagnosticCounter counter in _counters) - { - counter.WritePayload((float)elapsed.TotalSeconds, _pollingIntervalInMilliseconds); - } - _timeStampSinceCollectionStarted = now; - - do - { - _nextPollingTimeStamp += new TimeSpan(0, 0, 0, 0, _pollingIntervalInMilliseconds); - } while (_nextPollingTimeStamp <= now); - } - } - - private static Thread? s_pollingThread; - // Used for sleeping for a certain amount of time while allowing the thread to be woken up - private static AutoResetEvent? s_pollingThreadSleepEvent; - - private static List<CounterGroup>? s_counterGroupEnabledList; - - private static void PollForValues() - { - AutoResetEvent? sleepEvent = null; - while (true) - { - int sleepDurationInMilliseconds = int.MaxValue; - lock (s_counterGroupLock) - { - sleepEvent = s_pollingThreadSleepEvent; - foreach (CounterGroup counterGroup in s_counterGroupEnabledList!) - { - DateTime now = DateTime.UtcNow; - if (counterGroup._nextPollingTimeStamp < now + new TimeSpan(0, 0, 0, 0, 1)) - { - counterGroup.OnTimer(); - } - - int millisecondsTillNextPoll = (int)((counterGroup._nextPollingTimeStamp - now).TotalMilliseconds); - millisecondsTillNextPoll = Math.Max(1, millisecondsTillNextPoll); - sleepDurationInMilliseconds = Math.Min(sleepDurationInMilliseconds, millisecondsTillNextPoll); - } - } - if (sleepDurationInMilliseconds == int.MaxValue) - { - sleepDurationInMilliseconds = -1; // WaitOne uses -1 to mean infinite - } - sleepEvent?.WaitOne(sleepDurationInMilliseconds); - } - } - -#endregion // Timer Processing - - } -} diff --git a/netcore/System.Private.CoreLib/shared/System/Diagnostics/Tracing/CounterPayload.cs b/netcore/System.Private.CoreLib/shared/System/Diagnostics/Tracing/CounterPayload.cs deleted file mode 100644 index d81fdafca31..00000000000 --- a/netcore/System.Private.CoreLib/shared/System/Diagnostics/Tracing/CounterPayload.cs +++ /dev/null @@ -1,130 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -#if ES_BUILD_STANDALONE -using System; -using System.Diagnostics; -#endif -using System.Collections; -using System.Collections.Generic; - -#if ES_BUILD_STANDALONE -namespace Microsoft.Diagnostics.Tracing -#else -namespace System.Diagnostics.Tracing -#endif -{ - [EventData] - internal class CounterPayload : IEnumerable<KeyValuePair<string, object?>> - { - public string? Name { get; set; } - - public string? DisplayName { get; set; } - - public double Mean { get; set; } - - public double StandardDeviation { get; set; } - - public int Count { get; set; } - - public double Min { get; set; } - - public double Max { get; set; } - - public float IntervalSec { get; internal set; } - - public string? Series { get; set; } - - public string? CounterType { get; set; } - - public string? Metadata { get; set; } - - public string? DisplayUnits { get; set; } - -#region Implementation of the IEnumerable interface - - public IEnumerator<KeyValuePair<string, object?>> GetEnumerator() - { - return ForEnumeration.GetEnumerator(); - } - - IEnumerator IEnumerable.GetEnumerator() - { - return ForEnumeration.GetEnumerator(); - } - - private IEnumerable<KeyValuePair<string, object?>> ForEnumeration - { - get - { - yield return new KeyValuePair<string, object?>("Name", Name); - yield return new KeyValuePair<string, object?>("DisplayName", DisplayName); - yield return new KeyValuePair<string, object?>("DisplayUnits", DisplayUnits); - yield return new KeyValuePair<string, object?>("Mean", Mean); - yield return new KeyValuePair<string, object?>("StandardDeviation", StandardDeviation); - yield return new KeyValuePair<string, object?>("Count", Count); - yield return new KeyValuePair<string, object?>("Min", Min); - yield return new KeyValuePair<string, object?>("Max", Max); - yield return new KeyValuePair<string, object?>("IntervalSec", IntervalSec); - yield return new KeyValuePair<string, object?>("Series", $"Interval={IntervalSec}"); - yield return new KeyValuePair<string, object?>("CounterType", "Mean"); - yield return new KeyValuePair<string, object?>("Metadata", Metadata); - } - } - -#endregion // Implementation of the IEnumerable interface - } - - [EventData] - internal class IncrementingCounterPayload : IEnumerable<KeyValuePair<string, object?>> - { - public string? Name { get; set; } - - public string? DisplayName { get; set; } - - public string? DisplayRateTimeScale { get; set; } - - public double Increment { get; set; } - - public float IntervalSec { get; internal set; } - - public string? Metadata { get; set; } - - public string? Series { get; set; } - - public string? CounterType { get; set; } - - public string? DisplayUnits { get; set; } - -#region Implementation of the IEnumerable interface - - public IEnumerator<KeyValuePair<string, object?>> GetEnumerator() - { - return ForEnumeration.GetEnumerator(); - } - - IEnumerator IEnumerable.GetEnumerator() - { - return ForEnumeration.GetEnumerator(); - } - - private IEnumerable<KeyValuePair<string, object?>> ForEnumeration - { - get - { - yield return new KeyValuePair<string, object?>("Name", Name); - yield return new KeyValuePair<string, object?>("DisplayName", DisplayName); - yield return new KeyValuePair<string, object?>("DisplayRateTimeScale", DisplayRateTimeScale); - yield return new KeyValuePair<string, object?>("Increment", Increment); - yield return new KeyValuePair<string, object?>("IntervalSec", IntervalSec); - yield return new KeyValuePair<string, object?>("Series", $"Interval={IntervalSec}"); - yield return new KeyValuePair<string, object?>("CounterType", "Sum"); - yield return new KeyValuePair<string, object?>("Metadata", Metadata); - yield return new KeyValuePair<string, object?>("DisplayUnits", DisplayUnits); - } - } - -#endregion // Implementation of the IEnumerable interface - } -} diff --git a/netcore/System.Private.CoreLib/shared/System/Diagnostics/Tracing/DiagnosticCounter.cs b/netcore/System.Private.CoreLib/shared/System/Diagnostics/Tracing/DiagnosticCounter.cs deleted file mode 100644 index 577e9f193f8..00000000000 --- a/netcore/System.Private.CoreLib/shared/System/Diagnostics/Tracing/DiagnosticCounter.cs +++ /dev/null @@ -1,168 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -#if ES_BUILD_STANDALONE -using System; -using System.Diagnostics; -#endif -using System.Collections.Generic; -using System.Text; -using System.Threading; - -#if ES_BUILD_STANDALONE -namespace Microsoft.Diagnostics.Tracing -#else -namespace System.Diagnostics.Tracing -#endif -{ - /// <summary> - /// DiagnosticCounter is an abstract class that serves as the parent class for various Counter* classes, - /// namely EventCounter, PollingCounter, IncrementingEventCounter, and IncrementingPollingCounter. - /// </summary> - public abstract class DiagnosticCounter : IDisposable - { - /// <summary> - /// All Counters live as long as the EventSource that they are attached to unless they are - /// explicitly Disposed. - /// </summary> - /// <param name="name">The name.</param> - /// <param name="eventSource">The event source.</param> - internal DiagnosticCounter(string name, EventSource eventSource) - { - if (name == null) - { - throw new ArgumentNullException(nameof(Name)); - } - - if (eventSource == null) - { - throw new ArgumentNullException(nameof(EventSource)); - } - - Name = name; - EventSource = eventSource; - } - - /// <summary>Adds the counter to the set that the EventSource will report on.</summary> - /// <remarks> - /// Must only be invoked once, and only after the instance has been fully initialized. - /// This should be invoked by a derived type's ctor as the last thing it does. - /// </remarks> - private protected void Publish() - { - Debug.Assert(_group is null); - Debug.Assert(Name != null); - Debug.Assert(EventSource != null); - - _group = CounterGroup.GetCounterGroup(EventSource); - _group.Add(this); - } - - /// <summary> - /// Removes the counter from set that the EventSource will report on. After being disposed, this - /// counter will do nothing and its resource will be reclaimed if all references to it are removed. - /// If an EventCounter is not explicitly disposed it will be cleaned up automatically when the - /// EventSource it is attached to dies. - /// </summary> - public void Dispose() - { - if (_group != null) - { - _group.Remove(this); - _group = null; - } - } - - /// <summary> - /// Adds a key-value metadata to the EventCounter that will be included as a part of the payload - /// </summary> - public void AddMetadata(string key, string? value) - { - lock (this) - { - _metadata ??= new Dictionary<string, string?>(); - _metadata.Add(key, value); - } - } - - private string _displayName = ""; - public string DisplayName - { - get => _displayName; - set - { - if (value == null) - throw new ArgumentNullException(nameof(DisplayName)); - _displayName = value; - } - } - - private string _displayUnits = ""; - public string DisplayUnits - { - get => _displayUnits; - set - { - if (value == null) - throw new ArgumentNullException(nameof(DisplayUnits)); - _displayUnits = value; - } - } - - public string Name { get; } - - public EventSource EventSource { get; } - - #region private implementation - - private CounterGroup? _group; - private Dictionary<string, string?>? _metadata; - - internal abstract void WritePayload(float intervalSec, int pollingIntervalMillisec); - - internal void ReportOutOfBandMessage(string message) - { - EventSource.ReportOutOfBandMessage(message); - } - - internal string GetMetadataString() - { - Debug.Assert(Monitor.IsEntered(this)); - - if (_metadata == null) - { - return ""; - } - - // The dictionary is only initialized to non-null when there's metadata to add, and no items - // are ever removed, so if the dictionary is non-null, there must also be at least one element. - Dictionary<string, string?>.Enumerator enumerator = _metadata.GetEnumerator(); - Debug.Assert(_metadata.Count > 0); - bool gotOne = enumerator.MoveNext(); - Debug.Assert(gotOne); - - // If there's only one element, just concat a string for it. - KeyValuePair<string, string?> current = enumerator.Current; - if (!enumerator.MoveNext()) - { - return current.Key + ":" + current.Value; - } - - // Otherwise, append it, then append the element we moved to, and then - // iterate through the remainder of the elements, appending each. - StringBuilder sb = new StringBuilder().Append(current.Key).Append(':').Append(current.Value); - do - { - current = enumerator.Current; - sb.Append(',').Append(current.Key).Append(':').Append(current.Value); - } - while (enumerator.MoveNext()); - - // Return the final string. - return sb.ToString(); - } - - #endregion // private implementation - } -} diff --git a/netcore/System.Private.CoreLib/shared/System/Diagnostics/Tracing/EventActivityOptions.cs b/netcore/System.Private.CoreLib/shared/System/Diagnostics/Tracing/EventActivityOptions.cs deleted file mode 100644 index 02f60379c1e..00000000000 --- a/netcore/System.Private.CoreLib/shared/System/Diagnostics/Tracing/EventActivityOptions.cs +++ /dev/null @@ -1,41 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -#if ES_BUILD_STANDALONE -using System; -#endif - -#if ES_BUILD_STANDALONE -namespace Microsoft.Diagnostics.Tracing -#else -namespace System.Diagnostics.Tracing -#endif -{ - /// <summary> - /// EventActivityOptions flags allow to specify different activity related characteristics. - /// </summary> - [Flags] - public enum EventActivityOptions - { - /// <summary> - /// No special options are added to the event. - /// </summary> - None = 0, - - /// <summary> - /// Disable Implicit Activity Tracking - /// </summary> - Disable = 0x2, - - /// <summary> - /// Allow activity event to call itself (directly or indirectly) - /// </summary> - Recursive = 0x4, - - /// <summary> - /// Allows event activity to live beyond its parent. - /// </summary> - Detachable = 0x8 - } -} diff --git a/netcore/System.Private.CoreLib/shared/System/Diagnostics/Tracing/EventCounter.cs b/netcore/System.Private.CoreLib/shared/System/Diagnostics/Tracing/EventCounter.cs deleted file mode 100644 index ae3a5d7fceb..00000000000 --- a/netcore/System.Private.CoreLib/shared/System/Diagnostics/Tracing/EventCounter.cs +++ /dev/null @@ -1,202 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -#if ES_BUILD_STANDALONE -using System; -using System.Diagnostics; -#endif -using System.Threading; - -#if ES_BUILD_STANDALONE -namespace Microsoft.Diagnostics.Tracing -#else -namespace System.Diagnostics.Tracing -#endif -{ - /// <summary> - /// Provides the ability to collect statistics through EventSource - /// - /// See https://github.com/dotnet/corefx/blob/master/src/System.Diagnostics.Tracing/documentation/EventCounterTutorial.md - /// for a tutorial guide. - /// - /// See https://github.com/dotnet/corefx/blob/master/src/System.Diagnostics.Tracing/tests/BasicEventSourceTest/TestEventCounter.cs - /// which shows tests, which are also useful in seeing actual use. - /// </summary> - public partial class EventCounter : DiagnosticCounter - { - /// <summary> - /// Initializes a new instance of the <see cref="EventCounter"/> class. - /// EVentCounters live as long as the EventSource that they are attached to unless they are - /// explicitly Disposed. - /// </summary> - /// <param name="name">The name.</param> - /// <param name="eventSource">The event source.</param> - public EventCounter(string name, EventSource eventSource) : base(name, eventSource) - { - _min = double.PositiveInfinity; - _max = double.NegativeInfinity; - - InitializeBuffer(); - Publish(); - } - - /// <summary> - /// Writes 'value' to the stream of values tracked by the counter. This updates the sum and other statistics that will - /// be logged on the next timer interval. - /// </summary> - /// <param name="value">The value.</param> - public void WriteMetric(float value) - { - Enqueue((double)value); - } - - public void WriteMetric(double value) - { - Enqueue(value); - } - - public override string ToString() - { - int count = Volatile.Read(ref _count); - return count == 0 ? - $"EventCounter '{Name}' Count 0" : - $"EventCounter '{Name}' Count {count} Mean {(_sum / count).ToString("n3")}"; - } - - #region Statistics Calculation - - // Statistics - private int _count; - private double _sum; - private double _sumSquared; - private double _min; - private double _max; - - internal void OnMetricWritten(double value) - { - Debug.Assert(Monitor.IsEntered(this)); - _sum += value; - _sumSquared += value * value; - if (value > _max) - _max = value; - - if (value < _min) - _min = value; - - _count++; - } - - internal override void WritePayload(float intervalSec, int pollingIntervalMillisec) - { - lock (this) - { - Flush(); - CounterPayload payload = new CounterPayload(); - payload.Count = _count; - payload.IntervalSec = intervalSec; - if (0 < _count) - { - payload.Mean = _sum / _count; - payload.StandardDeviation = Math.Sqrt(_sumSquared / _count - _sum * _sum / _count / _count); - } - else - { - payload.Mean = 0; - payload.StandardDeviation = 0; - } - payload.Min = _min; - payload.Max = _max; - payload.Series = $"Interval={pollingIntervalMillisec}"; // TODO: This may need to change when we support multi-session - payload.CounterType = "Mean"; - payload.Metadata = GetMetadataString(); - payload.DisplayName = DisplayName ?? ""; - payload.DisplayUnits = DisplayUnits ?? ""; - payload.Name = Name; - ResetStatistics(); - EventSource.Write("EventCounters", new EventSourceOptions() { Level = EventLevel.LogAlways }, new CounterPayloadType(payload)); - } - } - - internal void ResetStatistics() - { - lock (this) - { - _count = 0; - _sum = 0; - _sumSquared = 0; - _min = double.PositiveInfinity; - _max = double.NegativeInfinity; - } - } - - #endregion // Statistics Calculation - - // Values buffering - private const int BufferedSize = 10; - private const double UnusedBufferSlotValue = double.NegativeInfinity; - private volatile double[] _bufferedValues = null!; - private volatile int _bufferedValuesIndex; - - private void InitializeBuffer() - { - _bufferedValues = new double[BufferedSize]; - for (int i = 0; i < _bufferedValues.Length; i++) - { - _bufferedValues[i] = UnusedBufferSlotValue; - } - } - - private void Enqueue(double value) - { - // It is possible that two threads read the same bufferedValuesIndex, but only one will be able to write the slot, so that is okay. - int i = _bufferedValuesIndex; - while (true) - { - double result = Interlocked.CompareExchange(ref _bufferedValues[i], value, UnusedBufferSlotValue); - i++; - if (_bufferedValues.Length <= i) - { - // It is possible that two threads both think the buffer is full, but only one get to actually flush it, the other - // will eventually enter this code path and potentially calling Flushing on a buffer that is not full, and that's okay too. - lock (this) // Lock the counter - Flush(); - i = 0; - } - - if (result == UnusedBufferSlotValue) - { - // CompareExchange succeeded - _bufferedValuesIndex = i; - return; - } - } - } - - protected void Flush() - { - Debug.Assert(Monitor.IsEntered(this)); - for (int i = 0; i < _bufferedValues.Length; i++) - { - double value = Interlocked.Exchange(ref _bufferedValues[i], UnusedBufferSlotValue); - if (value != UnusedBufferSlotValue) - { - OnMetricWritten(value); - } - } - - _bufferedValuesIndex = 0; - } - } - - - /// <summary> - /// This is the payload that is sent in the with EventSource.Write - /// </summary> - [EventData] - internal class CounterPayloadType - { - public CounterPayloadType(CounterPayload payload) { Payload = payload; } - public CounterPayload Payload { get; set; } - } -} diff --git a/netcore/System.Private.CoreLib/shared/System/Diagnostics/Tracing/EventDescriptor.cs b/netcore/System.Private.CoreLib/shared/System/Diagnostics/Tracing/EventDescriptor.cs deleted file mode 100644 index 02ba4d715ea..00000000000 --- a/netcore/System.Private.CoreLib/shared/System/Diagnostics/Tracing/EventDescriptor.cs +++ /dev/null @@ -1,145 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -#if ES_BUILD_STANDALONE -using System; -using Environment = Microsoft.Diagnostics.Tracing.Internal.Environment; -#endif -using System.Runtime.InteropServices; - -#if ES_BUILD_STANDALONE -namespace Microsoft.Diagnostics.Tracing -#else -namespace System.Diagnostics.Tracing -#endif -{ - [StructLayout(LayoutKind.Explicit, Size = 16)] -#if ES_BUILD_STANDALONE - [System.Security.Permissions.HostProtection(MayLeakOnAbort = true)] -#endif - - /* - EventDescriptor was public in the separate System.Diagnostics.Tracing assembly(pre NS2.0), - now the move to CoreLib marked them as private. - While they are technically private (it's a contract used between the library and the ILC toolchain), - we need them to be rooted and exported from shared library for the system to work. - For now I'm simply marking them as public again.A cleaner solution might be to use.rd.xml to - root them and modify shared library definition to force export them. - */ -#if ES_BUILD_PN - public -#else - internal -#endif - struct EventDescriptor - { - #region private - [FieldOffset(0)] - private readonly int m_traceloggingId; - [FieldOffset(0)] - private readonly ushort m_id; - [FieldOffset(2)] - private readonly byte m_version; - [FieldOffset(3)] - private readonly byte m_channel; - [FieldOffset(4)] - private readonly byte m_level; - [FieldOffset(5)] - private readonly byte m_opcode; - [FieldOffset(6)] - private readonly ushort m_task; - [FieldOffset(8)] - private readonly long m_keywords; - #endregion - - public EventDescriptor( - int traceloggingId, - byte level, - byte opcode, - long keywords - ) - { - this.m_id = 0; - this.m_version = 0; - this.m_channel = 0; - this.m_traceloggingId = traceloggingId; - this.m_level = level; - this.m_opcode = opcode; - this.m_task = 0; - this.m_keywords = keywords; - } - - public EventDescriptor( - int id, - byte version, - byte channel, - byte level, - byte opcode, - int task, - long keywords - ) - { - if (id < 0) - { - throw new ArgumentOutOfRangeException(nameof(id), SR.ArgumentOutOfRange_NeedNonNegNum); - } - - if (id > ushort.MaxValue) - { - throw new ArgumentOutOfRangeException(nameof(id), SR.Format(SR.ArgumentOutOfRange_NeedValidId, 1, ushort.MaxValue)); - } - - m_traceloggingId = 0; - m_id = (ushort)id; - m_version = version; - m_channel = channel; - m_level = level; - m_opcode = opcode; - m_keywords = keywords; - - if (task < 0) - { - throw new ArgumentOutOfRangeException(nameof(task), SR.ArgumentOutOfRange_NeedNonNegNum); - } - - if (task > ushort.MaxValue) - { - throw new ArgumentOutOfRangeException(nameof(task), SR.Format(SR.ArgumentOutOfRange_NeedValidId, 1, ushort.MaxValue)); - } - - m_task = (ushort)task; - } - - public int EventId => m_id; - public byte Version => m_version; - public byte Channel => m_channel; - public byte Level => m_level; - public byte Opcode => m_opcode; - public int Task => m_task; - public long Keywords => m_keywords; - - internal int TraceLoggingId => m_traceloggingId; - - public override bool Equals(object? obj) => - obj is EventDescriptor ed && Equals(ed); - - public override int GetHashCode() => - m_id ^ m_version ^ m_channel ^ m_level ^ m_opcode ^ m_task ^ (int)m_keywords; - - public bool Equals(EventDescriptor other) => - m_id == other.m_id && - m_version == other.m_version && - m_channel == other.m_channel && - m_level == other.m_level && - m_opcode == other.m_opcode && - m_task == other.m_task && - m_keywords == other.m_keywords; - - public static bool operator ==(EventDescriptor event1, EventDescriptor event2) => - event1.Equals(event2); - - public static bool operator !=(EventDescriptor event1, EventDescriptor event2) => - !event1.Equals(event2); - } -} diff --git a/netcore/System.Private.CoreLib/shared/System/Diagnostics/Tracing/EventProvider.cs b/netcore/System.Private.CoreLib/shared/System/Diagnostics/Tracing/EventProvider.cs deleted file mode 100644 index b1787019a16..00000000000 --- a/netcore/System.Private.CoreLib/shared/System/Diagnostics/Tracing/EventProvider.cs +++ /dev/null @@ -1,1353 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -#if ES_BUILD_STANDALONE -using Microsoft.Win32; -using System; -using System.Diagnostics; -using System.Security.Permissions; -using BitOperations = Microsoft.Diagnostics.Tracing.Internal.BitOperations; -#endif -using System.Collections.Generic; -using System.Globalization; -using System.Numerics; -using System.Runtime.InteropServices; -#if CORECLR && PLATFORM_WINDOWS -using Internal.Win32; -#endif -#if ES_BUILD_AGAINST_DOTNET_V35 -using Microsoft.Internal; // for Tuple (can't define alias for open generic types so we "use" the whole namespace) -#endif - -#if ES_BUILD_STANDALONE -namespace Microsoft.Diagnostics.Tracing -#else -namespace System.Diagnostics.Tracing -#endif -{ - internal enum EventProviderType - { - None = 0, - ETW, - EventPipe - } - - internal enum ControllerCommand - { - // Strictly Positive numbers are for provider-specific commands, negative number are for 'shared' commands. 256 - // The first 256 negative numbers are reserved for the framework. - Update = 0, // Not used by EventProviderBase. - SendManifest = -1, - Enable = -2, - Disable = -3, - } - - /// <summary> - /// Only here because System.Diagnostics.EventProvider needs one more extensibility hook (when it gets a - /// controller callback) - /// </summary> -#if ES_BUILD_STANDALONE - [System.Security.Permissions.HostProtection(MayLeakOnAbort = true)] -#endif - internal class EventProvider : IDisposable - { - // This is the windows EVENT_DATA_DESCRIPTOR structure. We expose it because this is what - // subclasses of EventProvider use when creating efficient (but unsafe) version of - // EventWrite. We do make it a nested type because we really don't expect anyone to use - // it except subclasses (and then only rarely). - [StructLayout(LayoutKind.Sequential)] - public struct EventData - { - internal unsafe ulong Ptr; - internal uint Size; - internal uint Reserved; - } - - /// <summary> - /// A struct characterizing ETW sessions (identified by the etwSessionId) as - /// activity-tracing-aware or legacy. A session that's activity-tracing-aware - /// has specified one non-zero bit in the reserved range 44-47 in the - /// 'allKeywords' value it passed in for a specific EventProvider. - /// </summary> - public struct SessionInfo - { - internal int sessionIdBit; // the index of the bit used for tracing in the "reserved" field of AllKeywords - internal int etwSessionId; // the machine-wide ETW session ID - - internal SessionInfo(int sessionIdBit_, int etwSessionId_) - { sessionIdBit = sessionIdBit_; etwSessionId = etwSessionId_; } - } - - internal IEventProvider m_eventProvider; // The interface that implements the specific logging mechanism functions. - private Interop.Advapi32.EtwEnableCallback? m_etwCallback; // Trace Callback function - private long m_regHandle; // Trace Registration Handle - private byte m_level; // Tracing Level - private long m_anyKeywordMask; // Trace Enable Flags - private long m_allKeywordMask; // Match all keyword - private List<SessionInfo>? m_liveSessions; // current live sessions (Tuple<sessionIdBit, etwSessionId>) - private bool m_enabled; // Enabled flag from Trace callback - private string? m_providerName; // Control name - private Guid m_providerId; // Control Guid - internal bool m_disposed; // when true provider has unregistered - - [ThreadStatic] - private static WriteEventErrorCode s_returnCode; // The last return code - - private const int s_basicTypeAllocationBufferSize = 16; - private const int s_etwMaxNumberArguments = 128; - private const int s_etwAPIMaxRefObjCount = 8; - private const int s_traceEventMaximumSize = 65482; - - public enum WriteEventErrorCode : int - { - // check mapping to runtime codes - NoError = 0, - NoFreeBuffers = 1, - EventTooBig = 2, - NullInput = 3, - TooManyArgs = 4, - Other = 5, - } - - // Because callbacks happen on registration, and we need the callbacks for those setup - // we can't call Register in the constructor. - // - // Note that EventProvider should ONLY be used by EventSource. In particular because - // it registers a callback from native code you MUST dispose it BEFORE shutdown, otherwise - // you may get native callbacks during shutdown when we have destroyed the delegate. - // EventSource has special logic to do this, no one else should be calling EventProvider. - internal EventProvider(EventProviderType providerType) - { - m_eventProvider = providerType switch - { -#if PLATFORM_WINDOWS - EventProviderType.ETW => new EtwEventProvider(), -#endif -#if FEATURE_PERFTRACING - EventProviderType.EventPipe => new EventPipeEventProvider(), -#endif - _ => new NoOpEventProvider(), - }; - } - - /// <summary> - /// This method registers the controlGuid of this class with ETW. We need to be running on - /// Vista or above. If not a PlatformNotSupported exception will be thrown. If for some - /// reason the ETW Register call failed a NotSupported exception will be thrown. - /// </summary> - // <SecurityKernel Critical="True" Ring="0"> - // <CallsSuppressUnmanagedCode Name="Interop.Advapi32.EventRegister(System.Guid&,Microsoft.Win32.Interop.Advapi32+EtwEnableCallback,System.Void*,System.Int64&):System.UInt32" /> - // <SatisfiesLinkDemand Name="Win32Exception..ctor(System.Int32)" /> - // <ReferencesCritical Name="Method: EtwEnableCallBack(Guid&, Int32, Byte, Int64, Int64, Void*, Void*):Void" Ring="1" /> - // </SecurityKernel> - internal unsafe void Register(EventSource eventSource) - { - uint status; - m_etwCallback = new Interop.Advapi32.EtwEnableCallback(EtwEnableCallBack); - - status = EventRegister(eventSource, m_etwCallback); - if (status != 0) - { -#if PLATFORM_WINDOWS && !ES_BUILD_STANDALONE - throw new ArgumentException(Interop.Kernel32.GetMessage(unchecked((int)status))); -#else - throw new ArgumentException(Convert.ToString(unchecked((int)status))); -#endif - } - } - - // - // implement Dispose Pattern to early deregister from ETW insted of waiting for - // the finalizer to call deregistration. - // Once the user is done with the provider it needs to call Close() or Dispose() - // If neither are called the finalizer will unregister the provider anyway - // - public void Dispose() - { - Dispose(true); - GC.SuppressFinalize(this); - } - - // <SecurityKernel Critical="True" TreatAsSafe="Does not expose critical resource" Ring="1"> - // <ReferencesCritical Name="Method: Deregister():Void" Ring="1" /> - // </SecurityKernel> - protected virtual void Dispose(bool disposing) - { - // - // explicit cleanup is done by calling Dispose with true from - // Dispose() or Close(). The disposing arguement is ignored because there - // are no unmanaged resources. - // The finalizer calls Dispose with false. - // - - // - // check if the object has been already disposed - // - if (m_disposed) - return; - - // Disable the provider. - m_enabled = false; - - // Do most of the work under a lock to avoid shutdown race. - - long registrationHandle = 0; - lock (EventListener.EventListenersLock) - { - // Double check - if (m_disposed) - return; - - registrationHandle = m_regHandle; - m_regHandle = 0; - m_disposed = true; - } - - // We do the Unregistration outside the EventListenerLock because there is a lock - // inside the ETW routines. This lock is taken before ETW issues commands - // Thus the ETW lock gets taken first and then our EventListenersLock gets taken - // in SendCommand(), and also here. If we called EventUnregister after taking - // the EventListenersLock then the take-lock order is reversed and we can have - // deadlocks in race conditions (dispose racing with an ETW command). - // - // We solve by Unregistering after releasing the EventListenerLock. - if (registrationHandle != 0) - EventUnregister(registrationHandle); - } - - /// <summary> - /// This method deregisters the controlGuid of this class with ETW. - /// - /// </summary> - public virtual void Close() - { - Dispose(); - } - - ~EventProvider() - { - Dispose(false); - } - - // <SecurityKernel Critical="True" Ring="0"> - // <UsesUnsafeCode Name="Parameter filterData of type: Void*" /> - // <UsesUnsafeCode Name="Parameter callbackContext of type: Void*" /> - // </SecurityKernel> - private unsafe void EtwEnableCallBack( - in System.Guid sourceId, - int controlCode, - byte setLevel, - long anyKeyword, - long allKeyword, - Interop.Advapi32.EVENT_FILTER_DESCRIPTOR* filterData, - void* callbackContext - ) - { - // This is an optional callback API. We will therefore ignore any failures that happen as a - // result of turning on this provider as to not crash the app. - // EventSource has code to validate whether initialization it expected to occur actually occurred - try - { - ControllerCommand command = ControllerCommand.Update; - IDictionary<string, string?>? args = null; - bool skipFinalOnControllerCommand = false; - if (controlCode == Interop.Advapi32.EVENT_CONTROL_CODE_ENABLE_PROVIDER) - { - m_enabled = true; - m_level = setLevel; - m_anyKeywordMask = anyKeyword; - m_allKeywordMask = allKeyword; - - List<Tuple<SessionInfo, bool>> sessionsChanged = GetSessions(); - - // The GetSessions() logic was here to support the idea that different ETW sessions - // could have different user-defined filters. (I believe it is currently broken but that is another matter.) - // However in particular GetSessions() does not support EventPipe, only ETW, which is - // the immediate problem. We work-around establishing the invariant that we always get a - // OnControllerCallback under all circumstances, even if we can't find a delta in the - // ETW logic. This fixes things for the EventPipe case. - // - // All this session based logic should be reviewed and likely removed, but that is a larger - // change that needs more careful staging. - if (sessionsChanged.Count == 0) - sessionsChanged.Add(new Tuple<SessionInfo, bool>(new SessionInfo(0, 0), true)); - - foreach (Tuple<SessionInfo, bool> session in sessionsChanged) - { - int sessionChanged = session.Item1.sessionIdBit; - int etwSessionId = session.Item1.etwSessionId; - bool bEnabling = session.Item2; - - skipFinalOnControllerCommand = true; - args = null; // reinitialize args for every session... - - // if we get more than one session changed we have no way - // of knowing which one "filterData" belongs to - if (sessionsChanged.Count > 1) - filterData = null; - - // read filter data only when a session is being *added* - byte[]? data; - int keyIndex; - if (bEnabling && - GetDataFromController(etwSessionId, filterData, out command, out data, out keyIndex)) - { - args = new Dictionary<string, string?>(4); - // data can be null if the filterArgs had a very large size which failed our sanity check - if (data != null) - { - while (keyIndex < data.Length) - { - int keyEnd = FindNull(data, keyIndex); - int valueIdx = keyEnd + 1; - int valueEnd = FindNull(data, valueIdx); - if (valueEnd < data.Length) - { - string key = System.Text.Encoding.UTF8.GetString(data, keyIndex, keyEnd - keyIndex); - string value = System.Text.Encoding.UTF8.GetString(data, valueIdx, valueEnd - valueIdx); - args[key] = value; - } - keyIndex = valueEnd + 1; - } - } - } - - // execute OnControllerCommand once for every session that has changed. - OnControllerCommand(command, args, bEnabling ? sessionChanged : -sessionChanged, etwSessionId); - } - } - else if (controlCode == Interop.Advapi32.EVENT_CONTROL_CODE_DISABLE_PROVIDER) - { - m_enabled = false; - m_level = 0; - m_anyKeywordMask = 0; - m_allKeywordMask = 0; - m_liveSessions = null; - } - else if (controlCode == Interop.Advapi32.EVENT_CONTROL_CODE_CAPTURE_STATE) - { - command = ControllerCommand.SendManifest; - } - else - return; // per spec you ignore commands you don't recognize. - - if (!skipFinalOnControllerCommand) - OnControllerCommand(command, args, 0, 0); - } - catch - { - // We want to ignore any failures that happen as a result of turning on this provider as to - // not crash the app. - } - } - - protected virtual void OnControllerCommand(ControllerCommand command, IDictionary<string, string?>? arguments, int sessionId, int etwSessionId) { } - - protected EventLevel Level - { - get => (EventLevel)m_level; - set => m_level = (byte)value; - } - - protected EventKeywords MatchAnyKeyword - { - get => (EventKeywords)m_anyKeywordMask; - set => m_anyKeywordMask = unchecked((long)value); - } - - protected EventKeywords MatchAllKeyword - { - get => (EventKeywords)m_allKeywordMask; - set => m_allKeywordMask = unchecked((long)value); - } - - private static int FindNull(byte[] buffer, int idx) - { - while (idx < buffer.Length && buffer[idx] != 0) - idx++; - return idx; - } - - /// <summary> - /// Determines the ETW sessions that have been added and/or removed to the set of - /// sessions interested in the current provider. It does so by (1) enumerating over all - /// ETW sessions that enabled 'this.m_Guid' for the current process ID, and (2) - /// comparing the current list with a list it cached on the previous invocation. - /// - /// The return value is a list of tuples, where the SessionInfo specifies the - /// ETW session that was added or remove, and the bool specifies whether the - /// session was added or whether it was removed from the set. - /// </summary> - private List<Tuple<SessionInfo, bool>> GetSessions() - { - List<SessionInfo>? liveSessionList = null; - - GetSessionInfo( - (int etwSessionId, long matchAllKeywords, ref List<SessionInfo>? sessionList) => - GetSessionInfoCallback(etwSessionId, matchAllKeywords, ref sessionList), - ref liveSessionList); - - List<Tuple<SessionInfo, bool>> changedSessionList = new List<Tuple<SessionInfo, bool>>(); - - // first look for sessions that have gone away (or have changed) - // (present in the m_liveSessions but not in the new liveSessionList) - if (m_liveSessions != null) - { - foreach (SessionInfo s in m_liveSessions) - { - int idx; - if ((idx = IndexOfSessionInList(liveSessionList, s.etwSessionId)) < 0 || - (liveSessionList![idx].sessionIdBit != s.sessionIdBit)) - changedSessionList.Add(Tuple.Create(s, false)); - } - } - // next look for sessions that were created since the last callback (or have changed) - // (present in the new liveSessionList but not in m_liveSessions) - if (liveSessionList != null) - { - foreach (SessionInfo s in liveSessionList) - { - int idx; - if ((idx = IndexOfSessionInList(m_liveSessions, s.etwSessionId)) < 0 || - (m_liveSessions![idx].sessionIdBit != s.sessionIdBit)) - changedSessionList.Add(Tuple.Create(s, true)); - } - } - - m_liveSessions = liveSessionList; - return changedSessionList; - } - - /// <summary> - /// This method is the callback used by GetSessions() when it calls into GetSessionInfo(). - /// It updates a List{SessionInfo} based on the etwSessionId and matchAllKeywords that - /// GetSessionInfo() passes in. - /// </summary> - private static void GetSessionInfoCallback(int etwSessionId, long matchAllKeywords, - ref List<SessionInfo>? sessionList) - { - uint sessionIdBitMask = (uint)SessionMask.FromEventKeywords(unchecked((ulong)matchAllKeywords)); - // an ETW controller that specifies more than the mandated bit for our EventSource - // will be ignored... - int val = BitOperations.PopCount(sessionIdBitMask); - if (val > 1) - return; - - sessionList ??= new List<SessionInfo>(8); - - if (val == 1) - { - // activity-tracing-aware etw session - val = BitOperations.TrailingZeroCount(sessionIdBitMask); - } - else - { - // legacy etw session - val = BitOperations.PopCount((uint)SessionMask.All); - } - - sessionList.Add(new SessionInfo(val + 1, etwSessionId)); - } - - private delegate void SessionInfoCallback(int etwSessionId, long matchAllKeywords, ref List<SessionInfo>? sessionList); - - /// <summary> - /// This method enumerates over all active ETW sessions that have enabled 'this.m_Guid' - /// for the current process ID, calling 'action' for each session, and passing it the - /// ETW session and the 'AllKeywords' the session enabled for the current provider. - /// </summary> - private unsafe void GetSessionInfo(SessionInfoCallback action, ref List<SessionInfo>? sessionList) - { - // We wish the EventSource package to be legal for Windows Store applications. - // Currently EnumerateTraceGuidsEx is not an allowed API, so we avoid its use here - // and use the information in the registry instead. This means that ETW controllers - // that do not publish their intent to the registry (basically all controllers EXCEPT - // TraceEventSesion) will not work properly - - // However the framework version of EventSource DOES have ES_SESSION_INFO defined and thus - // does not have this issue. -#if (PLATFORM_WINDOWS && (ES_SESSION_INFO || !ES_BUILD_STANDALONE)) - int buffSize = 256; // An initial guess that probably works most of the time. - byte* buffer; - while (true) - { - byte* space = stackalloc byte[buffSize]; - buffer = space; - int hr = 0; - - fixed (Guid* provider = &m_providerId) - { - hr = Interop.Advapi32.EnumerateTraceGuidsEx(Interop.Advapi32.TRACE_QUERY_INFO_CLASS.TraceGuidQueryInfo, - provider, sizeof(Guid), buffer, buffSize, out buffSize); - } - if (hr == 0) - break; - if (hr != Interop.Errors.ERROR_INSUFFICIENT_BUFFER) - return; - } - - var providerInfos = (Interop.Advapi32.TRACE_GUID_INFO*)buffer; - var providerInstance = (Interop.Advapi32.TRACE_PROVIDER_INSTANCE_INFO*)&providerInfos[1]; - int processId = unchecked((int)Interop.Kernel32.GetCurrentProcessId()); - // iterate over the instances of the EventProvider in all processes - for (int i = 0; i < providerInfos->InstanceCount; i++) - { - if (providerInstance->Pid == processId) - { - var enabledInfos = (Interop.Advapi32.TRACE_ENABLE_INFO*)&providerInstance[1]; - // iterate over the list of active ETW sessions "listening" to the current provider - for (int j = 0; j < providerInstance->EnableCount; j++) - action(enabledInfos[j].LoggerId, enabledInfos[j].MatchAllKeyword, ref sessionList); - } - if (providerInstance->NextOffset == 0) - break; - Debug.Assert(0 <= providerInstance->NextOffset && providerInstance->NextOffset < buffSize); - byte* structBase = (byte*)providerInstance; - providerInstance = (Interop.Advapi32.TRACE_PROVIDER_INSTANCE_INFO*)&structBase[providerInstance->NextOffset]; - } -#else -#if !ES_BUILD_PCL && PLATFORM_WINDOWS // TODO command arguments don't work on PCL builds... - // This code is only used in the Nuget Package Version of EventSource. because - // the code above is using APIs baned from UWP apps. - // - // TODO: In addition to only working when TraceEventSession enables the provider, this code - // also has a problem because TraceEvent does not clean up if the registry is stale - // It is unclear if it is worth keeping, but for now we leave it as it does work - // at least some of the time. - - // Determine our session from what is in the registry. - string regKey = @"\Microsoft\Windows\CurrentVersion\Winevt\Publishers\{" + m_providerName + "}"; - if (IntPtr.Size == 8) - regKey = @"Software" + @"\Wow6432Node" + regKey; - else - regKey = @"Software" + regKey; - - using (var key = Registry.LocalMachine.OpenSubKey(regKey)) - { - if (key != null) - { - foreach (string valueName in key.GetValueNames()) - { - if (valueName.StartsWith("ControllerData_Session_", StringComparison.Ordinal)) - { - string strId = valueName.Substring(23); // strip of the ControllerData_Session_ - int etwSessionId; - if (int.TryParse(strId, out etwSessionId)) - { -#if ES_BUILD_STANDALONE - // we need to assert this permission for partial trust scenarios - (new RegistryPermission(RegistryPermissionAccess.Read, regKey)).Assert(); -#endif - var data = key.GetValue(valueName) as byte[]; - if (data != null) - { - var dataAsString = System.Text.Encoding.UTF8.GetString(data); - int keywordIdx = dataAsString.IndexOf("EtwSessionKeyword", StringComparison.Ordinal); - if (0 <= keywordIdx) - { - int startIdx = keywordIdx + 18; - int endIdx = dataAsString.IndexOf('\0', startIdx); - string keywordBitString = dataAsString.Substring(startIdx, endIdx - startIdx); - int keywordBit; - if (0 < endIdx && int.TryParse(keywordBitString, out keywordBit)) - action(etwSessionId, 1L << keywordBit, ref sessionList); - } - } - } - } - } - } - } -#endif -#endif - } - - /// <summary> - /// Returns the index of the SesisonInfo from 'sessions' that has the specified 'etwSessionId' - /// or -1 if the value is not present. - /// </summary> - private static int IndexOfSessionInList(List<SessionInfo>? sessions, int etwSessionId) - { - if (sessions == null) - return -1; - // for non-coreclr code we could use List<T>.FindIndex(Predicate<T>), but we need this to compile - // on coreclr as well - for (int i = 0; i < sessions.Count; ++i) - if (sessions[i].etwSessionId == etwSessionId) - return i; - - return -1; - } - - /// <summary> - /// Gets any data to be passed from the controller to the provider. It starts with what is passed - /// into the callback, but unfortunately this data is only present for when the provider is active - /// at the time the controller issues the command. To allow for providers to activate after the - /// controller issued a command, we also check the registry and use that to get the data. The function - /// returns an array of bytes representing the data, the index into that byte array where the data - /// starts, and the command being issued associated with that data. - /// </summary> - private unsafe bool GetDataFromController(int etwSessionId, - Interop.Advapi32.EVENT_FILTER_DESCRIPTOR* filterData, out ControllerCommand command, out byte[]? data, out int dataStart) - { - data = null; - dataStart = 0; - if (filterData == null) - { -#if (!ES_BUILD_PCL && !ES_BUILD_PN && PLATFORM_WINDOWS) - string regKey = @"\Microsoft\Windows\CurrentVersion\Winevt\Publishers\{" + m_providerId + "}"; - if (IntPtr.Size == 8) - regKey = @"Software\Wow6432Node" + regKey; - else - regKey = "Software" + regKey; - - string valueName = "ControllerData_Session_" + etwSessionId.ToString(CultureInfo.InvariantCulture); - - // we need to assert this permission for partial trust scenarios -#if ES_BUILD_STANDALONE - (new RegistryPermission(RegistryPermissionAccess.Read, regKey)).Assert(); -#endif - using (RegistryKey? key = Registry.LocalMachine.OpenSubKey(regKey)) - { - data = key?.GetValue(valueName, null) as byte[]; - if (data != null) - { - // We only used the persisted data from the registry for updates. - command = ControllerCommand.Update; - return true; - } - } -#endif - } - else - { - // ETW limited filter data to 1024 bytes but EventPipe doesn't. DiagnosticSourceEventSource - // can legitimately use large filter data buffers to encode a large set of events and properties - // that should be gathered so I am bumping the limit from 1K -> 100K. - if (filterData->Ptr != 0 && 0 < filterData->Size && filterData->Size <= 100*1024) - { - data = new byte[filterData->Size]; - Marshal.Copy((IntPtr)filterData->Ptr, data, 0, data.Length); - } - command = (ControllerCommand)filterData->Type; - return true; - } - - command = ControllerCommand.Update; - return false; - } - - /// <summary> - /// IsEnabled, method used to test if provider is enabled - /// </summary> - public bool IsEnabled() - { - return m_enabled; - } - - /// <summary> - /// IsEnabled, method used to test if event is enabled - /// </summary> - /// <param name="level"> - /// Level to test - /// </param> - /// <param name="keywords"> - /// Keyword to test - /// </param> - public bool IsEnabled(byte level, long keywords) - { - // - // If not enabled at all, return false. - // - if (!m_enabled) - { - return false; - } - - // This also covers the case of Level == 0. - if ((level <= m_level) || - (m_level == 0)) - { - // - // Check if Keyword is enabled - // - - if ((keywords == 0) || - (((keywords & m_anyKeywordMask) != 0) && - ((keywords & m_allKeywordMask) == m_allKeywordMask))) - { - return true; - } - } - - return false; - } - - public static WriteEventErrorCode GetLastWriteEventError() - { - return s_returnCode; - } - - // - // Helper function to set the last error on the thread - // - private static void SetLastError(WriteEventErrorCode error) - { - s_returnCode = error; - } - - // <SecurityKernel Critical="True" Ring="0"> - // <UsesUnsafeCode Name="Local intptrPtr of type: IntPtr*" /> - // <UsesUnsafeCode Name="Local intptrPtr of type: Int32*" /> - // <UsesUnsafeCode Name="Local longptr of type: Int64*" /> - // <UsesUnsafeCode Name="Local uintptr of type: UInt32*" /> - // <UsesUnsafeCode Name="Local ulongptr of type: UInt64*" /> - // <UsesUnsafeCode Name="Local charptr of type: Char*" /> - // <UsesUnsafeCode Name="Local byteptr of type: Byte*" /> - // <UsesUnsafeCode Name="Local shortptr of type: Int16*" /> - // <UsesUnsafeCode Name="Local sbyteptr of type: SByte*" /> - // <UsesUnsafeCode Name="Local ushortptr of type: UInt16*" /> - // <UsesUnsafeCode Name="Local floatptr of type: Single*" /> - // <UsesUnsafeCode Name="Local doubleptr of type: Double*" /> - // <UsesUnsafeCode Name="Local boolptr of type: Boolean*" /> - // <UsesUnsafeCode Name="Local guidptr of type: Guid*" /> - // <UsesUnsafeCode Name="Local decimalptr of type: Decimal*" /> - // <UsesUnsafeCode Name="Local booleanptr of type: Boolean*" /> - // <UsesUnsafeCode Name="Parameter dataDescriptor of type: EventData*" /> - // <UsesUnsafeCode Name="Parameter dataBuffer of type: Byte*" /> - // </SecurityKernel> - private static unsafe object? EncodeObject(ref object? data, ref EventData* dataDescriptor, ref byte* dataBuffer, ref uint totalEventSize) - /*++ - - Routine Description: - - This routine is used by WriteEvent to unbox the object type and - to fill the passed in ETW data descriptor. - - Arguments: - - data - argument to be decoded - - dataDescriptor - pointer to the descriptor to be filled (updated to point to the next empty entry) - - dataBuffer - storage buffer for storing user data, needed because cant get the address of the object - (updated to point to the next empty entry) - - Return Value: - - null if the object is a basic type other than string or byte[]. String otherwise - - --*/ - { - Again: - dataDescriptor->Reserved = 0; - - string? sRet = data as string; - byte[]? blobRet = null; - - if (sRet != null) - { - dataDescriptor->Size = ((uint)sRet.Length + 1) * 2; - } - else if ((blobRet = data as byte[]) != null) - { - // first store array length - *(int*)dataBuffer = blobRet.Length; - dataDescriptor->Ptr = (ulong)dataBuffer; - dataDescriptor->Size = 4; - totalEventSize += dataDescriptor->Size; - - // then the array parameters - dataDescriptor++; - dataBuffer += s_basicTypeAllocationBufferSize; - dataDescriptor->Size = (uint)blobRet.Length; - } - else if (data is IntPtr) - { - dataDescriptor->Size = (uint)sizeof(IntPtr); - IntPtr* intptrPtr = (IntPtr*)dataBuffer; - *intptrPtr = (IntPtr)data; - dataDescriptor->Ptr = (ulong)intptrPtr; - } - else if (data is int) - { - dataDescriptor->Size = (uint)sizeof(int); - int* intptr = (int*)dataBuffer; - *intptr = (int)data; - dataDescriptor->Ptr = (ulong)intptr; - } - else if (data is long) - { - dataDescriptor->Size = (uint)sizeof(long); - long* longptr = (long*)dataBuffer; - *longptr = (long)data; - dataDescriptor->Ptr = (ulong)longptr; - } - else if (data is uint) - { - dataDescriptor->Size = (uint)sizeof(uint); - uint* uintptr = (uint*)dataBuffer; - *uintptr = (uint)data; - dataDescriptor->Ptr = (ulong)uintptr; - } - else if (data is ulong) - { - dataDescriptor->Size = (uint)sizeof(ulong); - ulong* ulongptr = (ulong*)dataBuffer; - *ulongptr = (ulong)data; - dataDescriptor->Ptr = (ulong)ulongptr; - } - else if (data is char) - { - dataDescriptor->Size = (uint)sizeof(char); - char* charptr = (char*)dataBuffer; - *charptr = (char)data; - dataDescriptor->Ptr = (ulong)charptr; - } - else if (data is byte) - { - dataDescriptor->Size = (uint)sizeof(byte); - byte* byteptr = (byte*)dataBuffer; - *byteptr = (byte)data; - dataDescriptor->Ptr = (ulong)byteptr; - } - else if (data is short) - { - dataDescriptor->Size = (uint)sizeof(short); - short* shortptr = (short*)dataBuffer; - *shortptr = (short)data; - dataDescriptor->Ptr = (ulong)shortptr; - } - else if (data is sbyte) - { - dataDescriptor->Size = (uint)sizeof(sbyte); - sbyte* sbyteptr = (sbyte*)dataBuffer; - *sbyteptr = (sbyte)data; - dataDescriptor->Ptr = (ulong)sbyteptr; - } - else if (data is ushort) - { - dataDescriptor->Size = (uint)sizeof(ushort); - ushort* ushortptr = (ushort*)dataBuffer; - *ushortptr = (ushort)data; - dataDescriptor->Ptr = (ulong)ushortptr; - } - else if (data is float) - { - dataDescriptor->Size = (uint)sizeof(float); - float* floatptr = (float*)dataBuffer; - *floatptr = (float)data; - dataDescriptor->Ptr = (ulong)floatptr; - } - else if (data is double) - { - dataDescriptor->Size = (uint)sizeof(double); - double* doubleptr = (double*)dataBuffer; - *doubleptr = (double)data; - dataDescriptor->Ptr = (ulong)doubleptr; - } - else if (data is bool) - { - // WIN32 Bool is 4 bytes - dataDescriptor->Size = 4; - int* intptr = (int*)dataBuffer; - if ((bool)data) - { - *intptr = 1; - } - else - { - *intptr = 0; - } - dataDescriptor->Ptr = (ulong)intptr; - } - else if (data is Guid) - { - dataDescriptor->Size = (uint)sizeof(Guid); - Guid* guidptr = (Guid*)dataBuffer; - *guidptr = (Guid)data; - dataDescriptor->Ptr = (ulong)guidptr; - } - else if (data is decimal) - { - dataDescriptor->Size = (uint)sizeof(decimal); - decimal* decimalptr = (decimal*)dataBuffer; - *decimalptr = (decimal)data; - dataDescriptor->Ptr = (ulong)decimalptr; - } - else if (data is DateTime) - { - const long UTCMinTicks = 504911232000000000; - long dateTimeTicks = 0; - // We cannot translate dates sooner than 1/1/1601 in UTC. - // To avoid getting an ArgumentOutOfRangeException we compare with 1/1/1601 DateTime ticks - if (((DateTime)data).Ticks > UTCMinTicks) - dateTimeTicks = ((DateTime)data).ToFileTimeUtc(); - dataDescriptor->Size = (uint)sizeof(long); - long* longptr = (long*)dataBuffer; - *longptr = dateTimeTicks; - dataDescriptor->Ptr = (ulong)longptr; - } - else - { - if (data is System.Enum) - { - try - { - Type underlyingType = Enum.GetUnderlyingType(data.GetType()); - if (underlyingType == typeof(ulong)) - data = (ulong)data; - else if (underlyingType == typeof(long)) - data = (long)data; - else - data = (int)Convert.ToInt64(data); // This handles all int/uint or below (we treat them like 32 bit ints) - goto Again; - } - catch { } // On wierd cases (e.g. enums of type double), give up and for compat simply tostring. - } - - // To our eyes, everything else is a just a string - if (data == null) - sRet = ""; - else - sRet = data.ToString()!; - dataDescriptor->Size = ((uint)sRet.Length + 1) * 2; - } - - totalEventSize += dataDescriptor->Size; - - // advance buffers - dataDescriptor++; - dataBuffer += s_basicTypeAllocationBufferSize; - - return (object?)sRet ?? (object?)blobRet; - } - - /// <summary> - /// WriteEvent, method to write a parameters with event schema properties - /// </summary> - /// <param name="eventDescriptor"> - /// Event Descriptor for this event. - /// </param> - /// <param name="activityID"> - /// A pointer to the activity ID GUID to log - /// </param> - /// <param name="childActivityID"> - /// childActivityID is marked as 'related' to the current activity ID. - /// </param> - /// <param name="eventPayload"> - /// Payload for the ETW event. - /// </param> - // <SecurityKernel Critical="True" Ring="0"> - // <CallsSuppressUnmanagedCode Name="Interop.Advapi32.EventWrite(System.Int64,EventDescriptor&,System.UInt32,System.Void*):System.UInt32" /> - // <UsesUnsafeCode Name="Local dataBuffer of type: Byte*" /> - // <UsesUnsafeCode Name="Local pdata of type: Char*" /> - // <UsesUnsafeCode Name="Local userData of type: EventData*" /> - // <UsesUnsafeCode Name="Local userDataPtr of type: EventData*" /> - // <UsesUnsafeCode Name="Local currentBuffer of type: Byte*" /> - // <UsesUnsafeCode Name="Local v0 of type: Char*" /> - // <UsesUnsafeCode Name="Local v1 of type: Char*" /> - // <UsesUnsafeCode Name="Local v2 of type: Char*" /> - // <UsesUnsafeCode Name="Local v3 of type: Char*" /> - // <UsesUnsafeCode Name="Local v4 of type: Char*" /> - // <UsesUnsafeCode Name="Local v5 of type: Char*" /> - // <UsesUnsafeCode Name="Local v6 of type: Char*" /> - // <UsesUnsafeCode Name="Local v7 of type: Char*" /> - // <ReferencesCritical Name="Method: EncodeObject(Object&, EventData*, Byte*):String" Ring="1" /> - // </SecurityKernel> - internal unsafe bool WriteEvent(ref EventDescriptor eventDescriptor, IntPtr eventHandle, Guid* activityID, Guid* childActivityID, params object?[] eventPayload) - { - WriteEventErrorCode status = WriteEventErrorCode.NoError; - - if (IsEnabled(eventDescriptor.Level, eventDescriptor.Keywords)) - { - int argCount = eventPayload.Length; - - if (argCount > s_etwMaxNumberArguments) - { - s_returnCode = WriteEventErrorCode.TooManyArgs; - return false; - } - - uint totalEventSize = 0; - int index; - int refObjIndex = 0; - List<int> refObjPosition = new List<int>(s_etwAPIMaxRefObjCount); - List<object?> dataRefObj = new List<object?>(s_etwAPIMaxRefObjCount); - EventData* userData = stackalloc EventData[2 * argCount]; - for (int i = 0; i < 2 * argCount; i++) - userData[i] = default; - EventData* userDataPtr = (EventData*)userData; - byte* dataBuffer = stackalloc byte[s_basicTypeAllocationBufferSize * 2 * argCount]; // Assume 16 chars for non-string argument - byte* currentBuffer = dataBuffer; - - // - // The loop below goes through all the arguments and fills in the data - // descriptors. For strings save the location in the dataString array. - // Calculates the total size of the event by adding the data descriptor - // size value set in EncodeObject method. - // - bool hasNonStringRefArgs = false; - for (index = 0; index < eventPayload.Length; index++) - { - if (eventPayload[index] != null) - { - object? supportedRefObj = EncodeObject(ref eventPayload[index], ref userDataPtr, ref currentBuffer, ref totalEventSize); - - if (supportedRefObj != null) - { - // EncodeObject advanced userDataPtr to the next empty slot - int idx = (int)(userDataPtr - userData - 1); - if (!(supportedRefObj is string)) - { - if (eventPayload.Length + idx + 1 - index > s_etwMaxNumberArguments) - { - s_returnCode = WriteEventErrorCode.TooManyArgs; - return false; - } - hasNonStringRefArgs = true; - } - dataRefObj.Add(supportedRefObj); - refObjPosition.Add(idx); - refObjIndex++; - } - } - else - { - s_returnCode = WriteEventErrorCode.NullInput; - return false; - } - } - - // update argCount based on actual number of arguments written to 'userData' - argCount = (int)(userDataPtr - userData); - - if (totalEventSize > s_traceEventMaximumSize) - { - s_returnCode = WriteEventErrorCode.EventTooBig; - return false; - } - - // the optimized path (using "fixed" instead of allocating pinned GCHandles - if (!hasNonStringRefArgs && (refObjIndex < s_etwAPIMaxRefObjCount)) - { - // Fast path: at most 8 string arguments - - // ensure we have at least s_etwAPIMaxStringCount in dataString, so that - // the "fixed" statement below works - while (refObjIndex < s_etwAPIMaxRefObjCount) - { - dataRefObj.Add(null); - ++refObjIndex; - } - - // - // now fix any string arguments and set the pointer on the data descriptor - // - fixed (char* v0 = (string?)dataRefObj[0], v1 = (string?)dataRefObj[1], v2 = (string?)dataRefObj[2], v3 = (string?)dataRefObj[3], - v4 = (string?)dataRefObj[4], v5 = (string?)dataRefObj[5], v6 = (string?)dataRefObj[6], v7 = (string?)dataRefObj[7]) - { - userDataPtr = (EventData*)userData; - if (dataRefObj[0] != null) - { - userDataPtr[refObjPosition[0]].Ptr = (ulong)v0; - } - if (dataRefObj[1] != null) - { - userDataPtr[refObjPosition[1]].Ptr = (ulong)v1; - } - if (dataRefObj[2] != null) - { - userDataPtr[refObjPosition[2]].Ptr = (ulong)v2; - } - if (dataRefObj[3] != null) - { - userDataPtr[refObjPosition[3]].Ptr = (ulong)v3; - } - if (dataRefObj[4] != null) - { - userDataPtr[refObjPosition[4]].Ptr = (ulong)v4; - } - if (dataRefObj[5] != null) - { - userDataPtr[refObjPosition[5]].Ptr = (ulong)v5; - } - if (dataRefObj[6] != null) - { - userDataPtr[refObjPosition[6]].Ptr = (ulong)v6; - } - if (dataRefObj[7] != null) - { - userDataPtr[refObjPosition[7]].Ptr = (ulong)v7; - } - - status = m_eventProvider.EventWriteTransfer(m_regHandle, in eventDescriptor, eventHandle, activityID, childActivityID, argCount, userData); - } - } - else - { - // Slow path: use pinned handles - userDataPtr = (EventData*)userData; - - GCHandle[] rgGCHandle = new GCHandle[refObjIndex]; - for (int i = 0; i < refObjIndex; ++i) - { - // below we still use "fixed" to avoid taking dependency on the offset of the first field - // in the object (the way we would need to if we used GCHandle.AddrOfPinnedObject) - rgGCHandle[i] = GCHandle.Alloc(dataRefObj[i], GCHandleType.Pinned); - if (dataRefObj[i] is string) - { - fixed (char* p = (string?)dataRefObj[i]) - userDataPtr[refObjPosition[i]].Ptr = (ulong)p; - } - else - { - fixed (byte* p = (byte[]?)dataRefObj[i]) - userDataPtr[refObjPosition[i]].Ptr = (ulong)p; - } - } - - status = m_eventProvider.EventWriteTransfer(m_regHandle, in eventDescriptor, eventHandle, activityID, childActivityID, argCount, userData); - - for (int i = 0; i < refObjIndex; ++i) - { - rgGCHandle[i].Free(); - } - } - } - - if (status != WriteEventErrorCode.NoError) - { - SetLastError(status); - return false; - } - - return true; - } - - /// <summary> - /// WriteEvent, method to be used by generated code on a derived class - /// </summary> - /// <param name="eventDescriptor"> - /// Event Descriptor for this event. - /// </param> - /// <param name="activityID"> - /// A pointer to the activity ID to log - /// </param> - /// <param name="childActivityID"> - /// If this event is generating a child activity (WriteEventTransfer related activity) this is child activity - /// This can be null for events that do not generate a child activity. - /// </param> - /// <param name="dataCount"> - /// number of event descriptors - /// </param> - /// <param name="data"> - /// pointer do the event data - /// </param> - // <SecurityKernel Critical="True" Ring="0"> - // <CallsSuppressUnmanagedCode Name="Interop.Advapi32.EventWrite(System.Int64,EventDescriptor&,System.UInt32,System.Void*):System.UInt32" /> - // </SecurityKernel> - protected internal unsafe bool WriteEvent(ref EventDescriptor eventDescriptor, IntPtr eventHandle, Guid* activityID, Guid* childActivityID, int dataCount, IntPtr data) - { - if (childActivityID != null) - { - // activity transfers are supported only for events that specify the Send or Receive opcode - Debug.Assert((EventOpcode)eventDescriptor.Opcode == EventOpcode.Send || - (EventOpcode)eventDescriptor.Opcode == EventOpcode.Receive || - (EventOpcode)eventDescriptor.Opcode == EventOpcode.Start || - (EventOpcode)eventDescriptor.Opcode == EventOpcode.Stop); - } - - WriteEventErrorCode status = m_eventProvider.EventWriteTransfer(m_regHandle, in eventDescriptor, eventHandle, activityID, childActivityID, dataCount, (EventData*)data); - - if (status != 0) - { - SetLastError(status); - return false; - } - return true; - } - - internal unsafe bool WriteEventRaw( - ref EventDescriptor eventDescriptor, - IntPtr eventHandle, - Guid* activityID, - Guid* relatedActivityID, - int dataCount, - IntPtr data) - { - WriteEventErrorCode status = m_eventProvider.EventWriteTransfer( - m_regHandle, - in eventDescriptor, - eventHandle, - activityID, - relatedActivityID, - dataCount, - (EventData*)data); - - if (status != WriteEventErrorCode.NoError) - { - SetLastError(status); - return false; - } - - return true; - } - - // These are look-alikes to the Manifest based ETW OS APIs that have been shimmed to work - // either with Manifest ETW or Classic ETW (if Manifest based ETW is not available). - private unsafe uint EventRegister(EventSource eventSource, Interop.Advapi32.EtwEnableCallback enableCallback) - { - m_providerName = eventSource.Name; - m_providerId = eventSource.Guid; - m_etwCallback = enableCallback; - return m_eventProvider.EventRegister(eventSource, enableCallback, null, ref m_regHandle); - } - - private void EventUnregister(long registrationHandle) => - m_eventProvider.EventUnregister(registrationHandle); - -#if PLATFORM_WINDOWS - private static bool m_setInformationMissing; - - internal unsafe int SetInformation( - Interop.Advapi32.EVENT_INFO_CLASS eventInfoClass, - IntPtr data, - uint dataSize) - { - int status = Interop.Errors.ERROR_NOT_SUPPORTED; - - if (!m_setInformationMissing) - { - try - { - status = Interop.Advapi32.EventSetInformation( - m_regHandle, - eventInfoClass, - (void*)data, - (int)dataSize); - } - catch (TypeLoadException) - { - m_setInformationMissing = true; - } - } - - return status; - } -#endif - } - -#if PLATFORM_WINDOWS - - // A wrapper around the ETW-specific API calls. - internal sealed class EtwEventProvider : IEventProvider - { - // Register an event provider. - unsafe uint IEventProvider.EventRegister( - EventSource eventSource, - Interop.Advapi32.EtwEnableCallback enableCallback, - void* callbackContext, - ref long registrationHandle) - { - Guid providerId = eventSource.Guid; - return Interop.Advapi32.EventRegister( - in providerId, - enableCallback, - callbackContext, - ref registrationHandle); - } - - // Unregister an event provider. - uint IEventProvider.EventUnregister(long registrationHandle) - { - return Interop.Advapi32.EventUnregister(registrationHandle); - } - - // Write an event. - unsafe EventProvider.WriteEventErrorCode IEventProvider.EventWriteTransfer( - long registrationHandle, - in EventDescriptor eventDescriptor, - IntPtr eventHandle, - Guid* activityId, - Guid* relatedActivityId, - int userDataCount, - EventProvider.EventData* userData) - { - int error = Interop.Advapi32.EventWriteTransfer( - registrationHandle, - in eventDescriptor, - activityId, - relatedActivityId, - userDataCount, - userData); - - switch (error) - { - case Interop.Errors.ERROR_ARITHMETIC_OVERFLOW: - case Interop.Errors.ERROR_MORE_DATA: - return EventProvider.WriteEventErrorCode.EventTooBig; - case Interop.Errors.ERROR_NOT_ENOUGH_MEMORY: - return EventProvider.WriteEventErrorCode.NoFreeBuffers; - } - - return EventProvider.WriteEventErrorCode.NoError; - } - - // Get or set the per-thread activity ID. - int IEventProvider.EventActivityIdControl(Interop.Advapi32.ActivityControl ControlCode, ref Guid ActivityId) - { - return Interop.Advapi32.EventActivityIdControl( - ControlCode, - ref ActivityId); - } - - // Define an EventPipeEvent handle. - unsafe IntPtr IEventProvider.DefineEventHandle(uint eventID, string eventName, long keywords, uint eventVersion, uint level, byte* pMetadata, uint metadataLength) - { - throw new System.NotSupportedException(); - } - } - -#endif - internal sealed class NoOpEventProvider : IEventProvider - { - unsafe uint IEventProvider.EventRegister( - EventSource eventSource, - Interop.Advapi32.EtwEnableCallback enableCallback, - void* callbackContext, - ref long registrationHandle) - { - return 0; - } - - uint IEventProvider.EventUnregister(long registrationHandle) - { - return 0; - } - - unsafe EventProvider.WriteEventErrorCode IEventProvider.EventWriteTransfer( - long registrationHandle, - in EventDescriptor eventDescriptor, - IntPtr eventHandle, - Guid* activityId, - Guid* relatedActivityId, - int userDataCount, - EventProvider.EventData* userData) - { - return EventProvider.WriteEventErrorCode.NoError; - } - - int IEventProvider.EventActivityIdControl(Interop.Advapi32.ActivityControl ControlCode, ref Guid ActivityId) - { - return 0; - } - - // Define an EventPipeEvent handle. - unsafe IntPtr IEventProvider.DefineEventHandle(uint eventID, string eventName, long keywords, uint eventVersion, uint level, byte* pMetadata, uint metadataLength) - { - return IntPtr.Zero; - } - } -} diff --git a/netcore/System.Private.CoreLib/shared/System/Diagnostics/Tracing/EventSource.cs b/netcore/System.Private.CoreLib/shared/System/Diagnostics/Tracing/EventSource.cs deleted file mode 100644 index e639f7c610c..00000000000 --- a/netcore/System.Private.CoreLib/shared/System/Diagnostics/Tracing/EventSource.cs +++ /dev/null @@ -1,6204 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -// This program uses code hyperlinks available as part of the HyperAddin Visual Studio plug-in. -// It is available from http://www.codeplex.com/hyperAddin -#if ES_BUILD_STANDALONE -#define FEATURE_MANAGED_ETW_CHANNELS -// #define FEATURE_ADVANCED_MANAGED_ETW_CHANNELS -#endif - -/* DESIGN NOTES DESIGN NOTES DESIGN NOTES DESIGN NOTES */ -// DESIGN NOTES -// Over the years EventSource has become more complex and so it is important to understand -// the basic structure of the code to insure that it does not grow more complex. -// -// Basic Model -// -// PRINCIPLE: EventSource - ETW decoupling -// -// Conceptually an EventSouce is something that takes event logging data from the source methods -// to the EventListener that can subscribe to them. Note that CONCEPTUALLY EVENTSOURCES DON'T -// KNOW ABOUT ETW!. The MODEL of the system is that there is a special EventListener which -// we will call the EtwEventListener, that forwards commands from ETW to EventSources and -// listens to EventSources and forwards on those events to ETW. Thus the model should -// be that you DON'T NEED ETW. -// -// Now in actual practice, EventSouce have rather intimate knowledge of ETW and send events -// to it directly, but this can be VIEWED AS AN OPTIMIZATION. -// -// Basic Event Data Flow: -// -// There are two ways for event Data to enter the system -// 1) WriteEvent* and friends. This is called the 'contract' based approach because -// you write a method per event which forms a contract that is know at compile time. -// In this scheme each event is given an EVENTID (small integer), which is its identity -// 2) Write<T> methods. This is called the 'dynamic' approach because new events -// can be created on the fly. Event identity is determined by the event NAME, and these -// are not quite as efficient at runtime since you have at least a hash table lookup -// on every event write. -// -// EventSource-EventListener transfer fully supports both ways of writing events (either contract -// based (WriteEvent*) or dynamic (Write<T>)). Both ways fully support the same set of data -// types. It is recommended, however, that you use the contract based approach when the event scheme -// is known at compile time (that is whenever possible). It is more efficient, but more importantly -// it makes the contract very explicit, and centralizes all policy about logging. These are good -// things. The Write<T> API is really meant for more ad-hoc cases. -// -// Allowed Data: -// -// Note that EventSource-EventListeners have a conceptual serialization-deserialization that happens -// during the transfer. In particular object identity is not preserved, some objects are morphed, -// and not all data types are supported. In particular you can pass -// -// A Valid type to log to an EventSource include -// * Primitive data types -// * IEnumerable<T> of valid types T (this include arrays) (* New for V4.6) -// * Explicitly Opted in class or struct with public property Getters over Valid types. (* New for V4.6) -// -// This set of types is roughly a generalization of JSON support (basically primitives, bags, and arrays). -// -// Explicitly allowed structs include (* New for V4.6) -// * Marked with the EventData attribute -// * implicitly defined (e.g the C# new {x = 3, y = 5} syntax) -// * KeyValuePair<K,V> (thus dictionaries can be passed since they are an IEnumerable of KeyValuePair) -// -// When classes are returned in an EventListener, what is returned is something that implements -// IDictionary<string, T>. Thus when objects are passed to an EventSource they are transformed -// into a key-value bag (the IDictionary<string, T>) for consumption in the listener. These -// are obviously NOT the original objects. -// -// ETW serialization formats: -// -// As mentioned, conceptually EventSources send data to EventListeners and there is a conceptual -// copy/morph of that data as described above. In addition the .NET framework supports a conceptual -// ETWListener that will send the data to the ETW stream. If you use this feature, the data needs -// to be serialized in a way that ETW supports. ETW supports the following serialization formats -// -// 1) Manifest Based serialization. -// 2) SelfDescribing serialization (TraceLogging style in the TraceLogging directory) -// -// A key factor is that the Write<T> method, which supports on the fly definition of events, can't -// support the manifest based serialization because the manifest needs the schema of all events -// to be known before any events are emitted. This implies the following: -// -// If you use Write<T> and the output goes to ETW it will use the SelfDescribing format. -// If you use the EventSource(string) constructor for an eventSource (in which you don't -// create a subclass), the default is also to use Self-Describing serialization. In addition -// you can use the EventSoruce(EventSourceSettings) constructor to also explicitly specify -// Self-Describing serialization format. These affect the WriteEvent* APIs going to ETW. -// -// Note that none of this ETW serialization logic affects EventListeners. Only the ETW listener. -// -// ************************************************************************************* -// *** INTERNALS: Event Propagation -// -// Data enters the system either though -// -// 1) A user defined method in the user defined subclass of EventSource which calls -// A) A typesafe type specific overload of WriteEvent(ID, ...) e.g. WriteEvent(ID, string, string) -// * which calls into the unsafe WriteEventCore(ID COUNT EventData*) WriteEventWithRelatedActivityIdCore() -// B) The typesafe overload WriteEvent(ID, object[]) which calls the private helper WriteEventVarargs(ID, Guid* object[]) -// C) Directly into the unsafe WriteEventCore(ID, COUNT EventData*) or WriteEventWithRelatedActivityIdCore() -// -// All event data eventually flows to one of -// * WriteEventWithRelatedActivityIdCore(ID, Guid*, COUNT, EventData*) -// * WriteEventVarargs(ID, Guid*, object[]) -// -// 2) A call to one of the overloads of Write<T>. All these overloads end up in -// * WriteImpl<T>(EventName, Options, Data, Guid*, Guid*) -// -// On output there are the following routines -// Writing to all listeners that are NOT ETW, we have the following routines -// * WriteToAllListeners(ID, Guid*, Guid*, COUNT, EventData*) -// * WriteToAllListeners(ID, Guid*, Guid*, object[]) -// * WriteToAllListeners(NAME, Guid*, Guid*, EventPayload) -// -// EventPayload is the internal type that implements the IDictionary<string, object> interface -// The EventListeners will pass back for serialized classes for nested object, but -// WriteToAllListeners(NAME, Guid*, Guid*, EventPayload) unpacks this and uses the fields as if they -// were parameters to a method. -// -// The first two are used for the WriteEvent* case, and the later is used for the Write<T> case. -// -// Writing to ETW, Manifest Based -// EventProvider.WriteEvent(EventDescriptor, Guid*, COUNT, EventData*) -// EventProvider.WriteEvent(EventDescriptor, Guid*, object[]) -// Writing to ETW, Self-Describing format -// WriteMultiMerge(NAME, Options, Types, EventData*) -// WriteMultiMerge(NAME, Options, Types, object[]) -// WriteImpl<T> has logic that knows how to serialize (like WriteMultiMerge) but also knows -// where it will write it to -// -// All ETW writes eventually call -// EventWriteTransfer -// EventProvider.WriteEventRaw - sets last error -// EventSource.WriteEventRaw - Does EventSource exception handling logic -// WriteMultiMerge -// WriteImpl<T> -// EventProvider.WriteEvent(EventDescriptor, Guid*, COUNT, EventData*) -// EventProvider.WriteEvent(EventDescriptor, Guid*, object[]) -// -// Serialization: We have a bit of a hodge-podge of serializers right now. Only the one for ETW knows -// how to deal with nested classes or arrays. I will call this serializer the 'TypeInfo' serializer -// since it is the TraceLoggingTypeInfo structure that knows how to do this. Effectively for a type you -// can call one of these -// WriteMetadata - transforms the type T into serialization meta data blob for that type -// WriteObjectData - transforms an object of T into serialization data blob for that instance -// GetData - transforms an object of T into its deserialized form suitable for passing to EventListener. -// The first two are used to serialize something for ETW. The second one is used to transform the object -// for use by the EventListener. We also have a 'DecodeObject' method that will take a EventData* and -// deserialize to pass to an EventListener, but it only works on primitive types (types supported in version V4.5). -// -// It is an important observation that while EventSource does support users directly calling with EventData* -// blobs, we ONLY support that for the primitive types (V4.5 level support). Thus while there is a EventData* -// path through the system it is only for some types. The object[] path is the more general (but less efficient) path. -// -// TODO There is cleanup needed There should be no divergence until WriteEventRaw. -// -// TODO: We should have a single choke point (right now we always have this parallel EventData* and object[] path. This -// was historical (at one point we tried to pass object directly from EventSoruce to EventListener. That was always -// fragile and a compatibility headache, but we have finally been forced into the idea that there is always a transformation. -// This allows us to use the EventData* form to be the canonical data format in the low level APIs. This also gives us the -// opportunity to expose this format to EventListeners in the future. -// - -#if ES_BUILD_STANDALONE -using System; -using System.Diagnostics; -using System.Security.Permissions; -using EventDescriptor = Microsoft.Diagnostics.Tracing.EventDescriptor; -using BitOperations = Microsoft.Diagnostics.Tracing.Internal.BitOperations; -#else -using System.Threading.Tasks; -#endif -using System.Collections.Generic; -using System.Collections.ObjectModel; -using System.Diagnostics.CodeAnalysis; -using System.Globalization; -using System.Numerics; -using System.Reflection; -using System.Resources; -using System.Text; -using System.Threading; -using Microsoft.Reflection; - -#if ES_BUILD_STANDALONE -namespace Microsoft.Diagnostics.Tracing -#else -namespace System.Diagnostics.Tracing -#endif -{ - /// <summary> - /// This class is meant to be inherited by a user-defined event source in order to define a managed - /// ETW provider. Please See DESIGN NOTES above for the internal architecture. - /// The minimal definition of an EventSource simply specifies a number of ETW event methods that - /// call one of the EventSource.WriteEvent overloads, <see cref="EventSource.WriteEventCore"/>, - /// or <see cref="EventSource.WriteEventWithRelatedActivityIdCore"/> to log them. This functionality - /// is sufficient for many users. - /// <para> - /// To achieve more control over the ETW provider manifest exposed by the event source type, the - /// [<see cref="EventAttribute"/>] attributes can be specified for the ETW event methods. - /// </para><para> - /// For very advanced EventSources, it is possible to intercept the commands being given to the - /// eventSource and change what filtering is done (see EventListener.EnableEvents and - /// <see cref="EventListener.DisableEvents"/>) or cause actions to be performed by the eventSource, - /// e.g. dumping a data structure (see EventSource.SendCommand and - /// <see cref="EventSource.OnEventCommand"/>). - /// </para><para> - /// The eventSources can be turned on with Windows ETW controllers (e.g. logman), immediately. - /// It is also possible to control and intercept the data dispatcher programmatically. See - /// <see cref="EventListener"/> for more. - /// </para> - /// </summary> - /// <remarks> - /// This is a minimal definition for a custom event source: - /// <code> - /// [EventSource(Name="Samples-Demos-Minimal")] - /// sealed class MinimalEventSource : EventSource - /// { - /// public static MinimalEventSource Log = new MinimalEventSource(); - /// public void Load(long ImageBase, string Name) { WriteEvent(1, ImageBase, Name); } - /// public void Unload(long ImageBase) { WriteEvent(2, ImageBase); } - /// private MinimalEventSource() {} - /// } - /// </code> - /// </remarks> - public partial class EventSource : IDisposable - { -#if FEATURE_EVENTSOURCE_XPLAT -#pragma warning disable CA1823 // field is used to keep listener alive - private static readonly EventListener? persistent_Xplat_Listener = XplatEventLogger.InitializePersistentListener(); -#pragma warning restore CA1823 -#endif //FEATURE_EVENTSOURCE_XPLAT - - /// <summary> - /// The human-friendly name of the eventSource. It defaults to the simple name of the class - /// </summary> - public string Name => m_name; - /// <summary> - /// Every eventSource is assigned a GUID to uniquely identify it to the system. - /// </summary> - public Guid Guid => m_guid; - - /// <summary> - /// Returns true if the eventSource has been enabled at all. This is the preferred test - /// to be performed before a relatively expensive EventSource operation. - /// </summary> - public bool IsEnabled() - { - return m_eventSourceEnabled; - } - - /// <summary> - /// Returns true if events with greater than or equal 'level' and have one of 'keywords' set are enabled. - /// - /// Note that the result of this function is only an approximation on whether a particular - /// event is active or not. It is only meant to be used as way of avoiding expensive - /// computation for logging when logging is not on, therefore it sometimes returns false - /// positives (but is always accurate when returning false). EventSources are free to - /// have additional filtering. - /// </summary> - public bool IsEnabled(EventLevel level, EventKeywords keywords) - { - return IsEnabled(level, keywords, EventChannel.None); - } - - /// <summary> - /// Returns true if events with greater than or equal 'level' and have one of 'keywords' set are enabled, or - /// if 'keywords' specifies a channel bit for a channel that is enabled. - /// - /// Note that the result of this function only an approximation on whether a particular - /// event is active or not. It is only meant to be used as way of avoiding expensive - /// computation for logging when logging is not on, therefore it sometimes returns false - /// positives (but is always accurate when returning false). EventSources are free to - /// have additional filtering. - /// </summary> - public bool IsEnabled(EventLevel level, EventKeywords keywords, EventChannel channel) - { - if (!m_eventSourceEnabled) - return false; - - if (!IsEnabledCommon(m_eventSourceEnabled, m_level, m_matchAnyKeyword, level, keywords, channel)) - return false; - - return true; - } - - /// <summary> - /// Returns the settings for the event source instance - /// </summary> - public EventSourceSettings Settings => m_config; - - // Manifest support - /// <summary> - /// Returns the GUID that uniquely identifies the eventSource defined by 'eventSourceType'. - /// This API allows you to compute this without actually creating an instance of the EventSource. - /// It only needs to reflect over the type. - /// </summary> - public static Guid GetGuid(Type eventSourceType) - { - if (eventSourceType == null) - throw new ArgumentNullException(nameof(eventSourceType)); - - EventSourceAttribute? attrib = (EventSourceAttribute?)GetCustomAttributeHelper(eventSourceType, typeof(EventSourceAttribute)); - string name = eventSourceType.Name; - if (attrib != null) - { - if (attrib.Guid != null) - { -#if !ES_BUILD_AGAINST_DOTNET_V35 - if (Guid.TryParse(attrib.Guid, out Guid g)) - return g; -#else - try { return new Guid(attrib.Guid); } - catch (Exception) { } -#endif - } - - if (attrib.Name != null) - name = attrib.Name; - } - - if (name == null) - { - throw new ArgumentException(SR.Argument_InvalidTypeName, nameof(eventSourceType)); - } - return GenerateGuidFromName(name.ToUpperInvariant()); // Make it case insensitive. - } - /// <summary> - /// Returns the official ETW Provider name for the eventSource defined by 'eventSourceType'. - /// This API allows you to compute this without actually creating an instance of the EventSource. - /// It only needs to reflect over the type. - /// </summary> - public static string GetName(Type eventSourceType) - { - return GetName(eventSourceType, EventManifestOptions.None); - } - - /// <summary> - /// Returns a string of the XML manifest associated with the eventSourceType. The scheme for this XML is - /// documented at in EventManifest Schema https://docs.microsoft.com/en-us/windows/desktop/WES/eventmanifestschema-schema. - /// This is the preferred way of generating a manifest to be embedded in the ETW stream as it is fast and - /// the fact that it only includes localized entries for the current UI culture is an acceptable tradeoff. - /// </summary> - /// <param name="eventSourceType">The type of the event source class for which the manifest is generated</param> - /// <param name="assemblyPathToIncludeInManifest">The manifest XML fragment contains the string name of the DLL name in - /// which it is embedded. This parameter specifies what name will be used</param> - /// <returns>The XML data string</returns> - public static string? GenerateManifest(Type eventSourceType, string? assemblyPathToIncludeInManifest) - { - return GenerateManifest(eventSourceType, assemblyPathToIncludeInManifest, EventManifestOptions.None); - } - /// <summary> - /// Returns a string of the XML manifest associated with the eventSourceType. The scheme for this XML is - /// documented at in EventManifest Schema https://docs.microsoft.com/en-us/windows/desktop/WES/eventmanifestschema-schema. - /// Pass EventManifestOptions.AllCultures when generating a manifest to be registered on the machine. This - /// ensures that the entries in the event log will be "optimally" localized. - /// </summary> - /// <param name="eventSourceType">The type of the event source class for which the manifest is generated</param> - /// <param name="assemblyPathToIncludeInManifest">The manifest XML fragment contains the string name of the DLL name in - /// which it is embedded. This parameter specifies what name will be used</param> - /// <param name="flags">The flags to customize manifest generation. If flags has bit OnlyIfNeededForRegistration specified - /// this returns null when the eventSourceType does not require explicit registration</param> - /// <returns>The XML data string or null</returns> - public static string? GenerateManifest(Type eventSourceType, string? assemblyPathToIncludeInManifest, EventManifestOptions flags) - { - if (eventSourceType == null) - throw new ArgumentNullException(nameof(eventSourceType)); - - byte[]? manifestBytes = EventSource.CreateManifestAndDescriptors(eventSourceType, assemblyPathToIncludeInManifest, null, flags); - return (manifestBytes == null) ? null : Encoding.UTF8.GetString(manifestBytes, 0, manifestBytes.Length); - } - - // EventListener support - /// <summary> - /// returns a list (IEnumerable) of all sources in the appdomain). EventListeners typically need this. - /// </summary> - /// <returns></returns> - public static IEnumerable<EventSource> GetSources() - { - var ret = new List<EventSource>(); - lock (EventListener.EventListenersLock) - { - Debug.Assert(EventListener.s_EventSources != null); - - foreach (WeakReference<EventSource> eventSourceRef in EventListener.s_EventSources) - { - if (eventSourceRef.TryGetTarget(out EventSource? eventSource) && !eventSource.IsDisposed) - ret.Add(eventSource); - } - } - return ret; - } - - /// <summary> - /// Send a command to a particular EventSource identified by 'eventSource'. - /// Calling this routine simply forwards the command to the EventSource.OnEventCommand - /// callback. What the EventSource does with the command and its arguments are from - /// that point EventSource-specific. - /// </summary> - /// <param name="eventSource">The instance of EventSource to send the command to</param> - /// <param name="command">A positive user-defined EventCommand, or EventCommand.SendManifest</param> - /// <param name="commandArguments">A set of (name-argument, value-argument) pairs associated with the command</param> - public static void SendCommand(EventSource eventSource, EventCommand command, IDictionary<string, string?>? commandArguments) - { - if (eventSource == null) - throw new ArgumentNullException(nameof(eventSource)); - - // User-defined EventCommands should not conflict with the reserved commands. - if ((int)command <= (int)EventCommand.Update && (int)command != (int)EventCommand.SendManifest) - { - throw new ArgumentException(SR.EventSource_InvalidCommand, nameof(command)); - } - - eventSource.SendCommand(null, EventProviderType.ETW, 0, 0, command, true, EventLevel.LogAlways, EventKeywords.None, commandArguments); - } - - // Error APIs. (We don't throw by default, but you can probe for status) - /// <summary> - /// Because - /// - /// 1) Logging is often optional and thus should not generate fatal errors (exceptions) - /// 2) EventSources are often initialized in class constructors (which propagate exceptions poorly) - /// - /// The event source constructor does not throw exceptions. Instead we remember any exception that - /// was generated (it is also logged to Trace.WriteLine). - /// </summary> - public Exception? ConstructionException => m_constructionException; - - /// <summary> - /// EventSources can have arbitrary string key-value pairs associated with them called Traits. - /// These traits are not interpreted by the EventSource but may be interpreted by EventListeners - /// (e.g. like the built in ETW listener). These traits are specified at EventSource - /// construction time and can be retrieved by using this GetTrait API. - /// </summary> - /// <param name="key">The key to look up in the set of key-value pairs passed to the EventSource constructor</param> - /// <returns>The value string associated with key. Will return null if there is no such key.</returns> - public string? GetTrait(string key) - { - if (m_traits != null) - { - for (int i = 0; i < m_traits.Length - 1; i += 2) - { - if (m_traits[i] == key) - return m_traits[i + 1]; - } - } - - return null; - } - - /// <summary> - /// Displays the name and GUID for the eventSource for debugging purposes. - /// </summary> - public override string ToString() - { - return SR.Format(SR.EventSource_ToString, Name, Guid); - } - - /// <summary> - /// Fires when a Command (e.g. Enable) comes from a an EventListener. - /// </summary> - public event EventHandler<EventCommandEventArgs>? EventCommandExecuted - { - add - { - if (value == null) - return; - - m_eventCommandExecuted += value; - - // If we have an EventHandler<EventCommandEventArgs> attached to the EventSource before the first command arrives - // It should get a chance to handle the deferred commands. - EventCommandEventArgs? deferredCommands = m_deferredCommands; - while (deferredCommands != null) - { - value(this, deferredCommands); - deferredCommands = deferredCommands.nextCommand; - } - } - remove - { - m_eventCommandExecuted -= value; - } - } - -#region ActivityID - - /// <summary> - /// When a thread starts work that is on behalf of 'something else' (typically another - /// thread or network request) it should mark the thread as working on that other work. - /// This API marks the current thread as working on activity 'activityID'. This API - /// should be used when the caller knows the thread's current activity (the one being - /// overwritten) has completed. Otherwise, callers should prefer the overload that - /// return the oldActivityThatWillContinue (below). - /// - /// All events created with the EventSource on this thread are also tagged with the - /// activity ID of the thread. - /// - /// It is common, and good practice after setting the thread to an activity to log an event - /// with a 'start' opcode to indicate that precise time/thread where the new activity - /// started. - /// </summary> - /// <param name="activityId">A Guid that represents the new activity with which to mark - /// the current thread</param> - public static void SetCurrentThreadActivityId(Guid activityId) - { - if (TplEventSource.Log != null) - TplEventSource.Log.SetActivityId(activityId); - - // We ignore errors to keep with the convention that EventSources do not throw errors. - // Note we can't access m_throwOnWrites because this is a static method. -#if FEATURE_MANAGED_ETW -#if FEATURE_PERFTRACING - // Set the activity id via EventPipe. - EventPipeInternal.EventActivityIdControl( - (uint)Interop.Advapi32.ActivityControl.EVENT_ACTIVITY_CTRL_SET_ID, - ref activityId); -#endif // FEATURE_PERFTRACING -#if PLATFORM_WINDOWS - // Set the activity id via ETW. - Interop.Advapi32.EventActivityIdControl( - Interop.Advapi32.ActivityControl.EVENT_ACTIVITY_CTRL_SET_ID, - ref activityId); -#endif // PLATFORM_WINDOWS -#endif // FEATURE_MANAGED_ETW - } - - /// <summary> - /// Retrieves the ETW activity ID associated with the current thread. - /// </summary> - public static Guid CurrentThreadActivityId - { - get - { - // We ignore errors to keep with the convention that EventSources do not throw - // errors. Note we can't access m_throwOnWrites because this is a static method. - Guid retVal = default; -#if FEATURE_MANAGED_ETW -#if PLATFORM_WINDOWS - Interop.Advapi32.EventActivityIdControl( - Interop.Advapi32.ActivityControl.EVENT_ACTIVITY_CTRL_GET_ID, - ref retVal); -#elif FEATURE_PERFTRACING - EventPipeInternal.EventActivityIdControl( - (uint)Interop.Advapi32.ActivityControl.EVENT_ACTIVITY_CTRL_GET_ID, - ref retVal); -#endif // PLATFORM_WINDOWS -#endif // FEATURE_MANAGED_ETW - return retVal; - } - } - - /// <summary> - /// When a thread starts work that is on behalf of 'something else' (typically another - /// thread or network request) it should mark the thread as working on that other work. - /// This API marks the current thread as working on activity 'activityID'. It returns - /// whatever activity the thread was previously marked with. There is a convention that - /// callers can assume that callees restore this activity mark before the callee returns. - /// To encourage this, this API returns the old activity, so that it can be restored later. - /// - /// All events created with the EventSource on this thread are also tagged with the - /// activity ID of the thread. - /// - /// It is common, and good practice after setting the thread to an activity to log an event - /// with a 'start' opcode to indicate that precise time/thread where the new activity - /// started. - /// </summary> - /// <param name="activityId">A Guid that represents the new activity with which to mark - /// the current thread</param> - /// <param name="oldActivityThatWillContinue">The Guid that represents the current activity - /// which will continue at some point in the future, on the current thread</param> - public static void SetCurrentThreadActivityId(Guid activityId, out Guid oldActivityThatWillContinue) - { - oldActivityThatWillContinue = activityId; -#if FEATURE_MANAGED_ETW - // We ignore errors to keep with the convention that EventSources do not throw errors. - // Note we can't access m_throwOnWrites because this is a static method. - -#if FEATURE_PERFTRACING && PLATFORM_WINDOWS - EventPipeInternal.EventActivityIdControl( - (uint)Interop.Advapi32.ActivityControl.EVENT_ACTIVITY_CTRL_SET_ID, - ref oldActivityThatWillContinue); -#elif FEATURE_PERFTRACING - EventPipeInternal.EventActivityIdControl( - (uint)Interop.Advapi32.ActivityControl.EVENT_ACTIVITY_CTRL_GET_SET_ID, - ref oldActivityThatWillContinue); -#endif // FEATURE_PERFTRACING && PLATFORM_WINDOWS - -#if PLATFORM_WINDOWS - Interop.Advapi32.EventActivityIdControl( - Interop.Advapi32.ActivityControl.EVENT_ACTIVITY_CTRL_GET_SET_ID, - ref oldActivityThatWillContinue); -#endif // PLATFORM_WINDOWS -#endif // FEATURE_MANAGED_ETW - - // We don't call the activityDying callback here because the caller has declared that - // it is not dying. - if (TplEventSource.Log != null) - TplEventSource.Log.SetActivityId(activityId); - } -#endregion - -#region protected - /// <summary> - /// This is the constructor that most users will use to create their eventSource. It takes - /// no parameters. The ETW provider name and GUID of the EventSource are determined by the EventSource - /// custom attribute (so you can determine these things declaratively). If the GUID for the eventSource - /// is not specified in the EventSourceAttribute (recommended), it is Generated by hashing the name. - /// If the ETW provider name of the EventSource is not given, the name of the EventSource class is used as - /// the ETW provider name. - /// </summary> - protected EventSource() - : this(EventSourceSettings.EtwManifestEventFormat) - { - } - - /// <summary> - /// By default calling the 'WriteEvent' methods do NOT throw on errors (they silently discard the event). - /// This is because in most cases users assume logging is not 'precious' and do NOT wish to have logging failures - /// crash the program. However for those applications where logging is 'precious' and if it fails the caller - /// wishes to react, setting 'throwOnEventWriteErrors' will cause an exception to be thrown if WriteEvent - /// fails. Note the fact that EventWrite succeeds does not necessarily mean that the event reached its destination - /// only that operation of writing it did not fail. These EventSources will not generate self-describing ETW events. - /// - /// For compatibility only use the EventSourceSettings.ThrowOnEventWriteErrors flag instead. - /// </summary> - // [Obsolete("Use the EventSource(EventSourceSettings) overload")] - protected EventSource(bool throwOnEventWriteErrors) - : this(EventSourceSettings.EtwManifestEventFormat | (throwOnEventWriteErrors ? EventSourceSettings.ThrowOnEventWriteErrors : 0)) - { } - - /// <summary> - /// Construct an EventSource with additional non-default settings (see EventSourceSettings for more) - /// </summary> - protected EventSource(EventSourceSettings settings) : this(settings, null) { } - - /// <summary> - /// Construct an EventSource with additional non-default settings. - /// - /// Also specify a list of key-value pairs called traits (you must pass an even number of strings). - /// The first string is the key and the second is the value. These are not interpreted by EventSource - /// itself but may be interpreted the listeners. Can be fetched with GetTrait(string). - /// </summary> - /// <param name="settings">See EventSourceSettings for more.</param> - /// <param name="traits">A collection of key-value strings (must be an even number).</param> - protected EventSource(EventSourceSettings settings, params string[]? traits) - { - m_config = ValidateSettings(settings); - - Guid eventSourceGuid; - string? eventSourceName; - - GetMetadata(out eventSourceGuid, out eventSourceName, out _, out _); - - if (eventSourceGuid.Equals(Guid.Empty) || eventSourceName == null) - { - Type myType = this.GetType(); - eventSourceGuid = GetGuid(myType); - eventSourceName = GetName(myType); - } - - Initialize(eventSourceGuid, eventSourceName, traits); - } - -#if FEATURE_PERFTRACING - // Generate the serialized blobs that describe events for all strongly typed events (that is events that define strongly - // typed event methods. Dynamically defined events (that use Write) hare defined on the fly and are handled elsewhere. - private unsafe void DefineEventPipeEvents() - { - // If the EventSource is set to emit all events as TraceLogging events, skip this initialization. - // Events will be defined when they are emitted for the first time. - if (SelfDescribingEvents) - { - return; - } - - Debug.Assert(m_eventData != null); - Debug.Assert(m_eventPipeProvider != null); - int cnt = m_eventData.Length; - for (int i = 0; i < cnt; i++) - { - uint eventID = (uint)m_eventData[i].Descriptor.EventId; - if (eventID == 0) - continue; - - byte[]? metadata = EventPipeMetadataGenerator.Instance.GenerateEventMetadata(m_eventData[i]); - uint metadataLength = (metadata != null) ? (uint)metadata.Length : 0; - - string eventName = m_eventData[i].Name; - long keywords = m_eventData[i].Descriptor.Keywords; - uint eventVersion = m_eventData[i].Descriptor.Version; - uint level = m_eventData[i].Descriptor.Level; - - fixed (byte* pMetadata = metadata) - { - IntPtr eventHandle = m_eventPipeProvider.m_eventProvider.DefineEventHandle( - eventID, - eventName, - keywords, - eventVersion, - level, - pMetadata, - metadataLength); - - Debug.Assert(eventHandle != IntPtr.Zero); - m_eventData[i].EventHandle = eventHandle; - } - } - } -#endif - - internal virtual void GetMetadata(out Guid eventSourceGuid, out string? eventSourceName, out EventMetadata[]? eventData, out byte[]? manifestBytes) - { - // - // In ProjectN subclasses need to override this method, and return the data from their EventSourceAttribute and EventAttribute annotations. - // On other architectures it is a no-op. - // - // eventDescriptors needs to contain one EventDescriptor for each event; the event's ID should be the same as its index in this array. - // manifestBytes is a UTF-8 encoding of the ETW manifest for the type. - // - // This will be implemented by an IL rewriter, so we can't make this method abstract or the initial build of the subclass would fail. - // - eventSourceGuid = Guid.Empty; - eventSourceName = null; - eventData = null; - manifestBytes = null; - - return; - } - - /// <summary> - /// This method is called when the eventSource is updated by the controller. - /// </summary> - protected virtual void OnEventCommand(EventCommandEventArgs command) { } - -#pragma warning disable 1591 - // optimized for common signatures (no args) - protected unsafe void WriteEvent(int eventId) - { - WriteEventCore(eventId, 0, null); - } - - // optimized for common signatures (ints) - protected unsafe void WriteEvent(int eventId, int arg1) - { - if (m_eventSourceEnabled) - { - EventSource.EventData* descrs = stackalloc EventSource.EventData[1]; - descrs[0].DataPointer = (IntPtr)(&arg1); - descrs[0].Size = 4; - descrs[0].Reserved = 0; - WriteEventCore(eventId, 1, descrs); - } - } - - protected unsafe void WriteEvent(int eventId, int arg1, int arg2) - { - if (m_eventSourceEnabled) - { - EventSource.EventData* descrs = stackalloc EventSource.EventData[2]; - descrs[0].DataPointer = (IntPtr)(&arg1); - descrs[0].Size = 4; - descrs[0].Reserved = 0; - descrs[1].DataPointer = (IntPtr)(&arg2); - descrs[1].Size = 4; - descrs[1].Reserved = 0; - WriteEventCore(eventId, 2, descrs); - } - } - - protected unsafe void WriteEvent(int eventId, int arg1, int arg2, int arg3) - { - if (m_eventSourceEnabled) - { - EventSource.EventData* descrs = stackalloc EventSource.EventData[3]; - descrs[0].DataPointer = (IntPtr)(&arg1); - descrs[0].Size = 4; - descrs[0].Reserved = 0; - descrs[1].DataPointer = (IntPtr)(&arg2); - descrs[1].Size = 4; - descrs[1].Reserved = 0; - descrs[2].DataPointer = (IntPtr)(&arg3); - descrs[2].Size = 4; - descrs[2].Reserved = 0; - WriteEventCore(eventId, 3, descrs); - } - } - - // optimized for common signatures (longs) - protected unsafe void WriteEvent(int eventId, long arg1) - { - if (m_eventSourceEnabled) - { - EventSource.EventData* descrs = stackalloc EventSource.EventData[1]; - descrs[0].DataPointer = (IntPtr)(&arg1); - descrs[0].Size = 8; - descrs[0].Reserved = 0; - WriteEventCore(eventId, 1, descrs); - } - } - - protected unsafe void WriteEvent(int eventId, long arg1, long arg2) - { - if (m_eventSourceEnabled) - { - EventSource.EventData* descrs = stackalloc EventSource.EventData[2]; - descrs[0].DataPointer = (IntPtr)(&arg1); - descrs[0].Size = 8; - descrs[0].Reserved = 0; - descrs[1].DataPointer = (IntPtr)(&arg2); - descrs[1].Size = 8; - descrs[1].Reserved = 0; - WriteEventCore(eventId, 2, descrs); - } - } - - protected unsafe void WriteEvent(int eventId, long arg1, long arg2, long arg3) - { - if (m_eventSourceEnabled) - { - EventSource.EventData* descrs = stackalloc EventSource.EventData[3]; - descrs[0].DataPointer = (IntPtr)(&arg1); - descrs[0].Size = 8; - descrs[0].Reserved = 0; - descrs[1].DataPointer = (IntPtr)(&arg2); - descrs[1].Size = 8; - descrs[1].Reserved = 0; - descrs[2].DataPointer = (IntPtr)(&arg3); - descrs[2].Size = 8; - descrs[2].Reserved = 0; - WriteEventCore(eventId, 3, descrs); - } - } - - // optimized for common signatures (strings) - protected unsafe void WriteEvent(int eventId, string? arg1) - { - if (m_eventSourceEnabled) - { - arg1 ??= ""; - fixed (char* string1Bytes = arg1) - { - EventSource.EventData* descrs = stackalloc EventSource.EventData[1]; - descrs[0].DataPointer = (IntPtr)string1Bytes; - descrs[0].Size = ((arg1.Length + 1) * 2); - descrs[0].Reserved = 0; - WriteEventCore(eventId, 1, descrs); - } - } - } - - protected unsafe void WriteEvent(int eventId, string? arg1, string? arg2) - { - if (m_eventSourceEnabled) - { - arg1 ??= ""; - arg2 ??= ""; - fixed (char* string1Bytes = arg1) - fixed (char* string2Bytes = arg2) - { - EventSource.EventData* descrs = stackalloc EventSource.EventData[2]; - descrs[0].DataPointer = (IntPtr)string1Bytes; - descrs[0].Size = ((arg1.Length + 1) * 2); - descrs[0].Reserved = 0; - descrs[1].DataPointer = (IntPtr)string2Bytes; - descrs[1].Size = ((arg2.Length + 1) * 2); - descrs[1].Reserved = 0; - WriteEventCore(eventId, 2, descrs); - } - } - } - - protected unsafe void WriteEvent(int eventId, string? arg1, string? arg2, string? arg3) - { - if (m_eventSourceEnabled) - { - arg1 ??= ""; - arg2 ??= ""; - arg3 ??= ""; - fixed (char* string1Bytes = arg1) - fixed (char* string2Bytes = arg2) - fixed (char* string3Bytes = arg3) - { - EventSource.EventData* descrs = stackalloc EventSource.EventData[3]; - descrs[0].DataPointer = (IntPtr)string1Bytes; - descrs[0].Size = ((arg1.Length + 1) * 2); - descrs[0].Reserved = 0; - descrs[1].DataPointer = (IntPtr)string2Bytes; - descrs[1].Size = ((arg2.Length + 1) * 2); - descrs[1].Reserved = 0; - descrs[2].DataPointer = (IntPtr)string3Bytes; - descrs[2].Size = ((arg3.Length + 1) * 2); - descrs[2].Reserved = 0; - WriteEventCore(eventId, 3, descrs); - } - } - } - - // optimized for common signatures (string and ints) - protected unsafe void WriteEvent(int eventId, string? arg1, int arg2) - { - if (m_eventSourceEnabled) - { - arg1 ??= ""; - fixed (char* string1Bytes = arg1) - { - EventSource.EventData* descrs = stackalloc EventSource.EventData[2]; - descrs[0].DataPointer = (IntPtr)string1Bytes; - descrs[0].Size = ((arg1.Length + 1) * 2); - descrs[0].Reserved = 0; - descrs[1].DataPointer = (IntPtr)(&arg2); - descrs[1].Size = 4; - descrs[1].Reserved = 0; - WriteEventCore(eventId, 2, descrs); - } - } - } - - protected unsafe void WriteEvent(int eventId, string? arg1, int arg2, int arg3) - { - if (m_eventSourceEnabled) - { - arg1 ??= ""; - fixed (char* string1Bytes = arg1) - { - EventSource.EventData* descrs = stackalloc EventSource.EventData[3]; - descrs[0].DataPointer = (IntPtr)string1Bytes; - descrs[0].Size = ((arg1.Length + 1) * 2); - descrs[0].Reserved = 0; - descrs[1].DataPointer = (IntPtr)(&arg2); - descrs[1].Size = 4; - descrs[1].Reserved = 0; - descrs[2].DataPointer = (IntPtr)(&arg3); - descrs[2].Size = 4; - descrs[2].Reserved = 0; - WriteEventCore(eventId, 3, descrs); - } - } - } - - // optimized for common signatures (string and longs) - protected unsafe void WriteEvent(int eventId, string? arg1, long arg2) - { - if (m_eventSourceEnabled) - { - arg1 ??= ""; - fixed (char* string1Bytes = arg1) - { - EventSource.EventData* descrs = stackalloc EventSource.EventData[2]; - descrs[0].DataPointer = (IntPtr)string1Bytes; - descrs[0].Size = ((arg1.Length + 1) * 2); - descrs[0].Reserved = 0; - descrs[1].DataPointer = (IntPtr)(&arg2); - descrs[1].Size = 8; - descrs[1].Reserved = 0; - WriteEventCore(eventId, 2, descrs); - } - } - } - - // optimized for common signatures (long and string) - protected unsafe void WriteEvent(int eventId, long arg1, string? arg2) - { - if (m_eventSourceEnabled) - { - arg2 ??= ""; - fixed (char* string2Bytes = arg2) - { - EventSource.EventData* descrs = stackalloc EventSource.EventData[2]; - descrs[0].DataPointer = (IntPtr)(&arg1); - descrs[0].Size = 8; - descrs[0].Reserved = 0; - descrs[1].DataPointer = (IntPtr)string2Bytes; - descrs[1].Size = ((arg2.Length + 1) * 2); - descrs[1].Reserved = 0; - WriteEventCore(eventId, 2, descrs); - } - } - } - - // optimized for common signatures (int and string) - protected unsafe void WriteEvent(int eventId, int arg1, string? arg2) - { - if (m_eventSourceEnabled) - { - arg2 ??= ""; - fixed (char* string2Bytes = arg2) - { - EventSource.EventData* descrs = stackalloc EventSource.EventData[2]; - descrs[0].DataPointer = (IntPtr)(&arg1); - descrs[0].Size = 4; - descrs[0].Reserved = 0; - descrs[1].DataPointer = (IntPtr)string2Bytes; - descrs[1].Size = ((arg2.Length + 1) * 2); - descrs[1].Reserved = 0; - WriteEventCore(eventId, 2, descrs); - } - } - } - - protected unsafe void WriteEvent(int eventId, byte[]? arg1) - { - if (m_eventSourceEnabled) - { - EventSource.EventData* descrs = stackalloc EventSource.EventData[2]; - if (arg1 == null || arg1.Length == 0) - { - int blobSize = 0; - descrs[0].DataPointer = (IntPtr)(&blobSize); - descrs[0].Size = 4; - descrs[0].Reserved = 0; - descrs[1].DataPointer = (IntPtr)(&blobSize); // valid address instead of empty content - descrs[1].Size = 0; - descrs[1].Reserved = 0; - WriteEventCore(eventId, 2, descrs); - } - else - { - int blobSize = arg1.Length; - fixed (byte* blob = &arg1[0]) - { - descrs[0].DataPointer = (IntPtr)(&blobSize); - descrs[0].Size = 4; - descrs[0].Reserved = 0; - descrs[1].DataPointer = (IntPtr)blob; - descrs[1].Size = blobSize; - descrs[1].Reserved = 0; - WriteEventCore(eventId, 2, descrs); - } - } - } - } - - protected unsafe void WriteEvent(int eventId, long arg1, byte[]? arg2) - { - if (m_eventSourceEnabled) - { - EventSource.EventData* descrs = stackalloc EventSource.EventData[3]; - descrs[0].DataPointer = (IntPtr)(&arg1); - descrs[0].Size = 8; - descrs[0].Reserved = 0; - if (arg2 == null || arg2.Length == 0) - { - int blobSize = 0; - descrs[1].DataPointer = (IntPtr)(&blobSize); - descrs[1].Size = 4; - descrs[1].Reserved = 0; - descrs[2].DataPointer = (IntPtr)(&blobSize); // valid address instead of empty contents - descrs[2].Size = 0; - descrs[2].Reserved = 0; - WriteEventCore(eventId, 3, descrs); - } - else - { - int blobSize = arg2.Length; - fixed (byte* blob = &arg2[0]) - { - descrs[1].DataPointer = (IntPtr)(&blobSize); - descrs[1].Size = 4; - descrs[1].Reserved = 0; - descrs[2].DataPointer = (IntPtr)blob; - descrs[2].Size = blobSize; - descrs[2].Reserved = 0; - WriteEventCore(eventId, 3, descrs); - } - } - } - } - -#pragma warning restore 1591 - - /// <summary> - /// Used to construct the data structure to be passed to the native ETW APIs - EventWrite and EventWriteTransfer. - /// </summary> - protected internal struct EventData - { - /// <summary> - /// Address where the one argument lives (if this points to managed memory you must ensure the - /// managed object is pinned. - /// </summary> - public unsafe IntPtr DataPointer - { - get => (IntPtr)(void*)m_Ptr; - set => m_Ptr = unchecked((ulong)(void*)value); - } - - /// <summary> - /// Size of the argument referenced by DataPointer - /// </summary> - public int Size - { - get => m_Size; - set => m_Size = value; - } - - /// <summary> - /// Reserved by ETW. This property is present to ensure that we can zero it - /// since System.Private.CoreLib uses are not zero'd. - /// </summary> - internal int Reserved - { - get => m_Reserved; - set => m_Reserved = value; - } - -#region private - /// <summary> - /// Initializes the members of this EventData object to point at a previously-pinned - /// tracelogging-compatible metadata blob. - /// </summary> - /// <param name="pointer">Pinned tracelogging-compatible metadata blob.</param> - /// <param name="size">The size of the metadata blob.</param> - /// <param name="reserved">Value for reserved: 2 for per-provider metadata, 1 for per-event metadata</param> - internal unsafe void SetMetadata(byte* pointer, int size, int reserved) - { - this.m_Ptr = (ulong)pointer; - this.m_Size = size; - this.m_Reserved = reserved; // Mark this descriptor as containing tracelogging-compatible metadata. - } - - // Important, we pass this structure directly to the Win32 EventWrite API, so this structure must - // be layed out exactly the way EventWrite wants it. - internal ulong m_Ptr; - internal int m_Size; -#pragma warning disable 0649 - internal int m_Reserved; // Used to pad the size to match the Win32 API -#pragma warning restore 0649 -#endregion - } - - /// <summary> - /// This routine allows you to create efficient WriteEvent helpers, however the code that you use to - /// do this, while straightforward, is unsafe. - /// </summary> - /// <remarks> - /// <code> - /// protected unsafe void WriteEvent(int eventId, string arg1, long arg2) - /// { - /// if (IsEnabled()) - /// { - /// arg2 ??= ""; - /// fixed (char* string2Bytes = arg2) - /// { - /// EventSource.EventData* descrs = stackalloc EventSource.EventData[2]; - /// descrs[0].DataPointer = (IntPtr)(&arg1); - /// descrs[0].Size = 8; - /// descrs[0].Reserved = 0; - /// descrs[1].DataPointer = (IntPtr)string2Bytes; - /// descrs[1].Size = ((arg2.Length + 1) * 2); - /// descrs[1].Reserved = 0; - /// WriteEventCore(eventId, 2, descrs); - /// } - /// } - /// } - /// </code> - /// </remarks> - [CLSCompliant(false)] - protected unsafe void WriteEventCore(int eventId, int eventDataCount, EventSource.EventData* data) - { - WriteEventWithRelatedActivityIdCore(eventId, null, eventDataCount, data); - } - - /// <summary> - /// This routine allows you to create efficient WriteEventWithRelatedActivityId helpers, however the code - /// that you use to do this, while straightforward, is unsafe. The only difference from - /// <see cref="WriteEventCore"/> is that you pass the relatedActivityId from caller through to this API - /// </summary> - /// <remarks> - /// <code> - /// protected unsafe void WriteEventWithRelatedActivityId(int eventId, Guid relatedActivityId, string arg1, long arg2) - /// { - /// if (IsEnabled()) - /// { - /// arg2 ??= ""; - /// fixed (char* string2Bytes = arg2) - /// { - /// EventSource.EventData* descrs = stackalloc EventSource.EventData[2]; - /// descrs[0].DataPointer = (IntPtr)(&arg1); - /// descrs[0].Size = 8; - /// descrs[1].DataPointer = (IntPtr)string2Bytes; - /// descrs[1].Size = ((arg2.Length + 1) * 2); - /// WriteEventWithRelatedActivityIdCore(eventId, relatedActivityId, 2, descrs); - /// } - /// } - /// } - /// </code> - /// </remarks> - [CLSCompliant(false)] - protected unsafe void WriteEventWithRelatedActivityIdCore(int eventId, Guid* relatedActivityId, int eventDataCount, EventSource.EventData* data) - { - if (m_eventSourceEnabled) - { - Debug.Assert(m_eventData != null); // You must have initialized this if you enabled the source. - try - { - if (relatedActivityId != null) - ValidateEventOpcodeForTransfer(ref m_eventData[eventId], m_eventData[eventId].Name); - - EventOpcode opcode = (EventOpcode)m_eventData[eventId].Descriptor.Opcode; - EventActivityOptions activityOptions = m_eventData[eventId].ActivityOptions; - Guid* pActivityId = null; - Guid activityId = Guid.Empty; - Guid relActivityId = Guid.Empty; - - if (opcode != EventOpcode.Info && relatedActivityId == null && - ((activityOptions & EventActivityOptions.Disable) == 0)) - { - if (opcode == EventOpcode.Start) - { - m_activityTracker.OnStart(m_name, m_eventData[eventId].Name, m_eventData[eventId].Descriptor.Task, ref activityId, ref relActivityId, m_eventData[eventId].ActivityOptions); - } - else if (opcode == EventOpcode.Stop) - { - m_activityTracker.OnStop(m_name, m_eventData[eventId].Name, m_eventData[eventId].Descriptor.Task, ref activityId); - } - - if (activityId != Guid.Empty) - pActivityId = &activityId; - if (relActivityId != Guid.Empty) - relatedActivityId = &relActivityId; - } - -#if FEATURE_MANAGED_ETW - if (m_eventData[eventId].EnabledForETW -#if FEATURE_PERFTRACING - || m_eventData[eventId].EnabledForEventPipe -#endif // FEATURE_PERFTRACING - ) - { - if (!SelfDescribingEvents) - { - if (!m_etwProvider.WriteEvent(ref m_eventData[eventId].Descriptor, m_eventData[eventId].EventHandle, pActivityId, relatedActivityId, eventDataCount, (IntPtr)data)) - ThrowEventSourceException(m_eventData[eventId].Name); -#if FEATURE_PERFTRACING - if (!m_eventPipeProvider.WriteEvent(ref m_eventData[eventId].Descriptor, m_eventData[eventId].EventHandle, pActivityId, relatedActivityId, eventDataCount, (IntPtr)data)) - ThrowEventSourceException(m_eventData[eventId].Name); -#endif // FEATURE_PERFTRACING - } - else - { - TraceLoggingEventTypes? tlet = m_eventData[eventId].TraceLoggingEventTypes; - if (tlet == null) - { - tlet = new TraceLoggingEventTypes(m_eventData[eventId].Name, - m_eventData[eventId].Tags, - m_eventData[eventId].Parameters); - Interlocked.CompareExchange(ref m_eventData[eventId].TraceLoggingEventTypes, tlet, null); - } - EventSourceOptions opt = new EventSourceOptions - { - Keywords = (EventKeywords)m_eventData[eventId].Descriptor.Keywords, - Level = (EventLevel)m_eventData[eventId].Descriptor.Level, - Opcode = (EventOpcode)m_eventData[eventId].Descriptor.Opcode - }; - - WriteMultiMerge(m_eventData[eventId].Name, ref opt, tlet, pActivityId, relatedActivityId, data); - } - } -#endif // FEATURE_MANAGED_ETW - - if (m_Dispatchers != null && m_eventData[eventId].EnabledForAnyListener) - WriteToAllListeners(eventId, pActivityId, relatedActivityId, eventDataCount, data); - } - catch (Exception ex) - { - if (ex is EventSourceException) - throw; - else - ThrowEventSourceException(m_eventData[eventId].Name, ex); - } - } - } - - // fallback varags helpers. - /// <summary> - /// This is the 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 <see cref="WriteEventCore"/> to create fast helpers for your particular - /// method signature. Even if you use this for rare events, this call should be guarded by an <see cref="IsEnabled()"/> - /// check so that the varargs call is not made when the EventSource is not active. - /// </summary> - protected unsafe void WriteEvent(int eventId, params object?[] args) - { - WriteEventVarargs(eventId, null, args); - } - - /// <summary> - /// This is the varargs helper for writing an event which also specifies a related activity. It is completely analogous - /// to corresponding WriteEvent (they share implementation). 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 <see cref="WriteEventWithRelatedActivityIdCore"/> to create fast helpers for your - /// particular method signature. Even if you use this for rare events, this call should be guarded by an <see cref="IsEnabled()"/> - /// check so that the varargs call is not made when the EventSource is not active. - /// </summary> - protected unsafe void WriteEventWithRelatedActivityId(int eventId, Guid relatedActivityId, params object?[] args) - { - WriteEventVarargs(eventId, &relatedActivityId, args); - } - -#endregion - -#region IDisposable Members - /// <summary> - /// Disposes of an EventSource. - /// </summary> - public void Dispose() - { - this.Dispose(true); - GC.SuppressFinalize(this); - } - /// <summary> - /// Disposes of an EventSource. - /// </summary> - /// <remarks> - /// Called from Dispose() with disposing=true, and from the finalizer (~EventSource) with disposing=false. - /// Guidelines: - /// 1. We may be called more than once: do nothing after the first call. - /// 2. Avoid throwing exceptions if disposing is false, i.e. if we're being finalized. - /// </remarks> - /// <param name="disposing">True if called from Dispose(), false if called from the finalizer.</param> - protected virtual void Dispose(bool disposing) - { - if (disposing) - { -#if FEATURE_MANAGED_ETW - // Send the manifest one more time to ensure circular buffers have a chance to get to this information - // even in scenarios with a high volume of ETW events. - if (m_eventSourceEnabled) - { - try - { - SendManifest(m_rawManifest); - } - catch { } // If it fails, simply give up. - m_eventSourceEnabled = false; - } - if (m_etwProvider != null) - { - m_etwProvider.Dispose(); - m_etwProvider = null!; // TODO-NULLABLE: Avoid nulling out in Dispose - } -#endif -#if FEATURE_PERFTRACING - if (m_eventPipeProvider != null) - { - m_eventPipeProvider.Dispose(); - m_eventPipeProvider = null!; // TODO-NULLABLE: Avoid nulling out in Dispose - } -#endif - } - m_eventSourceEnabled = false; - m_eventSourceDisposed = true; - } - /// <summary> - /// Finalizer for EventSource - /// </summary> - ~EventSource() - { - this.Dispose(false); - } -#endregion - -#region private - - private unsafe void WriteEventRaw( - string? eventName, - ref EventDescriptor eventDescriptor, - IntPtr eventHandle, - Guid* activityID, - Guid* relatedActivityID, - int dataCount, - IntPtr data) - { -#if FEATURE_MANAGED_ETW - if (m_etwProvider == null) - { - ThrowEventSourceException(eventName); - } - else - { - if (!m_etwProvider.WriteEventRaw(ref eventDescriptor, eventHandle, activityID, relatedActivityID, dataCount, data)) - ThrowEventSourceException(eventName); - } -#endif // FEATURE_MANAGED_ETW -#if FEATURE_PERFTRACING - if (m_eventPipeProvider == null) - { - ThrowEventSourceException(eventName); - } - else - { - if (!m_eventPipeProvider.WriteEventRaw(ref eventDescriptor, eventHandle, activityID, relatedActivityID, dataCount, data)) - ThrowEventSourceException(eventName); - } -#endif // FEATURE_PERFTRACING - } - - // FrameworkEventSource is on the startup path for the framework, so we have this internal overload that it can use - // to prevent the working set hit from looking at the custom attributes on the type to get the Guid. - internal EventSource(Guid eventSourceGuid, string eventSourceName) - : this(eventSourceGuid, eventSourceName, EventSourceSettings.EtwManifestEventFormat) - { } - - // Used by the internal FrameworkEventSource constructor and the TraceLogging-style event source constructor - internal EventSource(Guid eventSourceGuid, string eventSourceName, EventSourceSettings settings, string[]? traits = null) - { - m_config = ValidateSettings(settings); - Initialize(eventSourceGuid, eventSourceName, traits); - } - - /// <summary> - /// This method is responsible for the common initialization path from our constructors. It must - /// not leak any exceptions (otherwise, since most EventSource classes define a static member, - /// "Log", such an exception would become a cached exception for the initialization of the static - /// member, and any future access to the "Log" would throw the cached exception). - /// </summary> - private unsafe void Initialize(Guid eventSourceGuid, string eventSourceName, string[]? traits) - { - try - { - m_traits = traits; - if (m_traits != null && m_traits.Length % 2 != 0) - { - throw new ArgumentException(SR.EventSource_TraitEven, nameof(traits)); - } - - if (eventSourceGuid == Guid.Empty) - { - throw new ArgumentException(SR.EventSource_NeedGuid); - } - - if (eventSourceName == null) - { - throw new ArgumentException(SR.EventSource_NeedName); - } - - m_name = eventSourceName; - m_guid = eventSourceGuid; - - // Enable Implicit Activity tracker - m_activityTracker = ActivityTracker.Instance; - -#if FEATURE_MANAGED_ETW || FEATURE_PERFTRACING - // Create and register our provider traits. We do this early because it is needed to log errors - // In the self-describing event case. - this.InitializeProviderMetadata(); -#endif -#if FEATURE_MANAGED_ETW - // Register the provider with ETW - var etwProvider = new OverideEventProvider(this, EventProviderType.ETW); - etwProvider.Register(this); -#endif -#if FEATURE_PERFTRACING - // Register the provider with EventPipe - var eventPipeProvider = new OverideEventProvider(this, EventProviderType.EventPipe); - lock (EventListener.EventListenersLock) - { - eventPipeProvider.Register(this); - } -#endif - // Add the eventSource to the global (weak) list. - // This also sets m_id, which is the index in the list. - EventListener.AddEventSource(this); - -#if FEATURE_MANAGED_ETW - // OK if we get this far without an exception, then we can at least write out error messages. - // Set m_provider, which allows this. - m_etwProvider = etwProvider; - -#if PLATFORM_WINDOWS -#if (!ES_BUILD_STANDALONE && !ES_BUILD_PN) - // API available on OS >= Win 8 and patched Win 7. - // Disable only for FrameworkEventSource to avoid recursion inside exception handling. - if (this.Name != "System.Diagnostics.Eventing.FrameworkEventSource" || Environment.IsWindows8OrAbove) -#endif - { - int setInformationResult; - System.Runtime.InteropServices.GCHandle metadataHandle = - System.Runtime.InteropServices.GCHandle.Alloc(this.providerMetadata, System.Runtime.InteropServices.GCHandleType.Pinned); - IntPtr providerMetadata = metadataHandle.AddrOfPinnedObject(); - - setInformationResult = m_etwProvider.SetInformation( - Interop.Advapi32.EVENT_INFO_CLASS.SetTraits, - providerMetadata, - (uint)this.providerMetadata.Length); - - metadataHandle.Free(); - } -#endif // PLATFORM_WINDOWS -#endif // FEATURE_MANAGED_ETW - -#if FEATURE_PERFTRACING - m_eventPipeProvider = eventPipeProvider; -#endif - Debug.Assert(!m_eventSourceEnabled); // We can't be enabled until we are completely initted. - // We are logically completely initialized at this point. - m_completelyInited = true; - } - catch (Exception e) - { - m_constructionException ??= e; - ReportOutOfBandMessage("ERROR: Exception during construction of EventSource " + Name + ": " + e.Message); - } - - // Once m_completelyInited is set, you can have concurrency, so all work is under the lock. - lock (EventListener.EventListenersLock) - { - // If there are any deferred commands, we can do them now. - // This is the most likely place for exceptions to happen. - // Note that we are NOT resetting m_deferredCommands to NULL here, - // We are giving for EventHandler<EventCommandEventArgs> that will be attached later - EventCommandEventArgs? deferredCommands = m_deferredCommands; - while (deferredCommands != null) - { - DoCommand(deferredCommands); // This can never throw, it catches them and reports the errors. - deferredCommands = deferredCommands.nextCommand; - } - } - } - - private static string GetName(Type eventSourceType, EventManifestOptions flags) - { - if (eventSourceType == null) - throw new ArgumentNullException(nameof(eventSourceType)); - - EventSourceAttribute? attrib = (EventSourceAttribute?)GetCustomAttributeHelper(eventSourceType, typeof(EventSourceAttribute), flags); - if (attrib != null && attrib.Name != null) - return attrib.Name; - - return eventSourceType.Name; - } - - /// <summary> - /// Implements the SHA1 hashing algorithm. Note that this - /// implementation is for hashing public information. Do not - /// use this code to hash private data, as this implementation does - /// not take any steps to avoid information disclosure. - /// </summary> - private struct Sha1ForNonSecretPurposes - { - private long length; // Total message length in bits - private uint[] w; // Workspace - private int pos; // Length of current chunk in bytes - - /// <summary> - /// Call Start() to initialize the hash object. - /// </summary> - public void Start() - { - this.w ??= new uint[85]; - - this.length = 0; - this.pos = 0; - this.w[80] = 0x67452301; - this.w[81] = 0xEFCDAB89; - this.w[82] = 0x98BADCFE; - this.w[83] = 0x10325476; - this.w[84] = 0xC3D2E1F0; - } - - /// <summary> - /// Adds an input byte to the hash. - /// </summary> - /// <param name="input">Data to include in the hash.</param> - public void Append(byte input) - { - this.w[this.pos / 4] = (this.w[this.pos / 4] << 8) | input; - if (64 == ++this.pos) - { - this.Drain(); - } - } - - /// <summary> - /// Adds input bytes to the hash. - /// </summary> - /// <param name="input"> - /// Data to include in the hash. Must not be null. - /// </param> - public void Append(byte[] input) - { - foreach (byte b in input) - { - this.Append(b); - } - } - - /// <summary> - /// Retrieves the hash value. - /// Note that after calling this function, the hash object should - /// be considered uninitialized. Subsequent calls to Append or - /// Finish will produce useless results. Call Start() to - /// reinitialize. - /// </summary> - /// <param name="output"> - /// Buffer to receive the hash value. Must not be null. - /// Up to 20 bytes of hash will be written to the output buffer. - /// If the buffer is smaller than 20 bytes, the remaining hash - /// bytes will be lost. If the buffer is larger than 20 bytes, the - /// rest of the buffer is left unmodified. - /// </param> - public void Finish(byte[] output) - { - long l = this.length + 8 * this.pos; - this.Append(0x80); - while (this.pos != 56) - { - this.Append(0x00); - } - - unchecked - { - this.Append((byte)(l >> 56)); - this.Append((byte)(l >> 48)); - this.Append((byte)(l >> 40)); - this.Append((byte)(l >> 32)); - this.Append((byte)(l >> 24)); - this.Append((byte)(l >> 16)); - this.Append((byte)(l >> 8)); - this.Append((byte)l); - - int end = output.Length < 20 ? output.Length : 20; - for (int i = 0; i != end; i++) - { - uint temp = this.w[80 + i / 4]; - output[i] = (byte)(temp >> 24); - this.w[80 + i / 4] = temp << 8; - } - } - } - - /// <summary> - /// Called when this.pos reaches 64. - /// </summary> - private void Drain() - { - for (int i = 16; i != 80; i++) - { - this.w[i] = BitOperations.RotateLeft(this.w[i - 3] ^ this.w[i - 8] ^ this.w[i - 14] ^ this.w[i - 16], 1); - } - - unchecked - { - uint a = this.w[80]; - uint b = this.w[81]; - uint c = this.w[82]; - uint d = this.w[83]; - uint e = this.w[84]; - - for (int i = 0; i != 20; i++) - { - const uint k = 0x5A827999; - uint f = (b & c) | ((~b) & d); - uint temp = BitOperations.RotateLeft(a, 5) + f + e + k + this.w[i]; e = d; d = c; c = BitOperations.RotateLeft(b, 30); b = a; a = temp; - } - - for (int i = 20; i != 40; i++) - { - uint f = b ^ c ^ d; - const uint k = 0x6ED9EBA1; - uint temp = BitOperations.RotateLeft(a, 5) + f + e + k + this.w[i]; e = d; d = c; c = BitOperations.RotateLeft(b, 30); b = a; a = temp; - } - - for (int i = 40; i != 60; i++) - { - uint f = (b & c) | (b & d) | (c & d); - const uint k = 0x8F1BBCDC; - uint temp = BitOperations.RotateLeft(a, 5) + f + e + k + this.w[i]; e = d; d = c; c = BitOperations.RotateLeft(b, 30); b = a; a = temp; - } - - for (int i = 60; i != 80; i++) - { - uint f = b ^ c ^ d; - const uint k = 0xCA62C1D6; - uint temp = BitOperations.RotateLeft(a, 5) + f + e + k + this.w[i]; e = d; d = c; c = BitOperations.RotateLeft(b, 30); b = a; a = temp; - } - - this.w[80] += a; - this.w[81] += b; - this.w[82] += c; - this.w[83] += d; - this.w[84] += e; - } - - this.length += 512; // 64 bytes == 512 bits - this.pos = 0; - } - } - - private static Guid GenerateGuidFromName(string name) - { - if (namespaceBytes == null) - { - namespaceBytes = new byte[] { - 0x48, 0x2C, 0x2D, 0xB2, 0xC3, 0x90, 0x47, 0xC8, - 0x87, 0xF8, 0x1A, 0x15, 0xBF, 0xC1, 0x30, 0xFB, - }; - } - - byte[] bytes = Encoding.BigEndianUnicode.GetBytes(name); - Sha1ForNonSecretPurposes hash = default; - hash.Start(); - hash.Append(namespaceBytes); - hash.Append(bytes); - Array.Resize(ref bytes, 16); - hash.Finish(bytes); - - bytes[7] = unchecked((byte)((bytes[7] & 0x0F) | 0x50)); // Set high 4 bits of octet 7 to 5, as per RFC 4122 - return new Guid(bytes); - } - - private unsafe object? DecodeObject(int eventId, int parameterId, ref EventSource.EventData* data) - { - // TODO FIX : We use reflection which in turn uses EventSource, right now we carefully avoid - // the recursion, but can we do this in a robust way? - - IntPtr dataPointer = data->DataPointer; - // advance to next EventData in array - ++data; - - Debug.Assert(m_eventData != null); - Type dataType = GetDataType(m_eventData[eventId], parameterId); - - Again: - if (dataType == typeof(IntPtr)) - { - return *((IntPtr*)dataPointer); - } - else if (dataType == typeof(int)) - { - return *((int*)dataPointer); - } - else if (dataType == typeof(uint)) - { - return *((uint*)dataPointer); - } - else if (dataType == typeof(long)) - { - return *((long*)dataPointer); - } - else if (dataType == typeof(ulong)) - { - return *((ulong*)dataPointer); - } - else if (dataType == typeof(byte)) - { - return *((byte*)dataPointer); - } - else if (dataType == typeof(sbyte)) - { - return *((sbyte*)dataPointer); - } - else if (dataType == typeof(short)) - { - return *((short*)dataPointer); - } - else if (dataType == typeof(ushort)) - { - return *((ushort*)dataPointer); - } - else if (dataType == typeof(float)) - { - return *((float*)dataPointer); - } - else if (dataType == typeof(double)) - { - return *((double*)dataPointer); - } - else if (dataType == typeof(decimal)) - { - return *((decimal*)dataPointer); - } - else if (dataType == typeof(bool)) - { - // The manifest defines a bool as a 32bit type (WIN32 BOOL), not 1 bit as CLR Does. - return *((int*)dataPointer) == 1; - } - else if (dataType == typeof(Guid)) - { - return *((Guid*)dataPointer); - } - else if (dataType == typeof(char)) - { - return *((char*)dataPointer); - } - else if (dataType == typeof(DateTime)) - { - long dateTimeTicks = *((long*)dataPointer); - return DateTime.FromFileTimeUtc(dateTimeTicks); - } - else if (dataType == typeof(byte[])) - { - // byte[] are written to EventData* as an int followed by a blob - int cbSize = *((int*)dataPointer); - byte[] blob = new byte[cbSize]; - dataPointer = data->DataPointer; - data++; - for (int i = 0; i < cbSize; ++i) - blob[i] = *((byte*)(dataPointer + i)); - return blob; - } - else if (dataType == typeof(byte*)) - { - // TODO: how do we want to handle this? For now we ignore it... - return null; - } - else - { - if (m_EventSourcePreventRecursion && m_EventSourceInDecodeObject) - { - return null; - } - - try - { - m_EventSourceInDecodeObject = true; - - if (dataType.IsEnum()) - { - dataType = Enum.GetUnderlyingType(dataType); -#if ES_BUILD_PN - int dataTypeSize = (int)dataType.TypeHandle.ToEETypePtr().ValueTypeSize; -#else - int dataTypeSize = System.Runtime.InteropServices.Marshal.SizeOf(dataType); -#endif - if (dataTypeSize < sizeof(int)) - dataType = typeof(int); - goto Again; - } - - // Everything else is marshaled as a string. - // ETW strings are NULL-terminated, so marshal everything up to the first - // null in the string. - if (dataPointer == IntPtr.Zero) - { - return null; - } - - return new string((char*)dataPointer); - } - finally - { - m_EventSourceInDecodeObject = false; - } - } - } - - // Finds the Dispatcher (which holds the filtering state), for a given dispatcher for the current - // eventSource). - private EventDispatcher? GetDispatcher(EventListener? listener) - { - EventDispatcher? dispatcher = m_Dispatchers; - while (dispatcher != null) - { - if (dispatcher.m_Listener == listener) - return dispatcher; - dispatcher = dispatcher.m_Next; - } - return dispatcher; - } - - private unsafe void WriteEventVarargs(int eventId, Guid* childActivityID, object?[] args) - { - if (m_eventSourceEnabled) - { - Debug.Assert(m_eventData != null); // You must have initialized this if you enabled the source. - try - { - if (childActivityID != null) - { - ValidateEventOpcodeForTransfer(ref m_eventData[eventId], m_eventData[eventId].Name); - - // If you use WriteEventWithRelatedActivityID you MUST declare the first argument to be a GUID - // with the name 'relatedActivityID, and NOT pass this argument to the WriteEvent method. - // During manifest creation we modify the ParameterInfo[] that we store to strip out any - // first parameter that is of type Guid and named "relatedActivityId." Thus, if you call - // WriteEventWithRelatedActivityID from a method that doesn't name its first parameter correctly - // we can end up in a state where the ParameterInfo[] doesn't have its first parameter stripped, - // and this leads to a mismatch between the number of arguments and the number of ParameterInfos, - // which would cause a cryptic IndexOutOfRangeException later if we don't catch it here. - if (!m_eventData[eventId].HasRelatedActivityID) - { - throw new ArgumentException(SR.EventSource_NoRelatedActivityId); - } - } - - LogEventArgsMismatches(eventId, args); - - Guid* pActivityId = null; - Guid activityId = Guid.Empty; - Guid relatedActivityId = Guid.Empty; - EventOpcode opcode = (EventOpcode)m_eventData[eventId].Descriptor.Opcode; - EventActivityOptions activityOptions = m_eventData[eventId].ActivityOptions; - - if (childActivityID == null && - ((activityOptions & EventActivityOptions.Disable) == 0)) - { - if (opcode == EventOpcode.Start) - { - m_activityTracker.OnStart(m_name, m_eventData[eventId].Name, m_eventData[eventId].Descriptor.Task, ref activityId, ref relatedActivityId, m_eventData[eventId].ActivityOptions); - } - else if (opcode == EventOpcode.Stop) - { - m_activityTracker.OnStop(m_name, m_eventData[eventId].Name, m_eventData[eventId].Descriptor.Task, ref activityId); - } - - if (activityId != Guid.Empty) - pActivityId = &activityId; - if (relatedActivityId != Guid.Empty) - childActivityID = &relatedActivityId; - } - -#if FEATURE_MANAGED_ETW - if (m_eventData[eventId].EnabledForETW -#if FEATURE_PERFTRACING - || m_eventData[eventId].EnabledForEventPipe -#endif // FEATURE_PERFTRACING - ) - { - if (!SelfDescribingEvents) - { - if (!m_etwProvider.WriteEvent(ref m_eventData[eventId].Descriptor, m_eventData[eventId].EventHandle, pActivityId, childActivityID, args)) - ThrowEventSourceException(m_eventData[eventId].Name); -#if FEATURE_PERFTRACING - if (!m_eventPipeProvider.WriteEvent(ref m_eventData[eventId].Descriptor, m_eventData[eventId].EventHandle, pActivityId, childActivityID, args)) - ThrowEventSourceException(m_eventData[eventId].Name); -#endif // FEATURE_PERFTRACING - } - else - { - TraceLoggingEventTypes? tlet = m_eventData[eventId].TraceLoggingEventTypes; - if (tlet == null) - { - tlet = new TraceLoggingEventTypes(m_eventData[eventId].Name, - EventTags.None, - m_eventData[eventId].Parameters); - Interlocked.CompareExchange(ref m_eventData[eventId].TraceLoggingEventTypes, tlet, null); - } - // TODO: activity ID support - EventSourceOptions opt = new EventSourceOptions - { - Keywords = (EventKeywords)m_eventData[eventId].Descriptor.Keywords, - Level = (EventLevel)m_eventData[eventId].Descriptor.Level, - Opcode = (EventOpcode)m_eventData[eventId].Descriptor.Opcode - }; - - WriteMultiMerge(m_eventData[eventId].Name, ref opt, tlet, pActivityId, childActivityID, args); - } - } -#endif // FEATURE_MANAGED_ETW - if (m_Dispatchers != null && m_eventData[eventId].EnabledForAnyListener) - { -#if !ES_BUILD_STANDALONE - // Maintain old behavior - object identity is preserved - if (LocalAppContextSwitches.PreserveEventListnerObjectIdentity) - { - WriteToAllListeners( - eventId: eventId, - osThreadId: null, - timeStamp: null, - activityID: pActivityId, - childActivityID: childActivityID, - args: args); - } - else -#endif // !ES_BUILD_STANDALONE - { - object?[] serializedArgs = SerializeEventArgs(eventId, args); - WriteToAllListeners( - eventId: eventId, - osThreadId: null, - timeStamp: null, - activityID: pActivityId, - childActivityID: childActivityID, - args: serializedArgs); - } - } - } - catch (Exception ex) - { - if (ex is EventSourceException) - throw; - else - ThrowEventSourceException(m_eventData[eventId].Name, ex); - } - } - } - - private unsafe object?[] SerializeEventArgs(int eventId, object?[] args) - { - Debug.Assert(m_eventData != null); - TraceLoggingEventTypes? eventTypes = m_eventData[eventId].TraceLoggingEventTypes; - if (eventTypes == null) - { - eventTypes = new TraceLoggingEventTypes(m_eventData[eventId].Name, - EventTags.None, - m_eventData[eventId].Parameters); - Interlocked.CompareExchange(ref m_eventData[eventId].TraceLoggingEventTypes, eventTypes, null); - } - int paramCount = Math.Min(eventTypes.typeInfos.Length, args.Length); // parameter count mismatch get logged in LogEventArgsMismatches - var eventData = new object?[eventTypes.typeInfos.Length]; - for (int i = 0; i < paramCount; i++) - { - eventData[i] = eventTypes.typeInfos[i].GetData(args[i]); - } - return eventData; - } - - /// <summary> - /// We expect that the arguments to the Event method and the arguments to WriteEvent match. This function - /// checks that they in fact match and logs a warning to the debugger if they don't. - /// </summary> - /// <param name="eventId"></param> - /// <param name="args"></param> - private void LogEventArgsMismatches(int eventId, object?[] args) - { - Debug.Assert(m_eventData != null); - ParameterInfo[] infos = m_eventData[eventId].Parameters; - - if (args.Length != infos.Length) - { - ReportOutOfBandMessage(SR.Format(SR.EventSource_EventParametersMismatch, eventId, args.Length, infos.Length)); - return; - } - - int i = 0; - while (i < args.Length) - { - Type pType = infos[i].ParameterType; - object? arg = args[i]; - - // Checking to see if the Parameter types (from the Event method) match the supplied argument types. - // Fail if one of two things hold : either the argument type is not equal or assignable to the parameter type, or the - // argument is null and the parameter type is a non-Nullable<T> value type. - if ((arg != null && !pType.IsAssignableFrom(arg.GetType())) - || (arg == null && (pType.IsValueType && !(pType.IsGenericType && pType.GetGenericTypeDefinition() == typeof(Nullable<>)))) - ) - { - ReportOutOfBandMessage(SR.Format(SR.EventSource_VarArgsParameterMismatch, eventId, infos[i].Name)); - return; - } - - ++i; - } - } - - private unsafe void WriteToAllListeners(int eventId, Guid* activityID, Guid* childActivityID, int eventDataCount, EventSource.EventData* data) - { - Debug.Assert(m_eventData != null); - // We represent a byte[] as a integer denoting the length and then a blob of bytes in the data pointer. This causes a spurious - // warning because eventDataCount is off by one for the byte[] case since a byte[] has 2 items associated it. So we want to check - // that the number of parameters is correct against the byte[] case, but also we the args array would be one too long if - // we just used the modifiedParamCount here -- so we need both. - int paramCount = GetParameterCount(m_eventData[eventId]); - int modifiedParamCount = 0; - for (int i = 0; i < paramCount; i++) - { - Type parameterType = GetDataType(m_eventData[eventId], i); - if (parameterType == typeof(byte[])) - { - modifiedParamCount += 2; - } - else - { - modifiedParamCount++; - } - } - if (eventDataCount != modifiedParamCount) - { - ReportOutOfBandMessage(SR.Format(SR.EventSource_EventParametersMismatch, eventId, eventDataCount, paramCount)); - paramCount = Math.Min(paramCount, eventDataCount); - } - - object?[] args = new object[paramCount]; - - EventSource.EventData* dataPtr = data; - for (int i = 0; i < paramCount; i++) - args[i] = DecodeObject(eventId, i, ref dataPtr); - WriteToAllListeners( - eventId: eventId, - osThreadId: null, - timeStamp: null, - activityID: activityID, - childActivityID: childActivityID, - args: args); - } - - // helper for writing to all EventListeners attached the current eventSource. - internal unsafe void WriteToAllListeners(int eventId, uint* osThreadId, DateTime* timeStamp, Guid* activityID, Guid* childActivityID, params object?[] args) - { - EventWrittenEventArgs eventCallbackArgs = new EventWrittenEventArgs(this); - eventCallbackArgs.EventId = eventId; - if (osThreadId != null) - eventCallbackArgs.OSThreadId = (int)*osThreadId; - if (timeStamp != null) - eventCallbackArgs.TimeStamp = *timeStamp; - if (activityID != null) - eventCallbackArgs.ActivityId = *activityID; - if (childActivityID != null) - eventCallbackArgs.RelatedActivityId = *childActivityID; - - Debug.Assert(m_eventData != null); - eventCallbackArgs.EventName = m_eventData[eventId].Name; - eventCallbackArgs.Message = m_eventData[eventId].Message; - eventCallbackArgs.Payload = new ReadOnlyCollection<object?>(args); - - DispatchToAllListeners(eventId, eventCallbackArgs); - } - - private unsafe void DispatchToAllListeners(int eventId, EventWrittenEventArgs eventCallbackArgs) - { - Exception? lastThrownException = null; - for (EventDispatcher? dispatcher = m_Dispatchers; dispatcher != null; dispatcher = dispatcher.m_Next) - { - Debug.Assert(dispatcher.m_EventEnabled != null); - if (eventId == -1 || dispatcher.m_EventEnabled[eventId]) - { - { - try - { - dispatcher.m_Listener.OnEventWritten(eventCallbackArgs); - } - catch (Exception e) - { - ReportOutOfBandMessage("ERROR: Exception during EventSource.OnEventWritten: " - + e.Message); - lastThrownException = e; - } - } - } - } - - if (lastThrownException != null) - { - throw new EventSourceException(lastThrownException); - } - } - - private unsafe void WriteEventString(EventLevel level, long keywords, string msgString) - { -#if FEATURE_MANAGED_ETW - if (m_etwProvider != null) - { - const string EventName = "EventSourceMessage"; - if (SelfDescribingEvents) - { - EventSourceOptions opt = new EventSourceOptions - { - Keywords = (EventKeywords)unchecked(keywords), - Level = level - }; - var msg = new { message = msgString }; - var tlet = new TraceLoggingEventTypes(EventName, EventTags.None, new Type[] { msg.GetType() }); - WriteMultiMergeInner(EventName, ref opt, tlet, null, null, msg); - } - else - { - // We want the name of the provider to show up so if we don't have a manifest we create - // on that at least has the provider name (I don't define any events). - if (m_rawManifest == null && m_outOfBandMessageCount == 1) - { - ManifestBuilder manifestBuilder = new ManifestBuilder(Name, Guid, Name, null, EventManifestOptions.None); - manifestBuilder.StartEvent(EventName, new EventAttribute(0) { Level = EventLevel.LogAlways, Task = (EventTask)0xFFFE }); - manifestBuilder.AddEventParameter(typeof(string), "message"); - manifestBuilder.EndEvent(); - SendManifest(manifestBuilder.CreateManifest()); - } - - // We use this low level routine to bypass the enabled checking, since the eventSource itself is only partially inited. - fixed (char* msgStringPtr = msgString) - { - EventDescriptor descr = new EventDescriptor(0, 0, 0, (byte)level, 0, 0, keywords); - EventProvider.EventData data = default; - data.Ptr = (ulong)msgStringPtr; - data.Size = (uint)(2 * (msgString.Length + 1)); - data.Reserved = 0; - m_etwProvider.WriteEvent(ref descr, IntPtr.Zero, null, null, 1, (IntPtr)((void*)&data)); - } - } - } -#endif // FEATURE_MANAGED_ETW - } - - /// <summary> - /// Since this is a means of reporting errors (see ReportoutOfBandMessage) any failure encountered - /// while writing the message to any one of the listeners will be silently ignored. - /// </summary> - private void WriteStringToAllListeners(string eventName, string msg) - { - EventWrittenEventArgs eventCallbackArgs = new EventWrittenEventArgs(this); - eventCallbackArgs.EventId = 0; - eventCallbackArgs.Message = msg; - eventCallbackArgs.Payload = new ReadOnlyCollection<object?>(new List<object?>() { msg }); - eventCallbackArgs.PayloadNames = new ReadOnlyCollection<string>(new List<string> { "message" }); - eventCallbackArgs.EventName = eventName; - - for (EventDispatcher? dispatcher = m_Dispatchers; dispatcher != null; dispatcher = dispatcher.m_Next) - { - bool dispatcherEnabled = false; - if (dispatcher.m_EventEnabled == null) - { - // if the listeners that weren't correctly initialized, we will send to it - // since this is an error message and we want to see it go out. - dispatcherEnabled = true; - } - else - { - // if there's *any* enabled event on the dispatcher we'll write out the string - // otherwise we'll treat the listener as disabled and skip it - for (int evtId = 0; evtId < dispatcher.m_EventEnabled.Length; ++evtId) - { - if (dispatcher.m_EventEnabled[evtId]) - { - dispatcherEnabled = true; - break; - } - } - } - try - { - if (dispatcherEnabled) - dispatcher.m_Listener.OnEventWritten(eventCallbackArgs); - } - catch - { - // ignore any exceptions thrown by listeners' OnEventWritten - } - } - } - - /// <summary> - /// Returns true if 'eventNum' is enabled if you only consider the level and matchAnyKeyword filters. - /// It is possible that eventSources turn off the event based on additional filtering criteria. - /// </summary> - private bool IsEnabledByDefault(int eventNum, bool enable, EventLevel currentLevel, EventKeywords currentMatchAnyKeyword) - { - if (!enable) - return false; - - Debug.Assert(m_eventData != null); - EventLevel eventLevel = (EventLevel)m_eventData[eventNum].Descriptor.Level; - EventKeywords eventKeywords = unchecked((EventKeywords)((ulong)m_eventData[eventNum].Descriptor.Keywords & (~(SessionMask.All.ToEventKeywords())))); - -#if FEATURE_MANAGED_ETW_CHANNELS - EventChannel channel = unchecked((EventChannel)m_eventData[eventNum].Descriptor.Channel); -#else - EventChannel channel = EventChannel.None; -#endif - - return IsEnabledCommon(enable, currentLevel, currentMatchAnyKeyword, eventLevel, eventKeywords, channel); - } - - private bool IsEnabledCommon(bool enabled, EventLevel currentLevel, EventKeywords currentMatchAnyKeyword, - EventLevel eventLevel, EventKeywords eventKeywords, EventChannel eventChannel) - { - if (!enabled) - return false; - - // does is pass the level test? - if ((currentLevel != 0) && (currentLevel < eventLevel)) - return false; - - // if yes, does it pass the keywords test? - if (currentMatchAnyKeyword != 0 && eventKeywords != 0) - { -#if FEATURE_MANAGED_ETW_CHANNELS - // is there a channel with keywords that match currentMatchAnyKeyword? - if (eventChannel != EventChannel.None && this.m_channelData != null && this.m_channelData.Length > (int)eventChannel) - { - EventKeywords channel_keywords = unchecked((EventKeywords)(m_channelData[(int)eventChannel] | (ulong)eventKeywords)); - if (channel_keywords != 0 && (channel_keywords & currentMatchAnyKeyword) == 0) - return false; - } - else -#endif - { - if ((unchecked((ulong)eventKeywords & (ulong)currentMatchAnyKeyword)) == 0) - return false; - } - } - return true; - } - [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.NoInlining)] - private void ThrowEventSourceException(string? eventName, Exception? innerEx = null) - { - // If we fail during out of band logging we may end up trying - // to throw another EventSourceException, thus hitting a StackOverflowException. - // Avoid StackOverflow by making sure we do not recursively call this method. - if (m_EventSourceExceptionRecurenceCount > 0) - return; - try - { - m_EventSourceExceptionRecurenceCount++; - - string errorPrefix = "EventSourceException"; - if (eventName != null) - { - errorPrefix += " while processing event \"" + eventName + "\""; - } - - // TODO Create variations of EventSourceException that indicate more information using the error code. - switch (EventProvider.GetLastWriteEventError()) - { - case EventProvider.WriteEventErrorCode.EventTooBig: - ReportOutOfBandMessage(errorPrefix + ": " + SR.EventSource_EventTooBig); - if (ThrowOnEventWriteErrors) throw new EventSourceException(SR.EventSource_EventTooBig, innerEx); - break; - case EventProvider.WriteEventErrorCode.NoFreeBuffers: - ReportOutOfBandMessage(errorPrefix + ": " + SR.EventSource_NoFreeBuffers); - if (ThrowOnEventWriteErrors) throw new EventSourceException(SR.EventSource_NoFreeBuffers, innerEx); - break; - case EventProvider.WriteEventErrorCode.NullInput: - ReportOutOfBandMessage(errorPrefix + ": " + SR.EventSource_NullInput); - if (ThrowOnEventWriteErrors) throw new EventSourceException(SR.EventSource_NullInput, innerEx); - break; - case EventProvider.WriteEventErrorCode.TooManyArgs: - ReportOutOfBandMessage(errorPrefix + ": " + SR.EventSource_TooManyArgs); - if (ThrowOnEventWriteErrors) throw new EventSourceException(SR.EventSource_TooManyArgs, innerEx); - break; - default: - if (innerEx != null) - { - innerEx = innerEx.GetBaseException(); - ReportOutOfBandMessage(errorPrefix + ": " + innerEx.GetType() + ":" + innerEx.Message); - } - else - ReportOutOfBandMessage(errorPrefix); - if (ThrowOnEventWriteErrors) throw new EventSourceException(innerEx); - break; - } - } - finally - { - m_EventSourceExceptionRecurenceCount--; - } - } - - private void ValidateEventOpcodeForTransfer(ref EventMetadata eventData, string? eventName) - { - if ((EventOpcode)eventData.Descriptor.Opcode != EventOpcode.Send && - (EventOpcode)eventData.Descriptor.Opcode != EventOpcode.Receive && - (EventOpcode)eventData.Descriptor.Opcode != EventOpcode.Start) - { - ThrowEventSourceException(eventName); - } - } - - internal static EventOpcode GetOpcodeWithDefault(EventOpcode opcode, string? eventName) - { - if (opcode == EventOpcode.Info && eventName != null) - { - if (eventName.EndsWith(s_ActivityStartSuffix, StringComparison.Ordinal)) - { - return EventOpcode.Start; - } - else if (eventName.EndsWith(s_ActivityStopSuffix, StringComparison.Ordinal)) - { - return EventOpcode.Stop; - } - } - - return opcode; - } - -#if FEATURE_MANAGED_ETW - /// <summary> - /// This class lets us hook the 'OnEventCommand' from the eventSource. - /// </summary> - private class OverideEventProvider : EventProvider - { - public OverideEventProvider(EventSource eventSource, EventProviderType providerType) - : base(providerType) - { - this.m_eventSource = eventSource; - this.m_eventProviderType = providerType; - } - protected override void OnControllerCommand(ControllerCommand command, IDictionary<string, string?>? arguments, - int perEventSourceSessionId, int etwSessionId) - { - // We use null to represent the ETW EventListener. - EventListener? listener = null; - m_eventSource.SendCommand(listener, m_eventProviderType, perEventSourceSessionId, etwSessionId, - (EventCommand)command, IsEnabled(), Level, MatchAnyKeyword, arguments); - } - private readonly EventSource m_eventSource; - private readonly EventProviderType m_eventProviderType; - } -#endif - - /// <summary> - /// Used to hold all the static information about an event. This includes everything in the event - /// descriptor as well as some stuff we added specifically for EventSource. see the - /// code:m_eventData for where we use this. - /// </summary> - - /* - EventMetadata was public in the separate System.Diagnostics.Tracing assembly(pre NS2.0), - now the move to CoreLib marked them as private. - While they are technically private (it's a contract used between the library and the ILC toolchain), - we need them to be rooted and exported from shared library for the system to work. - For now I'm simply marking them as public again.A cleaner solution might be to use.rd.xml to - root them and modify shared library definition to force export them. - */ -#if ES_BUILD_PN - public -#else - internal -#endif - partial struct EventMetadata - { -#if ES_BUILD_PN - public EventMetadata(EventDescriptor descriptor, - EventTags tags, - bool enabledForAnyListener, - bool enabledForETW, - string name, - string message, - EventParameterType[] parameterTypes) - { - this.Descriptor = descriptor; - this.Tags = tags; - this.EnabledForAnyListener = enabledForAnyListener; - this.EnabledForETW = enabledForETW; -#if FEATURE_PERFTRACING - this.EnabledForEventPipe = false; -#endif - this.TriggersActivityTracking = 0; - this.Name = name; - this.Message = message; - this.Parameters = null!; - this.TraceLoggingEventTypes = null; - this.ActivityOptions = EventActivityOptions.None; - this.ParameterTypes = parameterTypes; - this.HasRelatedActivityID = false; - this.EventHandle = IntPtr.Zero; - } -#endif - - public EventDescriptor Descriptor; - public IntPtr EventHandle; // EventPipeEvent handle. - public EventTags Tags; - public bool EnabledForAnyListener; // true if any dispatcher has this event turned on - public bool EnabledForETW; // is this event on for ETW? -#if FEATURE_PERFTRACING - public bool EnabledForEventPipe; // is this event on for EventPipe? -#endif - - public bool HasRelatedActivityID; // Set if the event method's first parameter is a Guid named 'relatedActivityId' -#pragma warning disable 0649 - public byte TriggersActivityTracking; // count of listeners that marked this event as trigger for start of activity logging. -#pragma warning restore 0649 - public string Name; // the name of the event - public string? Message; // If the event has a message associated with it, this is it. - public ParameterInfo[] Parameters; // TODO can we remove? - - public TraceLoggingEventTypes? TraceLoggingEventTypes; - public EventActivityOptions ActivityOptions; - -#if ES_BUILD_PN - public EventParameterType[] ParameterTypes; -#endif - } - -#if !ES_BUILD_PN - private static int GetParameterCount(EventMetadata eventData) - { - return eventData.Parameters.Length; - } - - private static Type GetDataType(EventMetadata eventData, int parameterId) - { - return eventData.Parameters[parameterId].ParameterType; - } - - private const bool m_EventSourcePreventRecursion = false; -#else - private static int GetParameterCount(EventMetadata eventData) - { - int paramCount; - if (eventData.Parameters == null) - { - paramCount = eventData.ParameterTypes.Length; - } - else - { - paramCount = eventData.Parameters.Length; - } - - return paramCount; - } - - private static Type GetDataType(EventMetadata eventData, int parameterId) - { - Type dataType; - if (eventData.Parameters == null) - { - dataType = EventTypeToType(eventData.ParameterTypes[parameterId]); - } - else - { - dataType = eventData.Parameters[parameterId].ParameterType; - } - - return dataType; - } - - private static readonly bool m_EventSourcePreventRecursion = true; - - public enum EventParameterType - { - Boolean, - Byte, - SByte, - Char, - Int16, - UInt16, - Int32, - UInt32, - Int64, - UInt64, - IntPtr, - Single, - Double, - Decimal, - Guid, - String - } - - private static Type EventTypeToType(EventParameterType type) - { - switch (type) - { - case EventParameterType.Boolean: - return typeof(bool); - case EventParameterType.Byte: - return typeof(byte); - case EventParameterType.SByte: - return typeof(sbyte); - case EventParameterType.Char: - return typeof(char); - case EventParameterType.Int16: - return typeof(short); - case EventParameterType.UInt16: - return typeof(ushort); - case EventParameterType.Int32: - return typeof(int); - case EventParameterType.UInt32: - return typeof(uint); - case EventParameterType.Int64: - return typeof(long); - case EventParameterType.UInt64: - return typeof(ulong); - case EventParameterType.IntPtr: - return typeof(IntPtr); - case EventParameterType.Single: - return typeof(float); - case EventParameterType.Double: - return typeof(double); - case EventParameterType.Decimal: - return typeof(decimal); - case EventParameterType.Guid: - return typeof(Guid); - case EventParameterType.String: - return typeof(string); - default: - // TODO: should I throw an exception here? - return null!; - } - } -#endif - - // This is the internal entry point that code:EventListeners call when wanting to send a command to a - // eventSource. The logic is as follows - // - // * if Command == Update - // * perEventSourceSessionId specifies the per-provider ETW session ID that the command applies - // to (if listener != null) - // perEventSourceSessionId = 0 - reserved for EventListeners - // perEventSourceSessionId = 1..SessionMask.MAX - reserved for activity tracing aware ETW sessions - // perEventSourceSessionId-1 represents the bit in the reserved field (bits 44..47) in - // Keywords that identifies the session - // perEventSourceSessionId = SessionMask.MAX+1 - reserved for legacy ETW sessions; these are - // discriminated by etwSessionId - // * etwSessionId specifies a machine-wide ETW session ID; this allows correlation of - // activity tracing across different providers (which might have different sessionIds - // for the same ETW session) - // * enable, level, matchAnyKeywords are used to set a default for all events for the - // eventSource. In particular, if 'enabled' is false, 'level' and - // 'matchAnyKeywords' are not used. - // * OnEventCommand is invoked, which may cause calls to - // code:EventSource.EnableEventForDispatcher which may cause changes in the filtering - // depending on the logic in that routine. - // * else (command != Update) - // * Simply call OnEventCommand. The expectation is that filtering is NOT changed. - // * The 'enabled' 'level', matchAnyKeyword' arguments are ignored (must be true, 0, 0). - // - // dispatcher == null has special meaning. It is the 'ETW' dispatcher. - internal void SendCommand(EventListener? listener, EventProviderType eventProviderType, int perEventSourceSessionId, int etwSessionId, - EventCommand command, bool enable, - EventLevel level, EventKeywords matchAnyKeyword, - IDictionary<string, string?>? commandArguments) - { - var commandArgs = new EventCommandEventArgs(command, commandArguments, this, listener, eventProviderType, perEventSourceSessionId, etwSessionId, enable, level, matchAnyKeyword); - lock (EventListener.EventListenersLock) - { - if (m_completelyInited) - { - // After the first command arrive after construction, we are ready to get rid of the deferred commands - this.m_deferredCommands = null; - // We are fully initialized, do the command - DoCommand(commandArgs); - } - else - { - // We can't do the command, simply remember it and we do it when we are fully constructed. - if (m_deferredCommands == null) - { - m_deferredCommands = commandArgs; // create the first entry - } - else - { - // We have one or more entries, find the last one and add it to that. - EventCommandEventArgs lastCommand = m_deferredCommands; - while (lastCommand.nextCommand != null) - lastCommand = lastCommand.nextCommand; - lastCommand.nextCommand = commandArgs; - } - } - } - } - - /// <summary> - /// We want the eventSource to be fully initialized when we do commands because that way we can send - /// error messages and other logging directly to the event stream. Unfortunately we can get callbacks - /// when we are not fully initialized. In that case we store them in 'commandArgs' and do them later. - /// This helper actually does all actual command logic. - /// </summary> - internal void DoCommand(EventCommandEventArgs commandArgs) - { - // PRECONDITION: We should be holding the EventListener.EventListenersLock - // We defer commands until we are completely inited. This allows error messages to be sent. - Debug.Assert(m_completelyInited); - -#if FEATURE_MANAGED_ETW - if (m_etwProvider == null) // If we failed to construct - return; -#endif // FEATURE_MANAGED_ETW -#if FEATURE_PERFTRACING - if (m_eventPipeProvider == null) - return; -#endif - - m_outOfBandMessageCount = 0; - try - { - EnsureDescriptorsInitialized(); - Debug.Assert(m_eventData != null); - - // Find the per-EventSource dispatcher corresponding to registered dispatcher - commandArgs.dispatcher = GetDispatcher(commandArgs.listener); - if (commandArgs.dispatcher == null && commandArgs.listener != null) // dispatcher == null means ETW dispatcher - { - throw new ArgumentException(SR.EventSource_ListenerNotFound); - } - - commandArgs.Arguments ??= new Dictionary<string, string?>(); - - if (commandArgs.Command == EventCommand.Update) - { - // Set it up using the 'standard' filtering bitfields (use the "global" enable, not session specific one) - for (int i = 0; i < m_eventData.Length; i++) - EnableEventForDispatcher(commandArgs.dispatcher, commandArgs.eventProviderType, i, IsEnabledByDefault(i, commandArgs.enable, commandArgs.level, commandArgs.matchAnyKeyword)); - - if (commandArgs.enable) - { - if (!m_eventSourceEnabled) - { - // EventSource turned on for the first time, simply copy the bits. - m_level = commandArgs.level; - m_matchAnyKeyword = commandArgs.matchAnyKeyword; - } - else - { - // Already enabled, make it the most verbose of the existing and new filter - if (commandArgs.level > m_level) - m_level = commandArgs.level; - if (commandArgs.matchAnyKeyword == 0) - m_matchAnyKeyword = 0; - else if (m_matchAnyKeyword != 0) - m_matchAnyKeyword = unchecked(m_matchAnyKeyword | commandArgs.matchAnyKeyword); - } - } - - // interpret perEventSourceSessionId's sign, and adjust perEventSourceSessionId to - // represent 0-based positive values - bool bSessionEnable = (commandArgs.perEventSourceSessionId >= 0); - if (commandArgs.perEventSourceSessionId == 0 && !commandArgs.enable) - bSessionEnable = false; - - if (commandArgs.listener == null) - { - if (!bSessionEnable) - commandArgs.perEventSourceSessionId = -commandArgs.perEventSourceSessionId; - // for "global" enable/disable (passed in with listener == null and - // perEventSourceSessionId == 0) perEventSourceSessionId becomes -1 - --commandArgs.perEventSourceSessionId; - } - - commandArgs.Command = bSessionEnable ? EventCommand.Enable : EventCommand.Disable; - - // perEventSourceSessionId = -1 when ETW sent a notification, but the set of active sessions - // hasn't changed. - // sesisonId = SessionMask.MAX when one of the legacy ETW sessions changed - // 0 <= perEventSourceSessionId < SessionMask.MAX for activity-tracing aware sessions - Debug.Assert(commandArgs.perEventSourceSessionId >= -1 && commandArgs.perEventSourceSessionId <= SessionMask.MAX); - - // Send the manifest if we are enabling an ETW session - if (bSessionEnable && commandArgs.dispatcher == null) - { - // eventSourceDispatcher == null means this is the ETW manifest - - // Note that we unconditionally send the manifest whenever we are enabled, even if - // we were already enabled. This is because there may be multiple sessions active - // and we can't know that all the sessions have seen the manifest. - if (!SelfDescribingEvents) - SendManifest(m_rawManifest); - } - - // Turn on the enable bit before making the OnEventCommand callback This allows you to do useful - // things like log messages, or test if keywords are enabled in the callback. - if (commandArgs.enable) - { - Debug.Assert(m_eventData != null); - m_eventSourceEnabled = true; - } - - this.OnEventCommand(commandArgs); - this.m_eventCommandExecuted?.Invoke(this, commandArgs); - - if (!commandArgs.enable) - { - // If we are disabling, maybe we can turn on 'quick checks' to filter - // quickly. These are all just optimizations (since later checks will still filter) - - // There is a good chance EnabledForAnyListener are not as accurate as - // they could be, go ahead and get a better estimate. - for (int i = 0; i < m_eventData.Length; i++) - { - bool isEnabledForAnyListener = false; - for (EventDispatcher? dispatcher = m_Dispatchers; dispatcher != null; dispatcher = dispatcher.m_Next) - { - Debug.Assert(dispatcher.m_EventEnabled != null); - - if (dispatcher.m_EventEnabled[i]) - { - isEnabledForAnyListener = true; - break; - } - } - m_eventData[i].EnabledForAnyListener = isEnabledForAnyListener; - } - - // If no events are enabled, disable the global enabled bit. - if (!AnyEventEnabled()) - { - m_level = 0; - m_matchAnyKeyword = 0; - m_eventSourceEnabled = false; - } - } - } - else - { -#if !FEATURE_PERFTRACING - if (commandArgs.Command == EventCommand.SendManifest) - { - // TODO: should we generate the manifest here if we hadn't already? - if (m_rawManifest != null) - SendManifest(m_rawManifest); - } -#endif - - // These are not used for non-update commands and thus should always be 'default' values - // Debug.Assert(enable == true); - // Debug.Assert(level == EventLevel.LogAlways); - // Debug.Assert(matchAnyKeyword == EventKeywords.None); - - this.OnEventCommand(commandArgs); - m_eventCommandExecuted?.Invoke(this, commandArgs); - } - } - catch (Exception e) - { - // When the ETW session is created after the EventSource has registered with the ETW system - // we can send any error messages here. - ReportOutOfBandMessage("ERROR: Exception in Command Processing for EventSource " + Name + ": " + e.Message); - // We never throw when doing a command. - } - } - - /// <summary> - /// If 'value is 'true' then set the eventSource so that 'dispatcher' will receive event with the eventId - /// of 'eventId. If value is 'false' disable the event for that dispatcher. If 'eventId' is out of - /// range return false, otherwise true. - /// </summary> - internal bool EnableEventForDispatcher(EventDispatcher? dispatcher, EventProviderType eventProviderType, int eventId, bool value) - { - Debug.Assert(m_eventData != null); - - if (dispatcher == null) - { - if (eventId >= m_eventData.Length) - return false; -#if FEATURE_MANAGED_ETW - if (m_etwProvider != null && eventProviderType == EventProviderType.ETW) - m_eventData[eventId].EnabledForETW = value; -#endif -#if FEATURE_PERFTRACING - if (m_eventPipeProvider != null && eventProviderType == EventProviderType.EventPipe) - m_eventData[eventId].EnabledForEventPipe = value; -#endif - } - else - { - Debug.Assert(dispatcher.m_EventEnabled != null); - if (eventId >= dispatcher.m_EventEnabled.Length) - return false; - dispatcher.m_EventEnabled[eventId] = value; - if (value) - m_eventData[eventId].EnabledForAnyListener = true; - } - return true; - } - - /// <summary> - /// Returns true if any event at all is on. - /// </summary> - private bool AnyEventEnabled() - { - Debug.Assert(m_eventData != null); - - for (int i = 0; i < m_eventData.Length; i++) - if (m_eventData[i].EnabledForETW || m_eventData[i].EnabledForAnyListener -#if FEATURE_PERFTRACING - || m_eventData[i].EnabledForEventPipe -#endif // FEATURE_PERFTRACING - ) - return true; - return false; - } - - private bool IsDisposed => m_eventSourceDisposed; - - private void EnsureDescriptorsInitialized() - { -#if !ES_BUILD_STANDALONE - Debug.Assert(Monitor.IsEntered(EventListener.EventListenersLock)); -#endif - if (m_eventData == null) - { - Guid eventSourceGuid = Guid.Empty; - string? eventSourceName = null; - EventMetadata[]? eventData = null; - byte[]? manifest = null; - - // Try the GetMetadata provided by the ILTransform in ProjectN. The default sets all to null, and in that case we fall back - // to the reflection approach. - GetMetadata(out eventSourceGuid, out eventSourceName, out eventData, out manifest); - - if (eventSourceGuid.Equals(Guid.Empty) || eventSourceName == null || eventData == null || manifest == null) - { - // GetMetadata failed, so we have to set it via reflection. - Debug.Assert(m_rawManifest == null); - - m_rawManifest = CreateManifestAndDescriptors(this.GetType(), Name, this); - Debug.Assert(m_eventData != null); - } - else - { - // GetMetadata worked, so set the fields as appropriate. - m_name = eventSourceName; - m_guid = eventSourceGuid; - m_eventData = eventData; - m_rawManifest = manifest; - } - // TODO Enforce singleton pattern - Debug.Assert(EventListener.s_EventSources != null, "should be called within lock on EventListener.EventListenersLock which ensures s_EventSources to be initialized"); - foreach (WeakReference<EventSource> eventSourceRef in EventListener.s_EventSources) - { - if (eventSourceRef.TryGetTarget(out EventSource? eventSource) && eventSource.Guid == m_guid && !eventSource.IsDisposed) - { - if (eventSource != this) - { - throw new ArgumentException(SR.Format(SR.EventSource_EventSourceGuidInUse, m_guid)); - } - } - } - - // Make certain all dispatchers also have their arrays initialized - EventDispatcher? dispatcher = m_Dispatchers; - while (dispatcher != null) - { - dispatcher.m_EventEnabled ??= new bool[m_eventData.Length]; - dispatcher = dispatcher.m_Next; - } -#if FEATURE_PERFTRACING - // Initialize the EventPipe event handles. - DefineEventPipeEvents(); -#endif - } - if (s_currentPid == 0) - { -#if ES_BUILD_STANDALONE - // for non-BCL EventSource we must assert SecurityPermission - new SecurityPermission(PermissionState.Unrestricted).Assert(); -#endif - s_currentPid = Interop.GetCurrentProcessId(); - } - } - - // Send out the ETW manifest XML out to ETW - // Today, we only send the manifest to ETW, custom listeners don't get it. - private unsafe void SendManifest(byte[]? rawManifest) - { - if (rawManifest == null) - return; - - Debug.Assert(!SelfDescribingEvents); - -#if FEATURE_MANAGED_ETW - fixed (byte* dataPtr = rawManifest) - { - // we don't want the manifest to show up in the event log channels so we specify as keywords - // everything but the first 8 bits (reserved for the 8 channels) - var manifestDescr = new EventDescriptor(0xFFFE, 1, 0, 0, 0xFE, 0xFFFE, 0x00ffFFFFffffFFFF); - ManifestEnvelope envelope = default; - - envelope.Format = ManifestEnvelope.ManifestFormats.SimpleXmlFormat; - envelope.MajorVersion = 1; - envelope.MinorVersion = 0; - envelope.Magic = 0x5B; // An unusual number that can be checked for consistency. - int dataLeft = rawManifest.Length; - envelope.ChunkNumber = 0; - - EventProvider.EventData* dataDescrs = stackalloc EventProvider.EventData[2]; - - dataDescrs[0].Ptr = (ulong)&envelope; - dataDescrs[0].Size = (uint)sizeof(ManifestEnvelope); - dataDescrs[0].Reserved = 0; - - dataDescrs[1].Ptr = (ulong)dataPtr; - dataDescrs[1].Reserved = 0; - - int chunkSize = ManifestEnvelope.MaxChunkSize; - TRY_AGAIN_WITH_SMALLER_CHUNK_SIZE: - envelope.TotalChunks = (ushort)((dataLeft + (chunkSize - 1)) / chunkSize); - while (dataLeft > 0) - { - dataDescrs[1].Size = (uint)Math.Min(dataLeft, chunkSize); - if (m_etwProvider != null) - { - if (!m_etwProvider.WriteEvent(ref manifestDescr, IntPtr.Zero, null, null, 2, (IntPtr)dataDescrs)) - { - // Turns out that if users set the BufferSize to something less than 64K then WriteEvent - // can fail. If we get this failure on the first chunk try again with something smaller - // The smallest BufferSize is 1K so if we get to 256 (to account for envelope overhead), we can give up making it smaller. - if (EventProvider.GetLastWriteEventError() == EventProvider.WriteEventErrorCode.EventTooBig) - { - if (envelope.ChunkNumber == 0 && chunkSize > 256) - { - chunkSize /= 2; - goto TRY_AGAIN_WITH_SMALLER_CHUNK_SIZE; - } - } - - if (ThrowOnEventWriteErrors) - ThrowEventSourceException("SendManifest"); - break; - } - } - dataLeft -= chunkSize; - dataDescrs[1].Ptr += (uint)chunkSize; - envelope.ChunkNumber++; - - // For large manifests we want to not overflow any receiver's buffer. Most manifests will fit within - // 5 chunks, so only the largest manifests will hit the pause. - if ((envelope.ChunkNumber % 5) == 0) - { - Thread.Sleep(15); - } - } - } -#endif // FEATURE_MANAGED_ETW - } - -#if (ES_BUILD_PCL) - internal static Attribute GetCustomAttributeHelper(Type type, Type attributeType, EventManifestOptions flags = EventManifestOptions.None) - { - return GetCustomAttributeHelper(type.GetTypeInfo(), attributeType, flags); - } -#endif - - // Helper to deal with the fact that the type we are reflecting over might be loaded in the ReflectionOnly context. - // When that is the case, we have the build the custom assemblies on a member by hand. - internal static Attribute? GetCustomAttributeHelper(MemberInfo member, Type attributeType, EventManifestOptions flags = EventManifestOptions.None) - { -#if !ES_BUILD_PN - // On ProjectN, ReflectionOnly() always equals false. AllowEventSourceOverride is an option that allows either Microsoft.Diagnostics.Tracing or - // System.Diagnostics.Tracing EventSource to be considered valid. This should not mattter anywhere but in Microsoft.Diagnostics.Tracing (nuget package). - if (!member.Module.Assembly.ReflectionOnly() && (flags & EventManifestOptions.AllowEventSourceOverride) == 0) -#endif // !ES_BUILD_PN - { - // Let the runtime to the work for us, since we can execute code in this context. - Attribute? firstAttribute = null; - foreach (object attribute in member.GetCustomAttributes(attributeType, false)) - { - firstAttribute = (Attribute)attribute; - break; - } - return firstAttribute; - } - -#if (!ES_BUILD_PCL && !ES_BUILD_PN) - foreach (CustomAttributeData data in CustomAttributeData.GetCustomAttributes(member)) - { - if (AttributeTypeNamesMatch(attributeType, data.Constructor.ReflectedType!)) - { - Attribute? attr = null; - - Debug.Assert(data.ConstructorArguments.Count <= 1); - - if (data.ConstructorArguments.Count == 1) - { - attr = (Attribute?)Activator.CreateInstance(attributeType, new object?[] { data.ConstructorArguments[0].Value }); - } - else if (data.ConstructorArguments.Count == 0) - { - attr = (Attribute?)Activator.CreateInstance(attributeType); - } - - if (attr != null) - { - Type t = attr.GetType(); - - foreach (CustomAttributeNamedArgument namedArgument in data.NamedArguments) - { - PropertyInfo p = t.GetProperty(namedArgument.MemberInfo.Name, BindingFlags.Public | BindingFlags.Instance)!; - object value = namedArgument.TypedValue.Value!; - - if (p.PropertyType.IsEnum) - { - string val = value.ToString()!; - value = Enum.Parse(p.PropertyType, val); - } - - p.SetValue(attr, value, null); - } - - return attr; - } - } - } - - return null; -#else // ES_BUILD_PCL && ES_BUILD_PN - // Don't use nameof here because the resource doesn't exist on some platforms, which results in a compilation error. - throw new ArgumentException("EventSource_PCLPlatformNotSupportedReflection", "EventSource"); -#endif - } - - /// <summary> - /// Evaluates if two related "EventSource"-domain types should be considered the same - /// </summary> - /// <param name="attributeType">The attribute type in the load context - it's associated with the running - /// EventSource type. This type may be different fromt he base type of the user-defined EventSource.</param> - /// <param name="reflectedAttributeType">The attribute type in the reflection context - it's associated with - /// the user-defined EventSource, and is in the same assembly as the eventSourceType passed to - /// </param> - /// <returns>True - if the types should be considered equivalent, False - otherwise</returns> - private static bool AttributeTypeNamesMatch(Type attributeType, Type reflectedAttributeType) - { - return - // are these the same type? - attributeType == reflectedAttributeType || - // are the full typenames equal? - string.Equals(attributeType.FullName, reflectedAttributeType.FullName, StringComparison.Ordinal) || - // are the typenames equal and the namespaces under "Diagnostics.Tracing" (typically - // either Microsoft.Diagnostics.Tracing or System.Diagnostics.Tracing)? - string.Equals(attributeType.Name, reflectedAttributeType.Name, StringComparison.Ordinal) && - attributeType.Namespace!.EndsWith("Diagnostics.Tracing", StringComparison.Ordinal) && - (reflectedAttributeType.Namespace!.EndsWith("Diagnostics.Tracing", StringComparison.Ordinal) -#if EVENT_SOURCE_LEGACY_NAMESPACE_SUPPORT - || reflectedAttributeType.Namespace.EndsWith("Diagnostics.Eventing", StringComparison.Ordinal) -#endif -); - } - - private static Type? GetEventSourceBaseType(Type eventSourceType, bool allowEventSourceOverride, bool reflectionOnly) - { - Type? ret = eventSourceType; - - // return false for "object" and interfaces - if (ret.BaseType() == null) - return null; - - // now go up the inheritance chain until hitting a concrete type ("object" at worse) - do - { - ret = ret.BaseType(); - } - while (ret != null && ret.IsAbstract()); - - if (ret != null) - { - if (!allowEventSourceOverride) - { - if (reflectionOnly && ret.FullName != typeof(EventSource).FullName || - !reflectionOnly && ret != typeof(EventSource)) - return null; - } - else - { - if (ret.Name != "EventSource") - return null; - } - } - return ret; - } - - // Use reflection to look at the attributes of a class, and generate a manifest for it (as UTF8) and - // return the UTF8 bytes. It also sets up the code:EventData structures needed to dispatch events - // at run time. 'source' is the event source to place the descriptors. If it is null, - // then the descriptors are not creaed, and just the manifest is generated. - private static byte[]? CreateManifestAndDescriptors(Type eventSourceType, string? eventSourceDllName, EventSource? source, - EventManifestOptions flags = EventManifestOptions.None) - { - ManifestBuilder? manifest = null; - bool bNeedsManifest = source != null ? !source.SelfDescribingEvents : true; - Exception? exception = null; // exception that might get raised during validation b/c we couldn't/didn't recover from a previous error - byte[]? res = null; - - if (eventSourceType.IsAbstract() && (flags & EventManifestOptions.Strict) == 0) - return null; - - try - { - MethodInfo[] methods = eventSourceType.GetMethods(BindingFlags.DeclaredOnly | BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance); - EventAttribute defaultEventAttribute; - int eventId = 1; // The number given to an event that does not have a explicitly given ID. - EventMetadata[]? eventData = null; - Dictionary<string, string>? eventsByName = null; - if (source != null || (flags & EventManifestOptions.Strict) != 0) - { - eventData = new EventMetadata[methods.Length + 1]; - eventData[0].Name = ""; // Event 0 is the 'write messages string' event, and has an empty name. - } - - // See if we have localization information. - ResourceManager? resources = null; - EventSourceAttribute? eventSourceAttrib = (EventSourceAttribute?)GetCustomAttributeHelper(eventSourceType, typeof(EventSourceAttribute), flags); - if (eventSourceAttrib != null && eventSourceAttrib.LocalizationResources != null) - resources = new ResourceManager(eventSourceAttrib.LocalizationResources, eventSourceType.Assembly()); - - manifest = new ManifestBuilder(GetName(eventSourceType, flags), GetGuid(eventSourceType), eventSourceDllName, - resources, flags); - - // Add an entry unconditionally for event ID 0 which will be for a string message. - manifest.StartEvent("EventSourceMessage", new EventAttribute(0) { Level = EventLevel.LogAlways, Task = (EventTask)0xFFFE }); - manifest.AddEventParameter(typeof(string), "message"); - manifest.EndEvent(); - - // eventSourceType must be sealed and must derive from this EventSource - if ((flags & EventManifestOptions.Strict) != 0) - { - bool typeMatch = GetEventSourceBaseType(eventSourceType, (flags & EventManifestOptions.AllowEventSourceOverride) != 0, eventSourceType.Assembly().ReflectionOnly()) != null; - - if (!typeMatch) - { - manifest.ManifestError(SR.EventSource_TypeMustDeriveFromEventSource); - } - if (!eventSourceType.IsAbstract() && !eventSourceType.IsSealed()) - { - manifest.ManifestError(SR.EventSource_TypeMustBeSealedOrAbstract); - } - } - - // Collect task, opcode, keyword and channel information -#if FEATURE_MANAGED_ETW_CHANNELS && FEATURE_ADVANCED_MANAGED_ETW_CHANNELS - foreach (var providerEnumKind in new string[] { "Keywords", "Tasks", "Opcodes", "Channels" }) -#else - foreach (string providerEnumKind in new string[] { "Keywords", "Tasks", "Opcodes" }) -#endif - { - Type? nestedType = eventSourceType.GetNestedType(providerEnumKind); - if (nestedType != null) - { - if (eventSourceType.IsAbstract()) - { - manifest.ManifestError(SR.Format(SR.EventSource_AbstractMustNotDeclareKTOC, nestedType.Name)); - } - else - { - foreach (FieldInfo staticField in nestedType.GetFields(BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static)) - { - AddProviderEnumKind(manifest, staticField, providerEnumKind); - } - } - } - } - // ensure we have keywords for the session-filtering reserved bits - { - manifest.AddKeyword("Session3", (long)0x1000 << 32); - manifest.AddKeyword("Session2", (long)0x2000 << 32); - manifest.AddKeyword("Session1", (long)0x4000 << 32); - manifest.AddKeyword("Session0", (long)0x8000 << 32); - } - - if (eventSourceType != typeof(EventSource)) - { - for (int i = 0; i < methods.Length; i++) - { - MethodInfo method = methods[i]; - ParameterInfo[] args = method.GetParameters(); - - // Get the EventDescriptor (from the Custom attributes) - EventAttribute? eventAttribute = (EventAttribute?)GetCustomAttributeHelper(method, typeof(EventAttribute), flags); - - // Compat: until v4.5.1 we ignored any non-void returning methods as well as virtual methods for - // the only reason of limiting the number of methods considered to be events. This broke a common - // design of having event sources implement specific interfaces. To fix this in a compatible way - // we will now allow both non-void returning and virtual methods to be Event methods, as long - // as they are marked with the [Event] attribute - if (/* method.IsVirtual || */ method.IsStatic) - { - continue; - } - - if (eventSourceType.IsAbstract()) - { - if (eventAttribute != null) - { - manifest.ManifestError(SR.Format(SR.EventSource_AbstractMustNotDeclareEventMethods, method.Name, eventAttribute.EventId)); - } - continue; - } - else if (eventAttribute == null) - { - // Methods that don't return void can't be events, if they're NOT marked with [Event]. - // (see Compat comment above) - if (method.ReturnType != typeof(void)) - { - continue; - } - - // Continue to ignore virtual methods if they do NOT have the [Event] attribute - // (see Compat comment above) - if (method.IsVirtual) - { - continue; - } - - // If we explicitly mark the method as not being an event, then honor that. - if (GetCustomAttributeHelper(method, typeof(NonEventAttribute), flags) != null) - continue; - - defaultEventAttribute = new EventAttribute(eventId); - eventAttribute = defaultEventAttribute; - } - else if (eventAttribute.EventId <= 0) - { - manifest.ManifestError(SR.Format(SR.EventSource_NeedPositiveId, method.Name), true); - continue; // don't validate anything else for this event - } - if (method.Name.LastIndexOf('.') >= 0) - { - manifest.ManifestError(SR.Format(SR.EventSource_EventMustNotBeExplicitImplementation, method.Name, eventAttribute.EventId)); - } - - eventId++; - string eventName = method.Name; - - if (eventAttribute.Opcode == EventOpcode.Info) // We are still using the default opcode. - { - // By default pick a task ID derived from the EventID, starting with the highest task number and working back - bool noTask = (eventAttribute.Task == EventTask.None); - if (noTask) - eventAttribute.Task = (EventTask)(0xFFFE - eventAttribute.EventId); - - // Unless we explicitly set the opcode to Info (to override the auto-generate of Start or Stop opcodes, - // pick a default opcode based on the event name (either Info or start or stop if the name ends with that suffix). - if (!eventAttribute.IsOpcodeSet) - eventAttribute.Opcode = GetOpcodeWithDefault(EventOpcode.Info, eventName); - - // Make the stop opcode have the same task as the start opcode. - if (noTask) - { - if (eventAttribute.Opcode == EventOpcode.Start) - { - string taskName = eventName.Substring(0, eventName.Length - s_ActivityStartSuffix.Length); // Remove the Stop suffix to get the task name - if (string.Compare(eventName, 0, taskName, 0, taskName.Length) == 0 && - string.Compare(eventName, taskName.Length, s_ActivityStartSuffix, 0, Math.Max(eventName.Length - taskName.Length, s_ActivityStartSuffix.Length)) == 0) - { - // Add a task that is just the task name for the start event. This suppress the auto-task generation - // That would otherwise happen (and create 'TaskName'Start as task name rather than just 'TaskName' - manifest.AddTask(taskName, (int)eventAttribute.Task); - } - } - else if (eventAttribute.Opcode == EventOpcode.Stop) - { - // Find the start associated with this stop event. We require start to be immediately before the stop - int startEventId = eventAttribute.EventId - 1; - if (eventData != null && startEventId < eventData.Length) - { - Debug.Assert(0 <= startEventId); // Since we reserve id 0, we know that id-1 is <= 0 - EventMetadata startEventMetadata = eventData[startEventId]; - - // If you remove the Stop and add a Start does that name match the Start Event's Name? - // Ideally we would throw an error - string taskName = eventName.Substring(0, eventName.Length - s_ActivityStopSuffix.Length); // Remove the Stop suffix to get the task name - if (startEventMetadata.Descriptor.Opcode == (byte)EventOpcode.Start && - string.Compare(startEventMetadata.Name, 0, taskName, 0, taskName.Length) == 0 && - string.Compare(startEventMetadata.Name, taskName.Length, s_ActivityStartSuffix, 0, Math.Max(startEventMetadata.Name.Length - taskName.Length, s_ActivityStartSuffix.Length)) == 0) - { - // Make the stop event match the start event - eventAttribute.Task = (EventTask)startEventMetadata.Descriptor.Task; - noTask = false; - } - } - if (noTask && (flags & EventManifestOptions.Strict) != 0) // Throw an error if we can compatibly. - { - throw new ArgumentException(SR.EventSource_StopsFollowStarts); - } - } - } - } - - bool hasRelatedActivityID = RemoveFirstArgIfRelatedActivityId(ref args); - if (!(source != null && source.SelfDescribingEvents)) - { - manifest.StartEvent(eventName, eventAttribute); - for (int fieldIdx = 0; fieldIdx < args.Length; fieldIdx++) - { - manifest.AddEventParameter(args[fieldIdx].ParameterType, args[fieldIdx].Name!); - } - manifest.EndEvent(); - } - - if (source != null || (flags & EventManifestOptions.Strict) != 0) - { - Debug.Assert(eventData != null); - // Do checking for user errors (optional, but not a big deal so we do it). - DebugCheckEvent(ref eventsByName, eventData, method, eventAttribute, manifest, flags); - -#if FEATURE_MANAGED_ETW_CHANNELS - // add the channel keyword for Event Viewer channel based filters. This is added for creating the EventDescriptors only - // and is not required for the manifest - if (eventAttribute.Channel != EventChannel.None) - { - unchecked - { - eventAttribute.Keywords |= (EventKeywords)manifest.GetChannelKeyword(eventAttribute.Channel, (ulong)eventAttribute.Keywords); - } - } -#endif - string eventKey = "event_" + eventName; - string? msg = manifest.GetLocalizedMessage(eventKey, CultureInfo.CurrentUICulture, etwFormat: false); - // overwrite inline message with the localized message - if (msg != null) eventAttribute.Message = msg; - - AddEventDescriptor(ref eventData, eventName, eventAttribute, args, hasRelatedActivityID); - } - } - } - - // Tell the TraceLogging stuff where to start allocating its own IDs. - NameInfo.ReserveEventIDsBelow(eventId); - - if (source != null) - { - Debug.Assert(eventData != null); - TrimEventDescriptors(ref eventData); - source.m_eventData = eventData; // officially initialize it. We do this at most once (it is racy otherwise). -#if FEATURE_MANAGED_ETW_CHANNELS - source.m_channelData = manifest.GetChannelData(); -#endif - } - - // if this is an abstract event source we've already performed all the validation we can - if (!eventSourceType.IsAbstract() && (source == null || !source.SelfDescribingEvents)) - { - bNeedsManifest = (flags & EventManifestOptions.OnlyIfNeededForRegistration) == 0 -#if FEATURE_MANAGED_ETW_CHANNELS - || manifest.GetChannelData().Length > 0 -#endif -; - - // if the manifest is not needed and we're not requested to validate the event source return early - if (!bNeedsManifest && (flags & EventManifestOptions.Strict) == 0) - return null; - - res = manifest.CreateManifest(); - } - } - catch (Exception e) - { - // if this is a runtime manifest generation let the exception propagate - if ((flags & EventManifestOptions.Strict) == 0) - throw; - // else store it to include it in the Argument exception we raise below - exception = e; - } - - if ((flags & EventManifestOptions.Strict) != 0 && (manifest?.Errors.Count > 0 || exception != null)) - { - string msg = string.Empty; - - if (manifest?.Errors.Count > 0) - { - bool firstError = true; - foreach (string error in manifest.Errors) - { - if (!firstError) - msg += Environment.NewLine; - firstError = false; - msg += error; - } - } - else - msg = "Unexpected error: " + exception!.Message; - - throw new ArgumentException(msg, exception); - } - - return bNeedsManifest ? res : null; - } - - private static bool RemoveFirstArgIfRelatedActivityId(ref ParameterInfo[] args) - { - // If the first parameter is (case insensitive) 'relatedActivityId' then skip it. - if (args.Length > 0 && args[0].ParameterType == typeof(Guid) && - string.Equals(args[0].Name, "relatedActivityId", StringComparison.OrdinalIgnoreCase)) - { - var newargs = new ParameterInfo[args.Length - 1]; - Array.Copy(args, 1, newargs, 0, args.Length - 1); - args = newargs; - - return true; - } - - return false; - } - - // adds a enumeration (keyword, opcode, task or channel) represented by 'staticField' - // to the manifest. - private static void AddProviderEnumKind(ManifestBuilder manifest, FieldInfo staticField, string providerEnumKind) - { - bool reflectionOnly = staticField.Module.Assembly.ReflectionOnly(); - Type staticFieldType = staticField.FieldType; - if (!reflectionOnly && (staticFieldType == typeof(EventOpcode)) || AttributeTypeNamesMatch(staticFieldType, typeof(EventOpcode))) - { - if (providerEnumKind != "Opcodes") goto Error; - int value = (int)staticField.GetRawConstantValue()!; - manifest.AddOpcode(staticField.Name, value); - } - else if (!reflectionOnly && (staticFieldType == typeof(EventTask)) || AttributeTypeNamesMatch(staticFieldType, typeof(EventTask))) - { - if (providerEnumKind != "Tasks") goto Error; - int value = (int)staticField.GetRawConstantValue()!; - manifest.AddTask(staticField.Name, value); - } - else if (!reflectionOnly && (staticFieldType == typeof(EventKeywords)) || AttributeTypeNamesMatch(staticFieldType, typeof(EventKeywords))) - { - if (providerEnumKind != "Keywords") goto Error; - ulong value = unchecked((ulong)(long)staticField.GetRawConstantValue()!); - manifest.AddKeyword(staticField.Name, value); - } -#if FEATURE_MANAGED_ETW_CHANNELS && FEATURE_ADVANCED_MANAGED_ETW_CHANNELS - else if (!reflectionOnly && (staticFieldType == typeof(EventChannel)) || AttributeTypeNamesMatch(staticFieldType, typeof(EventChannel))) - { - if (providerEnumKind != "Channels") goto Error; - var channelAttribute = (EventChannelAttribute)GetCustomAttributeHelper(staticField, typeof(EventChannelAttribute)); - manifest.AddChannel(staticField.Name, (byte)staticField.GetRawConstantValue(), channelAttribute); - } -#endif - return; - Error: - manifest.ManifestError(SR.Format(SR.EventSource_EnumKindMismatch, staticField.Name, staticField.FieldType.Name, providerEnumKind)); - } - - // Helper used by code:CreateManifestAndDescriptors to add a code:EventData descriptor for a method - // with the code:EventAttribute 'eventAttribute'. resourceManger may be null in which case we populate it - // it is populated if we need to look up message resources - private static void AddEventDescriptor( - [NotNull] ref EventMetadata[] eventData, - string eventName, - EventAttribute eventAttribute, - ParameterInfo[] eventParameters, - bool hasRelatedActivityID) - { - if (eventData.Length <= eventAttribute.EventId) - { - EventMetadata[] newValues = new EventMetadata[Math.Max(eventData.Length + 16, eventAttribute.EventId + 1)]; - Array.Copy(eventData, newValues, eventData.Length); - eventData = newValues; - } - - eventData[eventAttribute.EventId].Descriptor = new EventDescriptor( - eventAttribute.EventId, - eventAttribute.Version, -#if FEATURE_MANAGED_ETW_CHANNELS - (byte)eventAttribute.Channel, -#else - (byte)0, -#endif - (byte)eventAttribute.Level, - (byte)eventAttribute.Opcode, - (int)eventAttribute.Task, - unchecked((long)((ulong)eventAttribute.Keywords | SessionMask.All.ToEventKeywords()))); - - eventData[eventAttribute.EventId].Tags = eventAttribute.Tags; - eventData[eventAttribute.EventId].Name = eventName; - eventData[eventAttribute.EventId].Parameters = eventParameters; - eventData[eventAttribute.EventId].Message = eventAttribute.Message; - eventData[eventAttribute.EventId].ActivityOptions = eventAttribute.ActivityOptions; - eventData[eventAttribute.EventId].HasRelatedActivityID = hasRelatedActivityID; - eventData[eventAttribute.EventId].EventHandle = IntPtr.Zero; - } - - // Helper used by code:CreateManifestAndDescriptors that trims the m_eventData array to the correct - // size after all event descriptors have been added. - private static void TrimEventDescriptors(ref EventMetadata[] eventData) - { - int idx = eventData.Length; - while (0 < idx) - { - --idx; - if (eventData[idx].Descriptor.EventId != 0) - break; - } - if (eventData.Length - idx > 2) // allow one wasted slot. - { - EventMetadata[] newValues = new EventMetadata[idx + 1]; - Array.Copy(eventData, newValues, newValues.Length); - eventData = newValues; - } - } - - // Helper used by code:EventListener.AddEventSource and code:EventListener.EventListener - // when a listener gets attached to a eventSource - internal void AddListener(EventListener listener) - { - lock (EventListener.EventListenersLock) - { - bool[]? enabledArray = null; - if (m_eventData != null) - enabledArray = new bool[m_eventData.Length]; - m_Dispatchers = new EventDispatcher(m_Dispatchers, enabledArray, listener); - listener.OnEventSourceCreated(this); - } - } - - // Helper used by code:CreateManifestAndDescriptors to find user mistakes like reusing an event - // index for two distinct events etc. Throws exceptions when it finds something wrong. - private static void DebugCheckEvent(ref Dictionary<string, string>? eventsByName, - EventMetadata[] eventData, MethodInfo method, EventAttribute eventAttribute, - ManifestBuilder manifest, EventManifestOptions options) - { - int evtId = eventAttribute.EventId; - string evtName = method.Name; - int eventArg = GetHelperCallFirstArg(method); - if (eventArg >= 0 && evtId != eventArg) - { - manifest.ManifestError(SR.Format(SR.EventSource_MismatchIdToWriteEvent, evtName, evtId, eventArg), true); - } - - if (evtId < eventData.Length && eventData[evtId].Descriptor.EventId != 0) - { - manifest.ManifestError(SR.Format(SR.EventSource_EventIdReused, evtName, evtId, eventData[evtId].Name), true); - } - - // We give a task to things if they don't have one. - // TODO this is moderately expensive (N*N). We probably should not even bother.... - Debug.Assert(eventAttribute.Task != EventTask.None || eventAttribute.Opcode != EventOpcode.Info); - for (int idx = 0; idx < eventData.Length; ++idx) - { - // skip unused Event IDs. - if (eventData[idx].Name == null) - continue; - - if (eventData[idx].Descriptor.Task == (int)eventAttribute.Task && eventData[idx].Descriptor.Opcode == (int)eventAttribute.Opcode) - { - manifest.ManifestError(SR.Format(SR.EventSource_TaskOpcodePairReused, - evtName, evtId, eventData[idx].Name, idx)); - // If we are not strict stop on first error. We have had problems with really large providers taking forever. because of many errors. - if ((options & EventManifestOptions.Strict) == 0) - break; - } - } - - // for non-default event opcodes the user must define a task! - if (eventAttribute.Opcode != EventOpcode.Info) - { - bool failure = false; - if (eventAttribute.Task == EventTask.None) - failure = true; - else - { - // If you have the auto-assigned Task, then you did not explicitly set one. - // This is OK for Start events because we have special logic to assign the task to a prefix derived from the event name - // But all other cases we want to catch the omission. - var autoAssignedTask = (EventTask)(0xFFFE - evtId); - if (eventAttribute.Opcode != EventOpcode.Start && eventAttribute.Opcode != EventOpcode.Stop && eventAttribute.Task == autoAssignedTask) - failure = true; - } - if (failure) - { - manifest.ManifestError(SR.Format(SR.EventSource_EventMustHaveTaskIfNonDefaultOpcode, evtName, evtId)); - } - } - - // If we ever want to enforce the rule: MethodName = TaskName + OpcodeName here's how: - // (the reason we don't is backwards compat and the need for handling this as a non-fatal error - // by eventRegister.exe) - // taskName & opcodeName could be passed in by the caller which has opTab & taskTab handy - // if (!(((int)eventAttribute.Opcode == 0 && evtName == taskName) || (evtName == taskName+opcodeName))) - // { - // throw new WarningException(SR.EventSource_EventNameDoesNotEqualTaskPlusOpcode); - // } - - eventsByName ??= new Dictionary<string, string>(); - - if (eventsByName.ContainsKey(evtName)) - { - manifest.ManifestError(SR.Format(SR.EventSource_EventNameReused, evtName), true); - } - - eventsByName[evtName] = evtName; - } - - /// <summary> - /// This method looks at the IL and tries to pattern match against the standard - /// 'boilerplate' event body - /// <code> - /// { if (Enabled()) WriteEvent(#, ...) } - /// </code> - /// If the pattern matches, it returns the literal number passed as the first parameter to - /// the WriteEvent. This is used to find common user errors (mismatching this - /// number with the EventAttribute ID). It is only used for validation. - /// </summary> - /// <param name="method">The method to probe.</param> - /// <returns>The literal value or -1 if the value could not be determined. </returns> - private static int GetHelperCallFirstArg(MethodInfo method) - { -#if (!ES_BUILD_PCL && !ES_BUILD_PN) - // Currently searches for the following pattern - // - // ... // CAN ONLY BE THE INSTRUCTIONS BELOW - // LDARG0 - // LDC.I4 XXX - // ... // CAN ONLY BE THE INSTRUCTIONS BELOW CAN'T BE A BRANCH OR A CALL - // CALL - // NOP // 0 or more times - // RET - // - // If we find this pattern we return the XXX. Otherwise we return -1. -#if ES_BUILD_STANDALONE - (new ReflectionPermission(ReflectionPermissionFlag.MemberAccess)).Assert(); -#endif - byte[] instrs = method.GetMethodBody()!.GetILAsByteArray()!; - int retVal = -1; - for (int idx = 0; idx < instrs.Length;) - { - switch (instrs[idx]) - { - case 0: // NOP - case 1: // BREAK - case 2: // LDARG_0 - case 3: // LDARG_1 - case 4: // LDARG_2 - case 5: // LDARG_3 - case 6: // LDLOC_0 - case 7: // LDLOC_1 - case 8: // LDLOC_2 - case 9: // LDLOC_3 - case 10: // STLOC_0 - case 11: // STLOC_1 - case 12: // STLOC_2 - case 13: // STLOC_3 - break; - case 14: // LDARG_S - case 16: // STARG_S - idx++; - break; - case 20: // LDNULL - break; - case 21: // LDC_I4_M1 - case 22: // LDC_I4_0 - case 23: // LDC_I4_1 - case 24: // LDC_I4_2 - case 25: // LDC_I4_3 - case 26: // LDC_I4_4 - case 27: // LDC_I4_5 - case 28: // LDC_I4_6 - case 29: // LDC_I4_7 - case 30: // LDC_I4_8 - if (idx > 0 && instrs[idx - 1] == 2) // preceeded by LDARG0 - retVal = instrs[idx] - 22; - break; - case 31: // LDC_I4_S - if (idx > 0 && instrs[idx - 1] == 2) // preceeded by LDARG0 - retVal = instrs[idx + 1]; - idx++; - break; - case 32: // LDC_I4 - idx += 4; - break; - case 37: // DUP - break; - case 40: // CALL - idx += 4; - - if (retVal >= 0) - { - // Is this call just before return? - for (int search = idx + 1; search < instrs.Length; search++) - { - if (instrs[search] == 42) // RET - return retVal; - if (instrs[search] != 0) // NOP - break; - } - } - retVal = -1; - break; - case 44: // BRFALSE_S - case 45: // BRTRUE_S - retVal = -1; - idx++; - break; - case 57: // BRFALSE - case 58: // BRTRUE - retVal = -1; - idx += 4; - break; - case 103: // CONV_I1 - case 104: // CONV_I2 - case 105: // CONV_I4 - case 106: // CONV_I8 - case 109: // CONV_U4 - case 110: // CONV_U8 - break; - case 140: // BOX - case 141: // NEWARR - idx += 4; - break; - case 162: // STELEM_REF - break; - case 254: // PREFIX - idx++; - // Covers the CEQ instructions used in debug code for some reason. - if (idx >= instrs.Length || instrs[idx] >= 6) - goto default; - break; - default: - /* Debug.Fail("Warning: User validation code sub-optimial: Unsuported opcode " + instrs[idx] + - " at " + idx + " in method " + method.Name); */ - return -1; - } - idx++; - } -#endif - return -1; - } - - /// <summary> - /// Sends an error message to the debugger (outputDebugString), as well as the EventListeners - /// It will do this even if the EventSource is not enabled. - /// </summary> - internal void ReportOutOfBandMessage(string msg) - { - try - { -#if (!ES_BUILD_PCL && !ES_BUILD_PN) - // send message to debugger without delay - System.Diagnostics.Debugger.Log(0, null, string.Format("EventSource Error: {0}{1}", msg, Environment.NewLine)); -#endif - - // Send it to all listeners. - if (m_outOfBandMessageCount < 16 - 1) // Note this is only if size byte - m_outOfBandMessageCount++; - else - { - if (m_outOfBandMessageCount == 16) - return; - m_outOfBandMessageCount = 16; // Mark that we hit the limit. Notify them that this is the case. - msg = "Reached message limit. End of EventSource error messages."; - } - - WriteEventString(EventLevel.LogAlways, -1, msg); - WriteStringToAllListeners("EventSourceMessage", msg); - } - catch { } // If we fail during last chance logging, well, we have to give up.... - } - - private EventSourceSettings ValidateSettings(EventSourceSettings settings) - { - const EventSourceSettings evtFormatMask = EventSourceSettings.EtwManifestEventFormat | - EventSourceSettings.EtwSelfDescribingEventFormat; - if ((settings & evtFormatMask) == evtFormatMask) - { - throw new ArgumentException(SR.EventSource_InvalidEventFormat, nameof(settings)); - } - - // If you did not explicitly ask for manifest, you get self-describing. - if ((settings & evtFormatMask) == 0) - settings |= EventSourceSettings.EtwSelfDescribingEventFormat; - return settings; - } - - private bool ThrowOnEventWriteErrors => (m_config & EventSourceSettings.ThrowOnEventWriteErrors) != 0; - - private bool SelfDescribingEvents - { - get - { - Debug.Assert(((m_config & EventSourceSettings.EtwManifestEventFormat) != 0) != - ((m_config & EventSourceSettings.EtwSelfDescribingEventFormat) != 0)); - return (m_config & EventSourceSettings.EtwSelfDescribingEventFormat) != 0; - } - } - - // 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) -#if FEATURE_MANAGED_ETW - private volatile OverideEventProvider m_etwProvider = null!; // This hooks up ETW commands to our 'OnEventCommand' callback -#endif -#if FEATURE_PERFTRACING - private volatile OverideEventProvider 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 - - internal static uint s_currentPid; // current process id, used in synthesizing quasi-GUIDs - [ThreadStatic] - private static byte m_EventSourceExceptionRecurenceCount; // current recursion count inside ThrowEventSourceException - - [ThreadStatic] - private static bool m_EventSourceInDecodeObject; - -#if FEATURE_MANAGED_ETW_CHANNELS - internal volatile ulong[]? m_channelData; -#endif - - // 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 s_ActivityStartSuffix = "Start"; - internal const string s_ActivityStopSuffix = "Stop"; - - // WARNING: Do not depend upon initialized statics during creation of EventSources, as it is possible for creation of an EventSource to trigger - // creation of yet another EventSource. When this happens, these statics may not yet be initialized. - // Rather than depending on initialized statics, use lazy initialization to ensure that the statics are initialized exactly when they are needed. - - // used for generating GUID from eventsource name - private static byte[]? namespaceBytes; - -#endregion - } - - /// <summary> - /// Enables specifying event source configuration options to be used in the EventSource constructor. - /// </summary> - [Flags] - public enum EventSourceSettings - { - /// <summary> - /// This specifies none of the special configuration options should be enabled. - /// </summary> - Default = 0, - /// <summary> - /// Normally an EventSource NEVER throws; setting this option will tell it to throw when it encounters errors. - /// </summary> - ThrowOnEventWriteErrors = 1, - /// <summary> - /// Setting this option is a directive to the ETW listener should use manifest-based format when - /// firing events. This is the default option when defining a type derived from EventSource - /// (using the protected EventSource constructors). - /// Only one of EtwManifestEventFormat or EtwSelfDescribingEventFormat should be specified - /// </summary> - EtwManifestEventFormat = 4, - /// <summary> - /// Setting this option is a directive to the ETW listener should use self-describing event format - /// when firing events. This is the default option when creating a new instance of the EventSource - /// type (using the public EventSource constructors). - /// Only one of EtwManifestEventFormat or EtwSelfDescribingEventFormat should be specified - /// </summary> - EtwSelfDescribingEventFormat = 8, - } - - /// <summary> - /// An EventListener represents a target for the events generated by EventSources (that is subclasses - /// of <see cref="EventSource"/>), in the current appdomain. When a new EventListener is created - /// it is logically attached to all eventSources in that appdomain. When the EventListener is Disposed, then - /// it is disconnected from the event eventSources. Note that there is a internal list of STRONG references - /// to EventListeners, which means that relying on the lack of references to EventListeners to clean up - /// EventListeners will NOT work. You must call EventListener.Dispose explicitly when a dispatcher is no - /// longer needed. - /// <para> - /// Once created, EventListeners can enable or disable on a per-eventSource basis using verbosity levels - /// (<see cref="EventLevel"/>) and bitfields (<see cref="EventKeywords"/>) to further restrict the set of - /// events to be sent to the dispatcher. The dispatcher can also send arbitrary commands to a particular - /// eventSource using the 'SendCommand' method. The meaning of the commands are eventSource specific. - /// </para><para> - /// The Null Guid (that is (new Guid()) has special meaning as a wildcard for 'all current eventSources in - /// the appdomain'. Thus it is relatively easy to turn on all events in the appdomain if desired. - /// </para><para> - /// It is possible for there to be many EventListener's defined in a single appdomain. Each dispatcher is - /// logically independent of the other listeners. Thus when one dispatcher enables or disables events, it - /// affects only that dispatcher (other listeners get the events they asked for). It is possible that - /// commands sent with 'SendCommand' would do a semantic operation that would affect the other listeners - /// (like doing a GC, or flushing data ...), but this is the exception rather than the rule. - /// </para><para> - /// Thus the model is that each EventSource keeps a list of EventListeners that it is sending events - /// to. Associated with each EventSource-dispatcher pair is a set of filtering criteria that determine for - /// that eventSource what events that dispatcher will receive. - /// </para><para> - /// Listeners receive the events on their 'OnEventWritten' method. Thus subclasses of EventListener must - /// override this method to do something useful with the data. - /// </para><para> - /// In addition, when new eventSources are created, the 'OnEventSourceCreate' method is called. The - /// invariant associated with this callback is that every eventSource gets exactly one - /// 'OnEventSourceCreate' call for ever eventSource that can potentially send it log messages. In - /// particular when a EventListener is created, typically a series of OnEventSourceCreate' calls are - /// made to notify the new dispatcher of all the eventSources that existed before the EventListener was - /// created. - /// </para> - /// </summary> - public class EventListener : IDisposable - { - private event EventHandler<EventSourceCreatedEventArgs>? _EventSourceCreated; - - /// <summary> - /// This event is raised whenever a new eventSource is 'attached' to the dispatcher. - /// This can happen for all existing EventSources when the EventListener is created - /// as well as for any EventSources that come into existence after the EventListener - /// has been created. - /// - /// These 'catch up' events are called during the construction of the EventListener. - /// Subclasses need to be prepared for that. - /// - /// In a multi-threaded environment, it is possible that 'EventSourceEventWrittenCallback' - /// events for a particular eventSource to occur BEFORE the EventSourceCreatedCallback is issued. - /// </summary> - public event EventHandler<EventSourceCreatedEventArgs>? EventSourceCreated - { - add - { - CallBackForExistingEventSources(false, value); - - this._EventSourceCreated = (EventHandler<EventSourceCreatedEventArgs>?)Delegate.Combine(_EventSourceCreated, value); - } - remove - { - this._EventSourceCreated = (EventHandler<EventSourceCreatedEventArgs>?)Delegate.Remove(_EventSourceCreated, value); - } - } - - /// <summary> - /// This event is raised whenever an event has been written by a EventSource for which - /// the EventListener has enabled events. - /// </summary> - public event EventHandler<EventWrittenEventArgs>? EventWritten; - - static EventListener() - { -#if FEATURE_PERFTRACING - // Ensure that NativeRuntimeEventSource is initialized so that EventListeners get an opportunity to subscribe to its events. - // This is required because NativeRuntimeEventSource never emit events on its own, and thus will never be initialized - // in the normal way that EventSources are initialized. - GC.KeepAlive(NativeRuntimeEventSource.Log); -#endif // FEATURE_PERFTRACING - } - - /// <summary> - /// Create a new EventListener in which all events start off turned off (use EnableEvents to turn - /// them on). - /// </summary> - public EventListener() - { - // This will cause the OnEventSourceCreated callback to fire. - CallBackForExistingEventSources(true, (obj, args) => - args.EventSource!.AddListener((EventListener)obj!)); - } - - /// <summary> - /// Dispose should be called when the EventListener no longer desires 'OnEvent*' callbacks. Because - /// there is an internal list of strong references to all EventListeners, calling 'Dispose' directly - /// is the only way to actually make the listen die. Thus it is important that users of EventListener - /// call Dispose when they are done with their logging. - /// </summary> -#if ES_BUILD_STANDALONE -#endif - public virtual void Dispose() - { - lock (EventListenersLock) - { - if (s_Listeners != null) - { - if (this == s_Listeners) - { - EventListener cur = s_Listeners; - s_Listeners = this.m_Next; - RemoveReferencesToListenerInEventSources(cur); - } - else - { - // Find 'this' from the s_Listeners linked list. - EventListener prev = s_Listeners; - while (true) - { - EventListener? cur = prev.m_Next; - if (cur == null) - break; - if (cur == this) - { - // Found our Listener, remove references to it in the eventSources - prev.m_Next = cur.m_Next; // Remove entry. - RemoveReferencesToListenerInEventSources(cur); - break; - } - prev = cur; - } - } - } - Validate(); - } - } - // We don't expose a Dispose(bool), because the contract is that you don't have any non-syncronous - // 'cleanup' associated with this object - - /// <summary> - /// Enable all events from the eventSource identified by 'eventSource' to the current - /// dispatcher that have a verbosity level of 'level' or lower. - /// - /// This call can have the effect of REDUCING the number of events sent to the - /// dispatcher if 'level' indicates a less verbose level than was previously enabled. - /// - /// This call never has an effect on other EventListeners. - /// - /// </summary> - public void EnableEvents(EventSource eventSource, EventLevel level) - { - EnableEvents(eventSource, level, EventKeywords.None); - } - /// <summary> - /// Enable all events from the eventSource identified by 'eventSource' to the current - /// dispatcher that have a verbosity level of 'level' or lower and have a event keyword - /// matching any of the bits in 'matchAnyKeyword'. - /// - /// This call can have the effect of REDUCING the number of events sent to the - /// dispatcher if 'level' indicates a less verbose level than was previously enabled or - /// if 'matchAnyKeyword' has fewer keywords set than where previously set. - /// - /// This call never has an effect on other EventListeners. - /// </summary> - public void EnableEvents(EventSource eventSource, EventLevel level, EventKeywords matchAnyKeyword) - { - EnableEvents(eventSource, level, matchAnyKeyword, null); - } - /// <summary> - /// Enable all events from the eventSource identified by 'eventSource' to the current - /// dispatcher that have a verbosity level of 'level' or lower and have a event keyword - /// matching any of the bits in 'matchAnyKeyword' as well as any (eventSource specific) - /// effect passing additional 'key-value' arguments 'arguments' might have. - /// - /// This call can have the effect of REDUCING the number of events sent to the - /// dispatcher if 'level' indicates a less verbose level than was previously enabled or - /// if 'matchAnyKeyword' has fewer keywords set than where previously set. - /// - /// This call never has an effect on other EventListeners. - /// </summary> - public void EnableEvents(EventSource eventSource, EventLevel level, EventKeywords matchAnyKeyword, IDictionary<string, string?>? arguments) - { - if (eventSource == null) - { - throw new ArgumentNullException(nameof(eventSource)); - } - - eventSource.SendCommand(this, EventProviderType.None, 0, 0, EventCommand.Update, true, level, matchAnyKeyword, arguments); - -#if FEATURE_PERFTRACING - if (eventSource.GetType() == typeof(NativeRuntimeEventSource)) - { - EventPipeEventDispatcher.Instance.SendCommand(this, EventCommand.Update, true, level, matchAnyKeyword); - } -#endif // FEATURE_PERFTRACING - } - /// <summary> - /// Disables all events coming from eventSource identified by 'eventSource'. - /// - /// This call never has an effect on other EventListeners. - /// </summary> - public void DisableEvents(EventSource eventSource) - { - if (eventSource == null) - { - throw new ArgumentNullException(nameof(eventSource)); - } - - eventSource.SendCommand(this, EventProviderType.None, 0, 0, EventCommand.Update, false, EventLevel.LogAlways, EventKeywords.None, null); - -#if FEATURE_PERFTRACING - if (eventSource.GetType() == typeof(NativeRuntimeEventSource)) - { - EventPipeEventDispatcher.Instance.SendCommand(this, EventCommand.Update, false, EventLevel.LogAlways, EventKeywords.None); - } -#endif // FEATURE_PERFTRACING - } - - /// <summary> - /// EventSourceIndex is small non-negative integer (suitable for indexing in an array) - /// identifying EventSource. It is unique per-appdomain. Some EventListeners might find - /// it useful to store additional information about each eventSource connected to it, - /// and EventSourceIndex allows this extra information to be efficiently stored in a - /// (growable) array (eg List(T)). - /// </summary> - public static int EventSourceIndex(EventSource eventSource) { return eventSource.m_id; } - - /// <summary> - /// This method is called whenever a new eventSource is 'attached' to the dispatcher. - /// This can happen for all existing EventSources when the EventListener is created - /// as well as for any EventSources that come into existence after the EventListener - /// has been created. - /// - /// These 'catch up' events are called during the construction of the EventListener. - /// Subclasses need to be prepared for that. - /// - /// In a multi-threaded environment, it is possible that 'OnEventWritten' callbacks - /// for a particular eventSource to occur BEFORE the OnEventSourceCreated is issued. - /// </summary> - /// <param name="eventSource"></param> - protected internal virtual void OnEventSourceCreated(EventSource eventSource) - { - EventHandler<EventSourceCreatedEventArgs>? callBack = this._EventSourceCreated; - if (callBack != null) - { - EventSourceCreatedEventArgs args = new EventSourceCreatedEventArgs(); - args.EventSource = eventSource; - callBack(this, args); - } - } - - /// <summary> - /// This method is called whenever an event has been written by a EventSource for which - /// the EventListener has enabled events. - /// </summary> - /// <param name="eventData"></param> - protected internal virtual void OnEventWritten(EventWrittenEventArgs eventData) - { - this.EventWritten?.Invoke(this, eventData); - } - -#region private - /// <summary> - /// This routine adds newEventSource to the global list of eventSources, it also assigns the - /// ID to the eventSource (which is simply the ordinal in the global list). - /// - /// EventSources currently do not pro-actively remove themselves from this list. Instead - /// when eventSources's are GCed, the weak handle in this list naturally gets nulled, and - /// we will reuse the slot. Today this list never shrinks (but we do reuse entries - /// that are in the list). This seems OK since the expectation is that EventSources - /// tend to live for the lifetime of the appdomain anyway (they tend to be used in - /// global variables). - /// </summary> - /// <param name="newEventSource"></param> - internal static void AddEventSource(EventSource newEventSource) - { - lock (EventListenersLock) - { - s_EventSources ??= new List<WeakReference<EventSource>>(2); - - if (!s_EventSourceShutdownRegistered) - { - s_EventSourceShutdownRegistered = true; -#if ES_BUILD_STANDALONE - AppDomain.CurrentDomain.ProcessExit += DisposeOnShutdown; - AppDomain.CurrentDomain.DomainUnload += DisposeOnShutdown; -#else - AppContext.ProcessExit += DisposeOnShutdown; -#endif - } - - // Periodically search the list for existing entries to reuse, this avoids - // unbounded memory use if we keep recycling eventSources (an unlikely thing). - int newIndex = -1; - if (s_EventSources.Count % 64 == 63) // on every block of 64, fill up the block before continuing - { - int i = s_EventSources.Count; // Work from the top down. - while (0 < i) - { - --i; - WeakReference<EventSource> weakRef = s_EventSources[i]; - if (!weakRef.TryGetTarget(out _)) - { - newIndex = i; - weakRef.SetTarget(newEventSource); - break; - } - } - } - if (newIndex < 0) - { - newIndex = s_EventSources.Count; - s_EventSources.Add(new WeakReference<EventSource>(newEventSource)); - } - newEventSource.m_id = newIndex; - -#if DEBUG - // Disable validation of EventSource/EventListener connections in case a call to EventSource.AddListener - // causes a recursive call into this method. - bool previousValue = s_ConnectingEventSourcesAndListener; - s_ConnectingEventSourcesAndListener = true; - try - { -#endif - // Add every existing dispatcher to the new EventSource - for (EventListener? listener = s_Listeners; listener != null; listener = listener.m_Next) - newEventSource.AddListener(listener); -#if DEBUG - } - finally - { - s_ConnectingEventSourcesAndListener = previousValue; - } -#endif - - Validate(); - } - } - - // Whenver we have async callbacks from native code, there is an ugly issue where - // during .NET shutdown native code could be calling the callback, but the CLR - // has already prohibited callbacks to managed code in the appdomain, causing the CLR - // to throw a COMPLUS_BOOT_EXCEPTION. The guideline we give is that you must unregister - // such callbacks on process shutdown or appdomain so that unmanaged code will never - // do this. This is what this callback is for. - // See bug 724140 for more - private static void DisposeOnShutdown(object? sender, EventArgs e) - { - lock (EventListenersLock) - { - Debug.Assert(s_EventSources != null); - foreach (WeakReference<EventSource> esRef in s_EventSources) - { - if (esRef.TryGetTarget(out EventSource? es)) - es.Dispose(); - } - } - } - - /// <summary> - /// Helper used in code:Dispose that removes any references to 'listenerToRemove' in any of the - /// eventSources in the appdomain. - /// - /// The EventListenersLock must be held before calling this routine. - /// </summary> - private static void RemoveReferencesToListenerInEventSources(EventListener listenerToRemove) - { -#if !ES_BUILD_STANDALONE - Debug.Assert(Monitor.IsEntered(EventListener.EventListenersLock)); -#endif - // Foreach existing EventSource in the appdomain - Debug.Assert(s_EventSources != null); - foreach (WeakReference<EventSource> eventSourceRef in s_EventSources) - { - if (eventSourceRef.TryGetTarget(out EventSource? eventSource)) - { - Debug.Assert(eventSource.m_Dispatchers != null); - // Is the first output dispatcher the dispatcher we are removing? - if (eventSource.m_Dispatchers.m_Listener == listenerToRemove) - eventSource.m_Dispatchers = eventSource.m_Dispatchers.m_Next; - else - { - // Remove 'listenerToRemove' from the eventSource.m_Dispatchers linked list. - EventDispatcher? prev = eventSource.m_Dispatchers; - while (true) - { - EventDispatcher? cur = prev.m_Next; - if (cur == null) - { - Debug.Fail("EventSource did not have a registered EventListener!"); - break; - } - if (cur.m_Listener == listenerToRemove) - { - prev.m_Next = cur.m_Next; // Remove entry. - break; - } - prev = cur; - } - } - } - } - -#if FEATURE_PERFTRACING - // Remove the listener from the EventPipe dispatcher. - EventPipeEventDispatcher.Instance.RemoveEventListener(listenerToRemove); -#endif // FEATURE_PERFTRACING - } - - /// <summary> - /// Checks internal consistency of EventSources/Listeners. - /// </summary> - [Conditional("DEBUG")] - internal static void Validate() - { -#if DEBUG - // Don't run validation code if we're in the middle of modifying the connections between EventSources and EventListeners. - if (s_ConnectingEventSourcesAndListener) - { - return; - } -#endif - - lock (EventListenersLock) - { - Debug.Assert(s_EventSources != null); - // Get all listeners - Dictionary<EventListener, bool> allListeners = new Dictionary<EventListener, bool>(); - EventListener? cur = s_Listeners; - while (cur != null) - { - allListeners.Add(cur, true); - cur = cur.m_Next; - } - - // For all eventSources - int id = -1; - foreach (WeakReference<EventSource> eventSourceRef in s_EventSources) - { - id++; - if (!eventSourceRef.TryGetTarget(out EventSource? eventSource)) - continue; - Debug.Assert(eventSource.m_id == id, "Unexpected event source ID."); - - // None listeners on eventSources exist in the dispatcher list. - EventDispatcher? dispatcher = eventSource.m_Dispatchers; - while (dispatcher != null) - { - Debug.Assert(allListeners.ContainsKey(dispatcher.m_Listener), "EventSource has a listener not on the global list."); - dispatcher = dispatcher.m_Next; - } - - // Every dispatcher is on Dispatcher List of every eventSource. - foreach (EventListener listener in allListeners.Keys) - { - dispatcher = eventSource.m_Dispatchers; - while (true) - { - Debug.Assert(dispatcher != null, "Listener is not on all eventSources."); - if (dispatcher.m_Listener == listener) - break; - dispatcher = dispatcher.m_Next; - } - } - } - } - } - - /// <summary> - /// Gets a global lock that is intended to protect the code:s_Listeners linked list and the - /// code:s_EventSources list. (We happen to use the s_EventSources list as the lock object) - /// </summary> - internal static object EventListenersLock - { - get - { - if (s_EventSources == null) - Interlocked.CompareExchange(ref s_EventSources, new List<WeakReference<EventSource>>(2), null); - return s_EventSources; - } - } - - private void CallBackForExistingEventSources(bool addToListenersList, EventHandler<EventSourceCreatedEventArgs>? callback) - { - lock (EventListenersLock) - { - Debug.Assert(s_EventSources != null); - - // Disallow creating EventListener reentrancy. - if (s_CreatingListener) - { - throw new InvalidOperationException(SR.EventSource_ListenerCreatedInsideCallback); - } - - try - { - s_CreatingListener = true; - - if (addToListenersList) - { - // Add to list of listeners in the system, do this BEFORE firing the 'OnEventSourceCreated' so that - // Those added sources see this listener. - this.m_Next = s_Listeners; - s_Listeners = this; - } - - if (callback != null) - { - // Find all existing eventSources call OnEventSourceCreated to 'catchup' - // Note that we DO have reentrancy here because 'AddListener' calls out to user code (via OnEventSourceCreated callback) - // We tolerate this by iterating over a copy of the list here. New event sources will take care of adding listeners themselves - // EventSources are not guaranteed to be added at the end of the s_EventSource list -- We re-use slots when a new source - // is created. - WeakReference<EventSource>[] eventSourcesSnapshot = s_EventSources.ToArray(); - -#if DEBUG - bool previousValue = s_ConnectingEventSourcesAndListener; - s_ConnectingEventSourcesAndListener = true; - try - { -#endif - for (int i = 0; i < eventSourcesSnapshot.Length; i++) - { - WeakReference<EventSource> eventSourceRef = eventSourcesSnapshot[i]; - if (eventSourceRef.TryGetTarget(out EventSource? eventSource)) - { - EventSourceCreatedEventArgs args = new EventSourceCreatedEventArgs(); - args.EventSource = eventSource; - callback(this, args); - } - } -#if DEBUG - } - finally - { - s_ConnectingEventSourcesAndListener = previousValue; - } -#endif - } - - Validate(); - } - finally - { - s_CreatingListener = false; - } - } - } - - // Instance fields - internal volatile EventListener? m_Next; // These form a linked list in s_Listeners - - // static fields - - /// <summary> - /// The list of all listeners in the appdomain. Listeners must be explicitly disposed to remove themselves - /// from this list. Note that EventSources point to their listener but NOT the reverse. - /// </summary> - internal static EventListener? s_Listeners; - /// <summary> - /// The list of all active eventSources in the appdomain. Note that eventSources do NOT - /// remove themselves from this list this is a weak list and the GC that removes them may - /// not have happened yet. Thus it can contain event sources that are dead (thus you have - /// to filter those out. - /// </summary> - internal static List<WeakReference<EventSource>>? s_EventSources; - - /// <summary> - /// Used to disallow reentrancy. - /// </summary> - private static bool s_CreatingListener = false; - -#if DEBUG - /// <summary> - /// Used to disable validation of EventSource and EventListener connectivity. - /// This is needed when an EventListener is in the middle of being published to all EventSources - /// and another EventSource is created as part of the process. - /// </summary> - [ThreadStatic] - private static bool s_ConnectingEventSourcesAndListener; -#endif - - /// <summary> - /// Used to register AD/Process shutdown callbacks. - /// </summary> - private static bool s_EventSourceShutdownRegistered = false; -#endregion - } - - /// <summary> - /// Passed to the code:EventSource.OnEventCommand callback - /// </summary> - public class EventCommandEventArgs : EventArgs - { - /// <summary> - /// Gets the command for the callback. - /// </summary> - public EventCommand Command { get; internal set; } - - /// <summary> - /// Gets the arguments for the callback. - /// </summary> - public IDictionary<string, string?>? Arguments { get; internal set; } - - /// <summary> - /// Enables the event that has the specified identifier. - /// </summary> - /// <param name="eventId">Event ID of event to be enabled</param> - /// <returns>true if eventId is in range</returns> - public bool EnableEvent(int eventId) - { - if (Command != EventCommand.Enable && Command != EventCommand.Disable) - throw new InvalidOperationException(); - return eventSource.EnableEventForDispatcher(dispatcher, eventProviderType, eventId, true); - } - - /// <summary> - /// Disables the event that have the specified identifier. - /// </summary> - /// <param name="eventId">Event ID of event to be disabled</param> - /// <returns>true if eventId is in range</returns> - public bool DisableEvent(int eventId) - { - if (Command != EventCommand.Enable && Command != EventCommand.Disable) - throw new InvalidOperationException(); - return eventSource.EnableEventForDispatcher(dispatcher, eventProviderType, eventId, false); - } - -#region private - - internal EventCommandEventArgs(EventCommand command, IDictionary<string, string?>? arguments, EventSource eventSource, - EventListener? listener, EventProviderType eventProviderType, int perEventSourceSessionId, int etwSessionId, bool enable, EventLevel level, EventKeywords matchAnyKeyword) - { - this.Command = command; - this.Arguments = arguments; - this.eventSource = eventSource; - this.listener = listener; - this.eventProviderType = eventProviderType; - this.perEventSourceSessionId = perEventSourceSessionId; - this.etwSessionId = etwSessionId; - this.enable = enable; - this.level = level; - this.matchAnyKeyword = matchAnyKeyword; - } - - internal EventSource eventSource; - internal EventDispatcher? dispatcher; - internal EventProviderType eventProviderType; - - // These are the arguments of sendCommand and are only used for deferring commands until after we are fully initialized. - internal EventListener? listener; - internal int perEventSourceSessionId; - internal int etwSessionId; - internal bool enable; - internal EventLevel level; - internal EventKeywords matchAnyKeyword; - internal EventCommandEventArgs? nextCommand; // We form a linked list of these deferred commands. - -#endregion - } - - /// <summary> - /// EventSourceCreatedEventArgs is passed to <see cref="EventListener.EventSourceCreated"/> - /// </summary> - public class EventSourceCreatedEventArgs : EventArgs - { - /// <summary> - /// The EventSource that is attaching to the listener. - /// </summary> - public EventSource? EventSource - { - get; - internal set; - } - } - - /// <summary> - /// EventWrittenEventArgs is passed to the user-provided override for - /// <see cref="EventListener.OnEventWritten"/> when an event is fired. - /// </summary> - public class EventWrittenEventArgs : EventArgs - { - /// <summary> - /// The name of the event. - /// </summary> - public string? EventName - { - get - { - if (m_eventName != null || EventId < 0) // TraceLogging convention EventID == -1 - { - return m_eventName; - } - else - { - Debug.Assert(m_eventSource.m_eventData != null); - return m_eventSource.m_eventData[EventId].Name; - } - } - internal set => m_eventName = value; - } - - /// <summary> - /// Gets the event ID for the event that was written. - /// </summary> - public int EventId { get; internal set; } - - /// <summary> - /// Gets the activity ID for the thread on which the event was written. - /// </summary> - public Guid ActivityId - { - get - { - Guid activityId = m_activityId; - if (activityId == Guid.Empty) - { - activityId = EventSource.CurrentThreadActivityId; - } - - return activityId; - } - internal set => m_activityId = value; - } - - /// <summary> - /// Gets the related activity ID if one was specified when the event was written. - /// </summary> - public Guid RelatedActivityId - { - get; - internal set; - } - - /// <summary> - /// Gets the payload for the event. - /// </summary> - public ReadOnlyCollection<object?>? Payload { get; internal set; } - - /// <summary> - /// Gets the payload argument names. - /// </summary> - public ReadOnlyCollection<string>? PayloadNames - { - get - { - // For contract based events we create the list lazily. - // You can have m_payloadNames be null in the TraceLogging case (EventID < 0) so only - // do the lazy init if you know it is contract based (EventID >= 0) - if (EventId >= 0 && m_payloadNames == null) - { - var names = new List<string>(); - Debug.Assert(m_eventSource.m_eventData != null); - foreach (ParameterInfo parameter in m_eventSource.m_eventData[EventId].Parameters) - { - names.Add(parameter.Name!); - } - - m_payloadNames = new ReadOnlyCollection<string>(names); - } - - return m_payloadNames; - } - - internal set => m_payloadNames = value; - } - - /// <summary> - /// Gets the event source object. - /// </summary> - public EventSource EventSource => m_eventSource; - - /// <summary> - /// Gets the keywords for the event. - /// </summary> - public EventKeywords Keywords - { - get - { - if (EventId < 0) // TraceLogging convention EventID == -1 - return m_keywords; - - Debug.Assert(m_eventSource.m_eventData != null); - return (EventKeywords)m_eventSource.m_eventData[EventId].Descriptor.Keywords; - } - } - - /// <summary> - /// Gets the operation code for the event. - /// </summary> - public EventOpcode Opcode - { - get - { - if (EventId <= 0) // TraceLogging convention EventID == -1 - return m_opcode; - - Debug.Assert(m_eventSource.m_eventData != null); - return (EventOpcode)m_eventSource.m_eventData[EventId].Descriptor.Opcode; - } - } - - /// <summary> - /// Gets the task for the event. - /// </summary> - public EventTask Task - { - get - { - if (EventId <= 0) // TraceLogging convention EventID == -1 - return EventTask.None; - - Debug.Assert(m_eventSource.m_eventData != null); - return (EventTask)m_eventSource.m_eventData[EventId].Descriptor.Task; - } - } - - /// <summary> - /// Any provider/user defined options associated with the event. - /// </summary> - public EventTags Tags - { - get - { - if (EventId <= 0) // TraceLogging convention EventID == -1 - return m_tags; - - Debug.Assert(m_eventSource.m_eventData != null); - return m_eventSource.m_eventData[EventId].Tags; - } - } - - /// <summary> - /// Gets the message for the event. If the message has {N} parameters they are NOT substituted. - /// </summary> - public string? Message - { - get - { - if (EventId <= 0) // TraceLogging convention EventID == -1 - { - return m_message; - } - else - { - Debug.Assert(m_eventSource.m_eventData != null); - return m_eventSource.m_eventData[EventId].Message; - } - } - internal set => m_message = value; - } - -#if FEATURE_MANAGED_ETW_CHANNELS - /// <summary> - /// Gets the channel for the event. - /// </summary> - public EventChannel Channel - { - get - { - if (EventId <= 0) // TraceLogging convention EventID == -1 - return EventChannel.None; - - Debug.Assert(m_eventSource.m_eventData != null); - return (EventChannel)m_eventSource.m_eventData[EventId].Descriptor.Channel; - } - } -#endif - - /// <summary> - /// Gets the version of the event. - /// </summary> - public byte Version - { - get - { - if (EventId <= 0) // TraceLogging convention EventID == -1 - return 0; - - Debug.Assert(m_eventSource.m_eventData != null); - return m_eventSource.m_eventData[EventId].Descriptor.Version; - } - } - - /// <summary> - /// Gets the level for the event. - /// </summary> - public EventLevel Level - { - get - { - if (EventId <= 0) // TraceLogging convention EventID == -1 - return m_level; - - Debug.Assert(m_eventSource.m_eventData != null); - return (EventLevel)m_eventSource.m_eventData[EventId].Descriptor.Level; - } - } - - /// <summary> - /// Gets the identifier for the OS thread that wrote the event. - /// </summary> - public long OSThreadId - { - get - { - if (!m_osThreadId.HasValue) - { -#if ES_BUILD_STANDALONE - m_osThreadId = (long)Interop.Kernel32.GetCurrentThreadId(); -#else - m_osThreadId = (long)Thread.CurrentOSThreadId; -#endif - } - - return m_osThreadId.Value; - } - internal set => m_osThreadId = value; - } - - /// <summary> - /// Gets a UTC DateTime that specifies when the event was written. - /// </summary> - public DateTime TimeStamp - { - get; - internal set; - } - -#region private - internal EventWrittenEventArgs(EventSource eventSource) - { - m_eventSource = eventSource; - TimeStamp = DateTime.UtcNow; - } - private string? m_message; - private string? m_eventName; - private readonly EventSource m_eventSource; - private ReadOnlyCollection<string>? m_payloadNames; - private Guid m_activityId; - private long? m_osThreadId; - internal EventTags m_tags; - internal EventOpcode m_opcode; - internal EventLevel m_level; - internal EventKeywords m_keywords; -#endregion - } - - /// <summary> - /// Allows customizing defaults and specifying localization support for the event source class to which it is applied. - /// </summary> - [AttributeUsage(AttributeTargets.Class)] - public sealed class EventSourceAttribute : Attribute - { - /// <summary> - /// Overrides the ETW name of the event source (which defaults to the class name) - /// </summary> - public string? Name { get; set; } - - /// <summary> - /// Overrides the default (calculated) Guid of an EventSource type. Explicitly defining a GUID is discouraged, - /// except when upgrading existing ETW providers to using event sources. - /// </summary> - public string? Guid { get; set; } - - /// <summary> - /// <para> - /// EventSources support localization of events. The names used for events, opcodes, tasks, keywords and maps - /// can be localized to several languages if desired. This works by creating a ResX style string table - /// (by simply adding a 'Resource File' to your project). This resource file is given a name e.g. - /// 'DefaultNameSpace.ResourceFileName' which can be passed to the ResourceManager constructor to read the - /// resources. This name is the value of the LocalizationResources property. - /// </para><para> - /// If LocalizationResources property is non-null, then EventSource will look up the localized strings for events by - /// using the following resource naming scheme - /// </para> - /// <para>* event_EVENTNAME</para> - /// <para>* task_TASKNAME</para> - /// <para>* keyword_KEYWORDNAME</para> - /// <para>* map_MAPNAME</para> - /// <para> - /// where the capitalized name is the name of the event, task, keyword, or map value that should be localized. - /// Note that the localized string for an event corresponds to the Message string, and can have {0} values - /// which represent the payload values. - /// </para> - /// </summary> - public string? LocalizationResources { get; set; } - } - - /// <summary> - /// Any instance methods in a class that subclasses <see cref="EventSource"/> and that return void are - /// assumed by default to be methods that generate an ETW event. Enough information can be deduced from the - /// name of the method and its signature to generate basic schema information for the event. The - /// <see cref="EventAttribute"/> class allows you to specify additional event schema information for an event if - /// desired. - /// </summary> - [AttributeUsage(AttributeTargets.Method)] - public sealed class EventAttribute : Attribute - { - /// <summary>Construct an EventAttribute with specified eventId</summary> - /// <param name="eventId">ID of the ETW event (an integer between 1 and 65535)</param> - public EventAttribute(int eventId) { this.EventId = eventId; Level = EventLevel.Informational; this.m_opcodeSet = false; } - /// <summary>Event's ID</summary> - public int EventId { get; private set; } - /// <summary>Event's severity level: indicates the severity or verbosity of the event</summary> - public EventLevel Level { get; set; } - /// <summary>Event's keywords: allows classification of events by "categories"</summary> - public EventKeywords Keywords { get; set; } - /// <summary>Event's operation code: allows defining operations, generally used with Tasks</summary> - public EventOpcode Opcode - { - get => m_opcode; - set - { - this.m_opcode = value; - this.m_opcodeSet = true; - } - } - - internal bool IsOpcodeSet => m_opcodeSet; - - /// <summary>Event's task: allows logical grouping of events</summary> - public EventTask Task { get; set; } -#if FEATURE_MANAGED_ETW_CHANNELS - /// <summary>Event's channel: defines an event log as an additional destination for the event</summary> - public EventChannel Channel { get; set; } -#endif - /// <summary>Event's version</summary> - public byte Version { get; set; } - - /// <summary> - /// This can be specified to enable formatting and localization of the event's payload. You can - /// use standard .NET substitution operators (eg {1}) in the string and they will be replaced - /// with the 'ToString()' of the corresponding part of the event payload. - /// </summary> - public string? Message { get; set; } - - /// <summary> - /// User defined options associated with the event. These do not have meaning to the EventSource but - /// are passed through to listeners which given them semantics. - /// </summary> - public EventTags Tags { get; set; } - - /// <summary> - /// Allows fine control over the Activity IDs generated by start and stop events - /// </summary> - public EventActivityOptions ActivityOptions { get; set; } - -#region private - private EventOpcode m_opcode; - private bool m_opcodeSet; -#endregion - } - - /// <summary> - /// By default all instance methods in a class that subclasses code:EventSource that and return - /// void are assumed to be methods that generate an event. This default can be overridden by specifying - /// the code:NonEventAttribute - /// </summary> - [AttributeUsage(AttributeTargets.Method)] - public sealed class NonEventAttribute : Attribute - { - /// <summary> - /// Constructs a default NonEventAttribute - /// </summary> - public NonEventAttribute() { } - } - - // FUTURE we may want to expose this at some point once we have a partner that can help us validate the design. -#if FEATURE_MANAGED_ETW_CHANNELS - /// <summary> - /// EventChannelAttribute allows customizing channels supported by an EventSource. This attribute must be - /// applied to an member of type EventChannel defined in a Channels class nested in the EventSource class: - /// <code> - /// public static class Channels - /// { - /// [Channel(Enabled = true, EventChannelType = EventChannelType.Admin)] - /// public const EventChannel Admin = (EventChannel)16; - /// - /// [Channel(Enabled = false, EventChannelType = EventChannelType.Operational)] - /// public const EventChannel Operational = (EventChannel)17; - /// } - /// </code> - /// </summary> - [AttributeUsage(AttributeTargets.Field)] -#if FEATURE_ADVANCED_MANAGED_ETW_CHANNELS - public -#else - internal -#endif - class EventChannelAttribute : Attribute - { - /// <summary> - /// Specified whether the channel is enabled by default - /// </summary> - public bool Enabled { get; set; } - - /// <summary> - /// Legal values are in EventChannelType - /// </summary> - public EventChannelType EventChannelType { get; set; } - -#if FEATURE_ADVANCED_MANAGED_ETW_CHANNELS - /// <summary> - /// Specifies the isolation for the channel - /// </summary> - public EventChannelIsolation Isolation { get; set; } - - /// <summary> - /// Specifies an SDDL access descriptor that controls access to the log file that backs the channel. - /// See MSDN (https://docs.microsoft.com/en-us/windows/desktop/WES/eventmanifestschema-channeltype-complextype) for details. - /// </summary> - public string? Access { get; set; } - - /// <summary> - /// Allows importing channels defined in external manifests - /// </summary> - public string? ImportChannel { get; set; } -#endif - - // TODO: there is a convention that the name is the Provider/Type Should we provide an override? - // public string Name { get; set; } - } - - /// <summary> - /// Allowed channel types - /// </summary> -#if FEATURE_ADVANCED_MANAGED_ETW_CHANNELS - public -#else - internal -#endif - enum EventChannelType - { - /// <summary>The admin channel</summary> - Admin = 1, - /// <summary>The operational channel</summary> - Operational, - /// <summary>The Analytic channel</summary> - Analytic, - /// <summary>The debug channel</summary> - Debug, - } - -#if FEATURE_ADVANCED_MANAGED_ETW_CHANNELS - /// <summary> - /// Allowed isolation levels. See MSDN (https://docs.microsoft.com/en-us/windows/desktop/WES/eventmanifestschema-channeltype-complextype) - /// for the default permissions associated with each level. EventChannelIsolation and Access allows control over the - /// access permissions for the channel and backing file. - /// </summary> - public - enum EventChannelIsolation - { - /// <summary> - /// This is the default isolation level. All channels that specify Application isolation use the same ETW session - /// </summary> - Application = 1, - /// <summary> - /// All channels that specify System isolation use the same ETW session - /// </summary> - System, - /// <summary> - /// Use sparingly! When specifying Custom isolation, a separate ETW session is created for the channel. - /// Using Custom isolation lets you control the access permissions for the channel and backing file. - /// Because there are only 64 ETW sessions available, you should limit your use of Custom isolation. - /// </summary> - Custom, - } -#endif -#endif - - /// <summary> - /// Describes the pre-defined command (EventCommandEventArgs.Command property) that is passed to the OnEventCommand callback. - /// </summary> - public enum EventCommand - { - /// <summary> - /// Update EventSource state - /// </summary> - Update = 0, - /// <summary> - /// Request EventSource to generate and send its manifest - /// </summary> - SendManifest = -1, - /// <summary> - /// Enable event - /// </summary> - Enable = -2, - /// <summary> - /// Disable event - /// </summary> - Disable = -3 - } - -#region private classes - - // holds a bitfield representing a session mask - /// <summary> - /// A SessionMask represents a set of (at most MAX) sessions as a bit mask. The perEventSourceSessionId - /// is the index in the SessionMask of the bit that will be set. These can translate to - /// EventSource's reserved keywords bits using the provided ToEventKeywords() and - /// FromEventKeywords() methods. - /// </summary> - internal struct SessionMask - { - public SessionMask(SessionMask m) - { m_mask = m.m_mask; } - - public SessionMask(uint mask = 0) - { m_mask = mask & MASK; } - - public bool IsEqualOrSupersetOf(SessionMask m) - { - return (this.m_mask | m.m_mask) == this.m_mask; - } - - public static SessionMask All => new SessionMask(MASK); - - public static SessionMask FromId(int perEventSourceSessionId) - { - Debug.Assert(perEventSourceSessionId < MAX); - return new SessionMask((uint)1 << perEventSourceSessionId); - } - - public ulong ToEventKeywords() - { - return (ulong)m_mask << SHIFT_SESSION_TO_KEYWORD; - } - - public static SessionMask FromEventKeywords(ulong m) - { - return new SessionMask((uint)(m >> SHIFT_SESSION_TO_KEYWORD)); - } - - public bool this[int perEventSourceSessionId] - { - get - { - Debug.Assert(perEventSourceSessionId < MAX); - return (m_mask & (1 << perEventSourceSessionId)) != 0; - } - set - { - Debug.Assert(perEventSourceSessionId < MAX); - if (value) m_mask |= ((uint)1 << perEventSourceSessionId); - else m_mask &= ~((uint)1 << perEventSourceSessionId); - } - } - - public static SessionMask operator |(SessionMask m1, SessionMask m2) => - new SessionMask(m1.m_mask | m2.m_mask); - - public static SessionMask operator &(SessionMask m1, SessionMask m2) => - new SessionMask(m1.m_mask & m2.m_mask); - - public static SessionMask operator ^(SessionMask m1, SessionMask m2) => - new SessionMask(m1.m_mask ^ m2.m_mask); - - public static SessionMask operator ~(SessionMask m) => - new SessionMask(MASK & ~(m.m_mask)); - - public static explicit operator ulong(SessionMask m) => m.m_mask; - - public static explicit operator uint(SessionMask m) => m.m_mask; - - private uint m_mask; - - internal const int SHIFT_SESSION_TO_KEYWORD = 44; // bits 44-47 inclusive are reserved - internal const uint MASK = 0x0fU; // the mask of 4 reserved bits - internal const uint MAX = 4; // maximum number of simultaneous ETW sessions supported - } - - /// <summary> - /// code:EventDispatchers are a simple 'helper' structure that holds the filtering state - /// (m_EventEnabled) for a particular EventSource X EventListener tuple - /// - /// Thus a single EventListener may have many EventDispatchers (one for every EventSource - /// that EventListener has activate) and a Single EventSource may also have many - /// event Dispatchers (one for every EventListener that has activated it). - /// - /// Logically a particular EventDispatcher belongs to exactly one EventSource and exactly - /// one EventListener (although EventDispatcher does not 'remember' the EventSource it is - /// associated with. - /// </summary> - internal class EventDispatcher - { - internal EventDispatcher(EventDispatcher? next, bool[]? eventEnabled, EventListener listener) - { - m_Next = next; - m_EventEnabled = eventEnabled; - m_Listener = listener; - } - - // Instance fields - internal readonly EventListener m_Listener; // The dispatcher this entry is for - internal bool[]? m_EventEnabled; // For every event in a the eventSource, is it enabled? - - // Only guaranteed to exist after a InsureInit() - internal EventDispatcher? m_Next; // These form a linked list in code:EventSource.m_Dispatchers - // Of all listeners for that eventSource. - } - - /// <summary> - /// Flags that can be used with EventSource.GenerateManifest to control how the ETW manifest for the EventSource is - /// generated. - /// </summary> - [Flags] - public enum EventManifestOptions - { - /// <summary> - /// Only the resources associated with current UI culture are included in the manifest - /// </summary> - None = 0x0, - /// <summary> - /// Throw exceptions for any inconsistency encountered - /// </summary> - Strict = 0x1, - /// <summary> - /// Generate a "resources" node under "localization" for every satellite assembly provided - /// </summary> - AllCultures = 0x2, - /// <summary> - /// Generate the manifest only if the event source needs to be registered on the machine, - /// otherwise return null (but still perform validation if Strict is specified) - /// </summary> - OnlyIfNeededForRegistration = 0x4, - /// <summary> - /// When generating the manifest do *not* enforce the rule that the current EventSource class - /// must be the base class for the user-defined type passed in. This allows validation of .net - /// event sources using the new validation code - /// </summary> - AllowEventSourceOverride = 0x8, - } - - /// <summary> - /// ManifestBuilder is designed to isolate the details of the message of the event from the - /// rest of EventSource. This one happens to create XML. - /// </summary> - internal class ManifestBuilder - { - /// <summary> - /// Build a manifest for 'providerName' with the given GUID, which will be packaged into 'dllName'. - /// 'resources, is a resource manager. If specified all messages are localized using that manager. - /// </summary> - public ManifestBuilder(string providerName, Guid providerGuid, string? dllName, ResourceManager? resources, - EventManifestOptions flags) - { -#if FEATURE_MANAGED_ETW_CHANNELS - this.providerName = providerName; -#endif - this.flags = flags; - - this.resources = resources; - sb = new StringBuilder(); - events = new StringBuilder(); - templates = new StringBuilder(); - opcodeTab = new Dictionary<int, string>(); - stringTab = new Dictionary<string, string>(); - errors = new List<string>(); - perEventByteArrayArgIndices = new Dictionary<string, List<int>>(); - - sb.AppendLine("<instrumentationManifest xmlns=\"http://schemas.microsoft.com/win/2004/08/events\">"); - sb.AppendLine(" <instrumentation xmlns:xs=\"http://www.w3.org/2001/XMLSchema\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:win=\"http://manifests.microsoft.com/win/2004/08/windows/events\">"); - sb.AppendLine(" <events xmlns=\"http://schemas.microsoft.com/win/2004/08/events\">"); - sb.Append("<provider name=\"").Append(providerName). - Append("\" guid=\"{").Append(providerGuid.ToString()).Append("}"); - if (dllName != null) - sb.Append("\" resourceFileName=\"").Append(dllName).Append("\" messageFileName=\"").Append(dllName); - - string symbolsName = providerName.Replace("-", "").Replace('.', '_'); // Period and - are illegal replace them. - sb.Append("\" symbol=\"").Append(symbolsName); - sb.AppendLine("\">"); - } - - public void AddOpcode(string name, int value) - { - if ((flags & EventManifestOptions.Strict) != 0) - { - if (value <= 10 || value >= 239) - { - ManifestError(SR.Format(SR.EventSource_IllegalOpcodeValue, name, value)); - } - - if (opcodeTab.TryGetValue(value, out string? prevName) && !name.Equals(prevName, StringComparison.Ordinal)) - { - ManifestError(SR.Format(SR.EventSource_OpcodeCollision, name, prevName, value)); - } - } - - opcodeTab[value] = name; - } - - public void AddTask(string name, int value) - { - if ((flags & EventManifestOptions.Strict) != 0) - { - if (value <= 0 || value >= 65535) - { - ManifestError(SR.Format(SR.EventSource_IllegalTaskValue, name, value)); - } - - if (taskTab != null && taskTab.TryGetValue(value, out string? prevName) && !name.Equals(prevName, StringComparison.Ordinal)) - { - ManifestError(SR.Format(SR.EventSource_TaskCollision, name, prevName, value)); - } - } - - taskTab ??= new Dictionary<int, string>(); - taskTab[value] = name; - } - - public void AddKeyword(string name, ulong value) - { - if ((value & (value - 1)) != 0) // Is it a power of 2? - { - ManifestError(SR.Format(SR.EventSource_KeywordNeedPowerOfTwo, "0x" + value.ToString("x", CultureInfo.CurrentCulture), name), true); - } - if ((flags & EventManifestOptions.Strict) != 0) - { - if (value >= 0x0000100000000000UL && !name.StartsWith("Session", StringComparison.Ordinal)) - { - ManifestError(SR.Format(SR.EventSource_IllegalKeywordsValue, name, "0x" + value.ToString("x", CultureInfo.CurrentCulture))); - } - - if (keywordTab != null && keywordTab.TryGetValue(value, out string? prevName) && !name.Equals(prevName, StringComparison.Ordinal)) - { - ManifestError(SR.Format(SR.EventSource_KeywordCollision, name, prevName, "0x" + value.ToString("x", CultureInfo.CurrentCulture))); - } - } - - keywordTab ??= new Dictionary<ulong, string>(); - keywordTab[value] = name; - } - -#if FEATURE_MANAGED_ETW_CHANNELS - /// <summary> - /// Add a channel. channelAttribute can be null - /// </summary> - public void AddChannel(string? name, int value, EventChannelAttribute? channelAttribute) - { - EventChannel chValue = (EventChannel)value; - if (value < (int)EventChannel.Admin || value > 255) - ManifestError(SR.Format(SR.EventSource_EventChannelOutOfRange, name, value)); - else if (chValue >= EventChannel.Admin && chValue <= EventChannel.Debug && - channelAttribute != null && EventChannelToChannelType(chValue) != channelAttribute.EventChannelType) - { - // we want to ensure developers do not define EventChannels that conflict with the builtin ones, - // but we want to allow them to override the default ones... - ManifestError(SR.Format(SR.EventSource_ChannelTypeDoesNotMatchEventChannelValue, - name, ((EventChannel)value).ToString())); - } - - // TODO: validate there are no conflicting manifest exposed names (generally following the format "provider/type") - - ulong kwd = GetChannelKeyword(chValue); - - channelTab ??= new Dictionary<int, ChannelInfo>(4); - channelTab[value] = new ChannelInfo { Name = name, Keywords = kwd, Attribs = channelAttribute }; - } - - private static EventChannelType EventChannelToChannelType(EventChannel channel) - { -#if !ES_BUILD_STANDALONE - Debug.Assert(channel >= EventChannel.Admin && channel <= EventChannel.Debug); -#endif - return (EventChannelType)((int)channel - (int)EventChannel.Admin + (int)EventChannelType.Admin); - } - - private static EventChannelAttribute GetDefaultChannelAttribute(EventChannel channel) - { - EventChannelAttribute attrib = new EventChannelAttribute(); - attrib.EventChannelType = EventChannelToChannelType(channel); - if (attrib.EventChannelType <= EventChannelType.Operational) - attrib.Enabled = true; - return attrib; - } - - public ulong[] GetChannelData() - { - if (this.channelTab == null) - { - return Array.Empty<ulong>(); - } - - // We create an array indexed by the channel id for fast look up. - // E.g. channelMask[Admin] will give you the bit mask for Admin channel. - int maxkey = -1; - foreach (int item in this.channelTab.Keys) - { - if (item > maxkey) - { - maxkey = item; - } - } - - ulong[] channelMask = new ulong[maxkey + 1]; - foreach (KeyValuePair<int, ChannelInfo> item in this.channelTab) - { - channelMask[item.Key] = item.Value.Keywords; - } - - return channelMask; - } - -#endif - public void StartEvent(string eventName, EventAttribute eventAttribute) - { - Debug.Assert(numParams == 0); - Debug.Assert(this.eventName == null); - this.eventName = eventName; - numParams = 0; - byteArrArgIndices = null; - - events.Append(" <event"). - Append(" value=\"").Append(eventAttribute.EventId).Append("\""). - Append(" version=\"").Append(eventAttribute.Version).Append("\""). - Append(" level=\"").Append(GetLevelName(eventAttribute.Level)).Append("\""). - Append(" symbol=\"").Append(eventName).Append("\""); - - // at this point we add to the manifest's stringTab a message that is as-of-yet - // "untranslated to manifest convention", b/c we don't have the number or position - // of any byte[] args (which require string format index updates) - WriteMessageAttrib(events, "event", eventName, eventAttribute.Message); - - if (eventAttribute.Keywords != 0) - events.Append(" keywords=\"").Append(GetKeywords((ulong)eventAttribute.Keywords, eventName)).Append("\""); - if (eventAttribute.Opcode != 0) - events.Append(" opcode=\"").Append(GetOpcodeName(eventAttribute.Opcode, eventName)).Append("\""); - if (eventAttribute.Task != 0) - events.Append(" task=\"").Append(GetTaskName(eventAttribute.Task, eventName)).Append("\""); -#if FEATURE_MANAGED_ETW_CHANNELS - if (eventAttribute.Channel != 0) - { - events.Append(" channel=\"").Append(GetChannelName(eventAttribute.Channel, eventName, eventAttribute.Message)).Append("\""); - } -#endif - } - - public void AddEventParameter(Type type, string name) - { - if (numParams == 0) - templates.Append(" <template tid=\"").Append(eventName).AppendLine("Args\">"); - if (type == typeof(byte[])) - { - // mark this index as "extraneous" (it has no parallel in the managed signature) - // we use these values in TranslateToManifestConvention() - byteArrArgIndices ??= new List<int>(4); - byteArrArgIndices.Add(numParams); - - // add an extra field to the template representing the length of the binary blob - numParams++; - templates.Append(" <data name=\"").Append(name).AppendLine("Size\" inType=\"win:UInt32\"/>"); - } - numParams++; - templates.Append(" <data name=\"").Append(name).Append("\" inType=\"").Append(GetTypeName(type)).Append("\""); - // TODO: for 'byte*' types it assumes the user provided length is named using the same naming convention - // as for 'byte[]' args (blob_arg_name + "Size") - if ((type.IsArray || type.IsPointer) && type.GetElementType() == typeof(byte)) - { - // add "length" attribute to the "blob" field in the template (referencing the field added above) - templates.Append(" length=\"").Append(name).Append("Size\""); - } - // ETW does not support 64-bit value maps, so we don't specify these as ETW maps - if (type.IsEnum() && Enum.GetUnderlyingType(type) != typeof(ulong) && Enum.GetUnderlyingType(type) != typeof(long)) - { - templates.Append(" map=\"").Append(type.Name).Append("\""); - mapsTab ??= new Dictionary<string, Type>(); - if (!mapsTab.ContainsKey(type.Name)) - mapsTab.Add(type.Name, type); // Remember that we need to dump the type enumeration - } - - templates.AppendLine("/>"); - } - public void EndEvent() - { - Debug.Assert(eventName != null); - - if (numParams > 0) - { - templates.AppendLine(" </template>"); - events.Append(" template=\"").Append(eventName).Append("Args\""); - } - events.AppendLine("/>"); - - if (byteArrArgIndices != null) - perEventByteArrayArgIndices[eventName] = byteArrArgIndices; - - // at this point we have all the information we need to translate the C# Message - // to the manifest string we'll put in the stringTab - if (stringTab.TryGetValue("event_" + eventName, out string? msg)) - { - msg = TranslateToManifestConvention(msg, eventName); - stringTab["event_" + eventName] = msg; - } - - eventName = null; - numParams = 0; - byteArrArgIndices = null; - } - -#if FEATURE_MANAGED_ETW_CHANNELS - // Channel keywords are generated one per channel to allow channel based filtering in event viewer. These keywords are autogenerated - // by mc.exe for compiling a manifest and are based on the order of the channels (fields) in the Channels inner class (when advanced - // channel support is enabled), or based on the order the predefined channels appear in the EventAttribute properties (for simple - // support). The manifest generated *MUST* have the channels specified in the same order (that's how our computed keywords are mapped - // to channels by the OS infrastructure). - // If channelKeyworkds is present, and has keywords bits in the ValidPredefinedChannelKeywords then it is - // assumed that the keyword for that channel should be that bit. - // otherwise we allocate a channel bit for the channel. - // explicit channel bits are only used by WCF to mimic an existing manifest, - // so we don't dont do error checking. - public ulong GetChannelKeyword(EventChannel channel, ulong channelKeyword = 0) - { - // strip off any non-channel keywords, since we are only interested in channels here. - channelKeyword &= ValidPredefinedChannelKeywords; - channelTab ??= new Dictionary<int, ChannelInfo>(4); - - if (channelTab.Count == MaxCountChannels) - ManifestError(SR.EventSource_MaxChannelExceeded); - - ChannelInfo? info; - if (!channelTab.TryGetValue((int)channel, out info)) - { - // If we were not given an explicit channel, allocate one. - if (channelKeyword != 0) - { - channelKeyword = nextChannelKeywordBit; - nextChannelKeywordBit >>= 1; - } - } - else - { - channelKeyword = info.Keywords; - } - - return channelKeyword; - } -#endif - - public byte[] CreateManifest() - { - string str = CreateManifestString(); - return Encoding.UTF8.GetBytes(str); - } - - public IList<string> Errors => errors; - - /// <summary> - /// When validating an event source it adds the error to the error collection. - /// When not validating it throws an exception if runtimeCritical is "true". - /// Otherwise the error is ignored. - /// </summary> - /// <param name="msg"></param> - /// <param name="runtimeCritical"></param> - public void ManifestError(string msg, bool runtimeCritical = false) - { - if ((flags & EventManifestOptions.Strict) != 0) - errors.Add(msg); - else if (runtimeCritical) - throw new ArgumentException(msg); - } - - private string CreateManifestString() - { -#if FEATURE_MANAGED_ETW_CHANNELS - // Write out the channels - if (channelTab != null) - { - sb.AppendLine(" <channels>"); - var sortedChannels = new List<KeyValuePair<int, ChannelInfo>>(); - foreach (KeyValuePair<int, ChannelInfo> p in channelTab) { sortedChannels.Add(p); } - sortedChannels.Sort((p1, p2) => -Comparer<ulong>.Default.Compare(p1.Value.Keywords, p2.Value.Keywords)); - foreach (KeyValuePair<int, ChannelInfo> kvpair in sortedChannels) - { - int channel = kvpair.Key; - ChannelInfo channelInfo = kvpair.Value; - - string? channelType = null; - const string ElementName = "channel"; - bool enabled = false; - string? fullName = null; -#if FEATURE_ADVANCED_MANAGED_ETW_CHANNELS - string? isolation = null; - string? access = null; -#endif - if (channelInfo.Attribs != null) - { - EventChannelAttribute attribs = channelInfo.Attribs; - if (Enum.IsDefined(typeof(EventChannelType), attribs.EventChannelType)) - channelType = attribs.EventChannelType.ToString(); - enabled = attribs.Enabled; -#if FEATURE_ADVANCED_MANAGED_ETW_CHANNELS - if (attribs.ImportChannel != null) - { - fullName = attribs.ImportChannel; - elementName = "importChannel"; - } - if (Enum.IsDefined(typeof(EventChannelIsolation), attribs.Isolation)) - isolation = attribs.Isolation.ToString(); - access = attribs.Access; -#endif - } - - fullName ??= providerName + "/" + channelInfo.Name; - - sb.Append(" <").Append(ElementName); - sb.Append(" chid=\"").Append(channelInfo.Name).Append("\""); - sb.Append(" name=\"").Append(fullName).Append("\""); - if (ElementName == "channel") // not applicable to importChannels. - { - Debug.Assert(channelInfo.Name != null); - WriteMessageAttrib(sb, "channel", channelInfo.Name, null); - sb.Append(" value=\"").Append(channel).Append("\""); - if (channelType != null) - sb.Append(" type=\"").Append(channelType).Append("\""); - sb.Append(" enabled=\"").Append(enabled ? "true" : "false").Append("\""); -#if FEATURE_ADVANCED_MANAGED_ETW_CHANNELS - if (access != null) - sb.Append(" access=\"").Append(access).Append("\""); - if (isolation != null) - sb.Append(" isolation=\"").Append(isolation).Append("\""); -#endif - } - sb.AppendLine("/>"); - } - sb.AppendLine(" </channels>"); - } -#endif - - // Write out the tasks - if (taskTab != null) - { - sb.AppendLine(" <tasks>"); - var sortedTasks = new List<int>(taskTab.Keys); - sortedTasks.Sort(); - foreach (int task in sortedTasks) - { - sb.Append(" <task"); - WriteNameAndMessageAttribs(sb, "task", taskTab[task]); - sb.Append(" value=\"").Append(task).AppendLine("\"/>"); - } - sb.AppendLine(" </tasks>"); - } - - // Write out the maps - if (mapsTab != null) - { - sb.AppendLine(" <maps>"); - foreach (Type enumType in mapsTab.Values) - { - bool isbitmap = EventSource.GetCustomAttributeHelper(enumType, typeof(FlagsAttribute), flags) != null; - string mapKind = isbitmap ? "bitMap" : "valueMap"; - sb.Append(" <").Append(mapKind).Append(" name=\"").Append(enumType.Name).AppendLine("\">"); - - // write out each enum value - FieldInfo[] staticFields = enumType.GetFields(BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.Static); - bool anyValuesWritten = false; - foreach (FieldInfo staticField in staticFields) - { - object? constantValObj = staticField.GetRawConstantValue(); - - if (constantValObj != null) - { - ulong hexValue; - if (constantValObj is ulong) - hexValue = (ulong)constantValObj; // This is the only integer type that can't be represented by a long. - else - hexValue = (ulong)Convert.ToInt64(constantValObj); // Handles all integer types except ulong. - - // ETW requires all bitmap values to be powers of 2. Skip the ones that are not. - // TODO: Warn people about the dropping of values. - if (isbitmap && ((hexValue & (hexValue - 1)) != 0 || hexValue == 0)) - continue; - sb.Append(" <map value=\"0x").Append(hexValue.ToString("x", CultureInfo.InvariantCulture)).Append("\""); - WriteMessageAttrib(sb, "map", enumType.Name + "." + staticField.Name, staticField.Name); - sb.AppendLine("/>"); - anyValuesWritten = true; - } - } - - // the OS requires that bitmaps and valuemaps have at least one value or it reject the whole manifest. - // To avoid that put a 'None' entry if there are no other values. - if (!anyValuesWritten) - { - sb.Append(" <map value=\"0x0\""); - WriteMessageAttrib(sb, "map", enumType.Name + ".None", "None"); - sb.AppendLine("/>"); - } - sb.Append(" </").Append(mapKind).AppendLine(">"); - } - sb.AppendLine(" </maps>"); - } - - // Write out the opcodes - sb.AppendLine(" <opcodes>"); - var sortedOpcodes = new List<int>(opcodeTab.Keys); - sortedOpcodes.Sort(); - foreach (int opcode in sortedOpcodes) - { - sb.Append(" <opcode"); - WriteNameAndMessageAttribs(sb, "opcode", opcodeTab[opcode]); - sb.Append(" value=\"").Append(opcode).AppendLine("\"/>"); - } - sb.AppendLine(" </opcodes>"); - - // Write out the keywords - if (keywordTab != null) - { - sb.AppendLine(" <keywords>"); - var sortedKeywords = new List<ulong>(keywordTab.Keys); - sortedKeywords.Sort(); - foreach (ulong keyword in sortedKeywords) - { - sb.Append(" <keyword"); - WriteNameAndMessageAttribs(sb, "keyword", keywordTab[keyword]); - sb.Append(" mask=\"0x").Append(keyword.ToString("x", CultureInfo.InvariantCulture)).AppendLine("\"/>"); - } - sb.AppendLine(" </keywords>"); - } - - sb.AppendLine(" <events>"); - sb.Append(events); - sb.AppendLine(" </events>"); - - sb.AppendLine(" <templates>"); - if (templates.Length > 0) - { - sb.Append(templates); - } - else - { - // Work around a cornercase ETW issue where a manifest with no templates causes - // ETW events to not get sent to their associated channel. - sb.AppendLine(" <template tid=\"_empty\"></template>"); - } - sb.AppendLine(" </templates>"); - - sb.AppendLine("</provider>"); - sb.AppendLine("</events>"); - sb.AppendLine("</instrumentation>"); - - // Output the localization information. - sb.AppendLine("<localization>"); - - List<CultureInfo> cultures = (resources != null && (flags & EventManifestOptions.AllCultures) != 0) ? - GetSupportedCultures() : - new List<CultureInfo>() { CultureInfo.CurrentUICulture }; - - var sortedStrings = new string[stringTab.Keys.Count]; - stringTab.Keys.CopyTo(sortedStrings, 0); - Array.Sort<string>(sortedStrings, 0, sortedStrings.Length); - - foreach (CultureInfo ci in cultures) - { - sb.Append(" <resources culture=\"").Append(ci.Name).AppendLine("\">"); - sb.AppendLine(" <stringTable>"); - - foreach (string stringKey in sortedStrings) - { - string? val = GetLocalizedMessage(stringKey, ci, etwFormat: true); - sb.Append(" <string id=\"").Append(stringKey).Append("\" value=\"").Append(val).AppendLine("\"/>"); - } - sb.AppendLine(" </stringTable>"); - sb.AppendLine(" </resources>"); - } - sb.AppendLine("</localization>"); - sb.AppendLine("</instrumentationManifest>"); - return sb.ToString(); - } - -#region private - private void WriteNameAndMessageAttribs(StringBuilder stringBuilder, string elementName, string name) - { - stringBuilder.Append(" name=\"").Append(name).Append("\""); - WriteMessageAttrib(sb, elementName, name, name); - } - private void WriteMessageAttrib(StringBuilder stringBuilder, string elementName, string name, string? value) - { - string key = elementName + "_" + name; - // See if the user wants things localized. - if (resources != null) - { - // resource fallback: strings in the neutral culture will take precedence over inline strings - string? localizedString = resources.GetString(key, CultureInfo.InvariantCulture); - if (localizedString != null) - value = localizedString; - } - if (value == null) - return; - - stringBuilder.Append(" message=\"$(string.").Append(key).Append(")\""); - - if (stringTab.TryGetValue(key, out string? prevValue) && !prevValue.Equals(value)) - { - ManifestError(SR.Format(SR.EventSource_DuplicateStringKey, key), true); - return; - } - - stringTab[key] = value; - } - internal string? GetLocalizedMessage(string key, CultureInfo ci, bool etwFormat) - { - string? value = null; - if (resources != null) - { - string? localizedString = resources.GetString(key, ci); - if (localizedString != null) - { - value = localizedString; - if (etwFormat && key.StartsWith("event_", StringComparison.Ordinal)) - { - string evtName = key.Substring("event_".Length); - value = TranslateToManifestConvention(value, evtName); - } - } - } - if (etwFormat && value == null) - stringTab.TryGetValue(key, out value); - - return value; - } - - /// <summary> - /// There's no API to enumerate all languages an assembly is localized into, so instead - /// we enumerate through all the "known" cultures and attempt to load a corresponding satellite - /// assembly - /// </summary> - /// <returns></returns> - private static List<CultureInfo> GetSupportedCultures() - { - var cultures = new List<CultureInfo>(); - - if (!cultures.Contains(CultureInfo.CurrentUICulture)) - cultures.Insert(0, CultureInfo.CurrentUICulture); - return cultures; - } - - private static string GetLevelName(EventLevel level) - { - return (((int)level >= 16) ? "" : "win:") + level.ToString(); - } - -#if FEATURE_MANAGED_ETW_CHANNELS - private string? GetChannelName(EventChannel channel, string eventName, string? eventMessage) - { - ChannelInfo? info = null; - if (channelTab == null || !channelTab.TryGetValue((int)channel, out info)) - { - if (channel < EventChannel.Admin) // || channel > EventChannel.Debug) - ManifestError(SR.Format(SR.EventSource_UndefinedChannel, channel, eventName)); - - // allow channels to be auto-defined. The well known ones get their well known names, and the - // rest get names Channel<N>. This allows users to modify the Manifest if they want more advanced features. - channelTab ??= new Dictionary<int, ChannelInfo>(4); - - string channelName = channel.ToString(); // For well know channels this is a nice name, otherwise a number - if (EventChannel.Debug < channel) - channelName = "Channel" + channelName; // Add a 'Channel' prefix for numbers. - - AddChannel(channelName, (int)channel, GetDefaultChannelAttribute(channel)); - if (!channelTab.TryGetValue((int)channel, out info)) - ManifestError(SR.Format(SR.EventSource_UndefinedChannel, channel, eventName)); - } - // events that specify admin channels *must* have non-null "Message" attributes - if (resources != null) - eventMessage ??= resources.GetString("event_" + eventName, CultureInfo.InvariantCulture); - - Debug.Assert(info!.Attribs != null); - if (info.Attribs.EventChannelType == EventChannelType.Admin && eventMessage == null) - ManifestError(SR.Format(SR.EventSource_EventWithAdminChannelMustHaveMessage, eventName, info.Name)); - return info.Name; - } -#endif - private string GetTaskName(EventTask task, string eventName) - { - if (task == EventTask.None) - return ""; - - string? ret; - taskTab ??= new Dictionary<int, string>(); - if (!taskTab.TryGetValue((int)task, out ret)) - ret = taskTab[(int)task] = eventName; - return ret; - } - - private string? GetOpcodeName(EventOpcode opcode, string eventName) - { - switch (opcode) - { - case EventOpcode.Info: - return "win:Info"; - case EventOpcode.Start: - return "win:Start"; - case EventOpcode.Stop: - return "win:Stop"; - case EventOpcode.DataCollectionStart: - return "win:DC_Start"; - case EventOpcode.DataCollectionStop: - return "win:DC_Stop"; - case EventOpcode.Extension: - return "win:Extension"; - case EventOpcode.Reply: - return "win:Reply"; - case EventOpcode.Resume: - return "win:Resume"; - case EventOpcode.Suspend: - return "win:Suspend"; - case EventOpcode.Send: - return "win:Send"; - case EventOpcode.Receive: - return "win:Receive"; - } - - string? ret; - if (opcodeTab == null || !opcodeTab.TryGetValue((int)opcode, out ret)) - { - ManifestError(SR.Format(SR.EventSource_UndefinedOpcode, opcode, eventName), true); - ret = null; - } - - return ret; - } - - private string GetKeywords(ulong keywords, string eventName) - { -#if FEATURE_MANAGED_ETW_CHANNELS - // ignore keywords associate with channels - // See ValidPredefinedChannelKeywords def for more. - keywords &= ~ValidPredefinedChannelKeywords; -#endif - - string ret = ""; - for (ulong bit = 1; bit != 0; bit <<= 1) - { - if ((keywords & bit) != 0) - { - string? keyword = null; - if ((keywordTab == null || !keywordTab.TryGetValue(bit, out keyword)) && - (bit >= (ulong)0x1000000000000)) - { - // do not report Windows reserved keywords in the manifest (this allows the code - // to be resilient to potential renaming of these keywords) - keyword = string.Empty; - } - if (keyword == null) - { - ManifestError(SR.Format(SR.EventSource_UndefinedKeyword, "0x" + bit.ToString("x", CultureInfo.CurrentCulture), eventName), true); - keyword = string.Empty; - } - if (ret.Length != 0 && keyword.Length != 0) - ret += " "; - ret += keyword; - } - } - return ret; - } - - private string GetTypeName(Type type) - { - if (type.IsEnum()) - { - FieldInfo[] fields = type.GetFields(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance); - string typeName = GetTypeName(fields[0].FieldType); - return typeName.Replace("win:Int", "win:UInt"); // ETW requires enums to be unsigned. - } - - switch (type.GetTypeCode()) - { - case TypeCode.Boolean: - return "win:Boolean"; - case TypeCode.Byte: - return "win:UInt8"; - case TypeCode.Char: - case TypeCode.UInt16: - return "win:UInt16"; - case TypeCode.UInt32: - return "win:UInt32"; - case TypeCode.UInt64: - return "win:UInt64"; - case TypeCode.SByte: - return "win:Int8"; - case TypeCode.Int16: - return "win:Int16"; - case TypeCode.Int32: - return "win:Int32"; - case TypeCode.Int64: - return "win:Int64"; - case TypeCode.String: - return "win:UnicodeString"; - case TypeCode.Single: - return "win:Float"; - case TypeCode.Double: - return "win:Double"; - case TypeCode.DateTime: - return "win:FILETIME"; - default: - if (type == typeof(Guid)) - return "win:GUID"; - else if (type == typeof(IntPtr)) - return "win:Pointer"; - else if ((type.IsArray || type.IsPointer) && type.GetElementType() == typeof(byte)) - return "win:Binary"; - - ManifestError(SR.Format(SR.EventSource_UnsupportedEventTypeInManifest, type.Name), true); - return string.Empty; - } - } - - private static void UpdateStringBuilder([NotNull] ref StringBuilder? stringBuilder, string eventMessage, int startIndex, int count) - { - stringBuilder ??= new StringBuilder(); - stringBuilder.Append(eventMessage, startIndex, count); - } - - private static readonly string[] s_escapes = { "&", "<", ">", "'", """, "%r", "%n", "%t" }; - // Manifest messages use %N conventions for their message substitutions. Translate from - // .NET conventions. We can't use RegEx for this (we are in mscorlib), so we do it 'by hand' - private string TranslateToManifestConvention(string eventMessage, string evtName) - { - StringBuilder? stringBuilder = null; // We lazily create this - int writtenSoFar = 0; - int chIdx = -1; - for (int i = 0; ;) - { - if (i >= eventMessage.Length) - { - if (stringBuilder == null) - return eventMessage; - UpdateStringBuilder(ref stringBuilder, eventMessage, writtenSoFar, i - writtenSoFar); - return stringBuilder.ToString(); - } - - if (eventMessage[i] == '%') - { - // handle format message escaping character '%' by escaping it - UpdateStringBuilder(ref stringBuilder, eventMessage, writtenSoFar, i - writtenSoFar); - stringBuilder.Append("%%"); - i++; - writtenSoFar = i; - } - else if (i < eventMessage.Length - 1 && - (eventMessage[i] == '{' && eventMessage[i + 1] == '{' || eventMessage[i] == '}' && eventMessage[i + 1] == '}')) - { - // handle C# escaped '{" and '}' - UpdateStringBuilder(ref stringBuilder, eventMessage, writtenSoFar, i - writtenSoFar); - stringBuilder.Append(eventMessage[i]); - i++; i++; - writtenSoFar = i; - } - else if (eventMessage[i] == '{') - { - int leftBracket = i; - i++; - int argNum = 0; - while (i < eventMessage.Length && char.IsDigit(eventMessage[i])) - { - argNum = argNum * 10 + eventMessage[i] - '0'; - i++; - } - if (i < eventMessage.Length && eventMessage[i] == '}') - { - i++; - UpdateStringBuilder(ref stringBuilder, eventMessage, writtenSoFar, leftBracket - writtenSoFar); - int manIndex = TranslateIndexToManifestConvention(argNum, evtName); - stringBuilder.Append('%').Append(manIndex); - // An '!' after the insert specifier {n} will be interpreted as a literal. - // We'll escape it so that mc.exe does not attempt to consider it the - // beginning of a format string. - if (i < eventMessage.Length && eventMessage[i] == '!') - { - i++; - stringBuilder.Append("%!"); - } - writtenSoFar = i; - } - else - { - ManifestError(SR.Format(SR.EventSource_UnsupportedMessageProperty, evtName, eventMessage)); - } - } - else if ((chIdx = "&<>'\"\r\n\t".IndexOf(eventMessage[i])) >= 0) - { - UpdateStringBuilder(ref stringBuilder, eventMessage, writtenSoFar, i - writtenSoFar); - i++; - stringBuilder.Append(s_escapes[chIdx]); - writtenSoFar = i; - } - else - i++; - } - } - - private int TranslateIndexToManifestConvention(int idx, string evtName) - { - List<int>? byteArrArgIndices; - if (perEventByteArrayArgIndices.TryGetValue(evtName, out byteArrArgIndices)) - { - foreach (int byArrIdx in byteArrArgIndices) - { - if (idx >= byArrIdx) - ++idx; - else - break; - } - } - return idx + 1; - } - -#if FEATURE_MANAGED_ETW_CHANNELS - private class ChannelInfo - { - public string? Name; - public ulong Keywords; - public EventChannelAttribute? Attribs; - } -#endif - - private readonly Dictionary<int, string> opcodeTab; - private Dictionary<int, string>? taskTab; -#if FEATURE_MANAGED_ETW_CHANNELS - private Dictionary<int, ChannelInfo>? channelTab; -#endif - private Dictionary<ulong, string>? keywordTab; - private Dictionary<string, Type>? mapsTab; - private readonly Dictionary<string, string> stringTab; // Maps unlocalized strings to localized ones - -#if FEATURE_MANAGED_ETW_CHANNELS - // WCF used EventSource to mimic a existing ETW manifest. To support this - // in just their case, we allowed them to specify the keywords associated - // with their channels explicitly. ValidPredefinedChannelKeywords is - // this set of channel keywords that we allow to be explicitly set. You - // can ignore these bits otherwise. - internal const ulong ValidPredefinedChannelKeywords = 0xF000000000000000; - private ulong nextChannelKeywordBit = 0x8000000000000000; // available Keyword bit to be used for next channel definition, grows down - private const int MaxCountChannels = 8; // a manifest can defined at most 8 ETW channels -#endif - - private readonly StringBuilder sb; // Holds the provider information. - private readonly StringBuilder events; // Holds the events. - private readonly StringBuilder templates; - -#if FEATURE_MANAGED_ETW_CHANNELS - private readonly string providerName; -#endif - private readonly ResourceManager? resources; // Look up localized strings here. - private readonly EventManifestOptions flags; - private readonly IList<string> errors; // list of currently encountered errors - private readonly Dictionary<string, List<int>> perEventByteArrayArgIndices; // "event_name" -> List_of_Indices_of_Byte[]_Arg - - // State we track between StartEvent and EndEvent. - private string? eventName; // Name of the event currently being processed. - private int numParams; // keeps track of the number of args the event has. - private List<int>? byteArrArgIndices; // keeps track of the index of each byte[] argument -#endregion - } - - /// <summary> - /// Used to send the m_rawManifest into the event dispatcher as a series of events. - /// </summary> - internal struct ManifestEnvelope - { - public const int MaxChunkSize = 0xFF00; - public enum ManifestFormats : byte - { - SimpleXmlFormat = 1, // simply dump the XML manifest as UTF8 - } - -#if FEATURE_MANAGED_ETW - public ManifestFormats Format; - public byte MajorVersion; - public byte MinorVersion; - public byte Magic; - public ushort TotalChunks; - public ushort ChunkNumber; -#endif - } - -#endregion -} diff --git a/netcore/System.Private.CoreLib/shared/System/Diagnostics/Tracing/EventSourceException.cs b/netcore/System.Private.CoreLib/shared/System/Diagnostics/Tracing/EventSourceException.cs deleted file mode 100644 index 2a8515ad615..00000000000 --- a/netcore/System.Private.CoreLib/shared/System/Diagnostics/Tracing/EventSourceException.cs +++ /dev/null @@ -1,52 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -#if ES_BUILD_STANDALONE -using System; -using Environment = Microsoft.Diagnostics.Tracing.Internal.Environment; -#endif -using System.Runtime.Serialization; - -#if ES_BUILD_STANDALONE -namespace Microsoft.Diagnostics.Tracing -#else -namespace System.Diagnostics.Tracing -#endif -{ - /// <summary> - /// Exception that is thrown when an error occurs during EventSource operation. - /// </summary> -#if !ES_BUILD_PCL - [Serializable] -#endif - public class EventSourceException : Exception - { - /// <summary> - /// Initializes a new instance of the EventSourceException class. - /// </summary> - public EventSourceException() : - base(SR.EventSource_ListenerWriteFailure) { } - - /// <summary> - /// Initializes a new instance of the EventSourceException class with a specified error message. - /// </summary> - public EventSourceException(string? message) : base(message) { } - - /// <summary> - /// Initializes a new instance of the EventSourceException class with a specified error message - /// and a reference to the inner exception that is the cause of this exception. - /// </summary> - public EventSourceException(string? message, Exception? innerException) : base(message, innerException) { } - -#if !ES_BUILD_PCL - /// <summary> - /// Initializes a new instance of the EventSourceException class with serialized data. - /// </summary> - protected EventSourceException(SerializationInfo info, StreamingContext context) : base(info, context) { } -#endif - - internal EventSourceException(Exception? innerException) : - base(SR.EventSource_ListenerWriteFailure, innerException) { } - } -} diff --git a/netcore/System.Private.CoreLib/shared/System/Diagnostics/Tracing/FrameworkEventSource.cs b/netcore/System.Private.CoreLib/shared/System/Diagnostics/Tracing/FrameworkEventSource.cs deleted file mode 100644 index 321ae819f50..00000000000 --- a/netcore/System.Private.CoreLib/shared/System/Diagnostics/Tracing/FrameworkEventSource.cs +++ /dev/null @@ -1,161 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using Internal.Runtime.CompilerServices; - -namespace System.Diagnostics.Tracing -{ - [EventSource(Guid = "8E9F5090-2D75-4d03-8A81-E5AFBF85DAF1", Name = "System.Diagnostics.Eventing.FrameworkEventSource")] - internal sealed class FrameworkEventSource : EventSource - { - public static readonly FrameworkEventSource Log = new FrameworkEventSource(); - - // Keyword definitions. These represent logical groups of events that can be turned on and off independently - // Often each task has a keyword, but where tasks are determined by subsystem, keywords are determined by - // usefulness to end users to filter. Generally users don't mind extra events if they are not high volume - // so grouping low volume events together in a single keywords is OK (users can post-filter by task if desired) - public static class Keywords - { - public const EventKeywords ThreadPool = (EventKeywords)0x0002; - public const EventKeywords ThreadTransfer = (EventKeywords)0x0010; - } - - /// <summary>ETW tasks that have start/stop events.</summary> - public static class Tasks // this name is important for EventSource - { - /// <summary>Send / Receive - begin transfer/end transfer</summary> - public const EventTask ThreadTransfer = (EventTask)3; - } - - // The FrameworkEventSource GUID is {8E9F5090-2D75-4d03-8A81-E5AFBF85DAF1} - private FrameworkEventSource() : base(new Guid(0x8e9f5090, 0x2d75, 0x4d03, 0x8a, 0x81, 0xe5, 0xaf, 0xbf, 0x85, 0xda, 0xf1), "System.Diagnostics.Eventing.FrameworkEventSource") { } - - // optimized for common signatures (used by the ThreadTransferSend/Receive events) - [NonEvent] - private unsafe void WriteEvent(int eventId, long arg1, int arg2, string? arg3, bool arg4, int arg5, int arg6) - { - if (IsEnabled()) - { - arg3 ??= ""; - fixed (char* string3Bytes = arg3) - { - EventSource.EventData* descrs = stackalloc EventSource.EventData[6]; - descrs[0].DataPointer = (IntPtr)(&arg1); - descrs[0].Size = 8; - descrs[0].Reserved = 0; - descrs[1].DataPointer = (IntPtr)(&arg2); - descrs[1].Size = 4; - descrs[1].Reserved = 0; - descrs[2].DataPointer = (IntPtr)string3Bytes; - descrs[2].Size = ((arg3.Length + 1) * 2); - descrs[2].Reserved = 0; - descrs[3].DataPointer = (IntPtr)(&arg4); - descrs[3].Size = 4; - descrs[3].Reserved = 0; - descrs[4].DataPointer = (IntPtr)(&arg5); - descrs[4].Size = 4; - descrs[4].Reserved = 0; - descrs[5].DataPointer = (IntPtr)(&arg6); - descrs[5].Size = 4; - descrs[5].Reserved = 0; - WriteEventCore(eventId, 6, descrs); - } - } - } - - // optimized for common signatures (used by the ThreadTransferSend/Receive events) - [NonEvent] - private unsafe void WriteEvent(int eventId, long arg1, int arg2, string? arg3) - { - if (IsEnabled()) - { - arg3 ??= ""; - fixed (char* string3Bytes = arg3) - { - EventSource.EventData* descrs = stackalloc EventSource.EventData[3]; - descrs[0].DataPointer = (IntPtr)(&arg1); - descrs[0].Size = 8; - descrs[0].Reserved = 0; - descrs[1].DataPointer = (IntPtr)(&arg2); - descrs[1].Size = 4; - descrs[1].Reserved = 0; - descrs[2].DataPointer = (IntPtr)string3Bytes; - descrs[2].Size = ((arg3.Length + 1) * 2); - descrs[2].Reserved = 0; - WriteEventCore(eventId, 3, descrs); - } - } - } - - [Event(30, Level = EventLevel.Verbose, Keywords = Keywords.ThreadPool | Keywords.ThreadTransfer)] - public void ThreadPoolEnqueueWork(long workID) - { - WriteEvent(30, workID); - } - - [NonEvent] - public unsafe void ThreadPoolEnqueueWorkObject(object workID) - { - // convert the Object Id to a long - ThreadPoolEnqueueWork((long)*((void**)Unsafe.AsPointer(ref workID))); - } - - [Event(31, Level = EventLevel.Verbose, Keywords = Keywords.ThreadPool | Keywords.ThreadTransfer)] - public void ThreadPoolDequeueWork(long workID) - { - WriteEvent(31, workID); - } - - [NonEvent] - public unsafe void ThreadPoolDequeueWorkObject(object workID) - { - // convert the Object Id to a long - ThreadPoolDequeueWork((long)*((void**)Unsafe.AsPointer(ref workID))); - } - - // id - represents a correlation ID that allows correlation of two activities, one stamped by - // ThreadTransferSend, the other by ThreadTransferReceive - // kind - identifies the transfer: values below 64 are reserved for the runtime. Currently used values: - // 1 - managed Timers ("roaming" ID) - // 2 - managed async IO operations (FileStream, PipeStream, a.o.) - // 3 - WinRT dispatch operations - // info - any additional information user code might consider interesting - // intInfo1/2 - any additional integer information user code might consider interesting - [Event(150, Level = EventLevel.Informational, Keywords = Keywords.ThreadTransfer, Task = Tasks.ThreadTransfer, Opcode = EventOpcode.Send)] - public void ThreadTransferSend(long id, int kind, string info, bool multiDequeues, int intInfo1, int intInfo2) - { - WriteEvent(150, id, kind, info, multiDequeues, intInfo1, intInfo2); - } - - // id - is a managed object. it gets translated to the object's address. ETW listeners must - // keep track of GC movements in order to correlate the value passed to XyzSend with the - // (possibly changed) value passed to XyzReceive - [NonEvent] - public unsafe void ThreadTransferSendObj(object id, int kind, string info, bool multiDequeues, int intInfo1, int intInfo2) - { - ThreadTransferSend((long)*((void**)Unsafe.AsPointer(ref id)), kind, info, multiDequeues, intInfo1, intInfo2); - } - - // id - represents a correlation ID that allows correlation of two activities, one stamped by - // ThreadTransferSend, the other by ThreadTransferReceive - // kind - identifies the transfer: values below 64 are reserved for the runtime. Currently used values: - // 1 - managed Timers ("roaming" ID) - // 2 - managed async IO operations (FileStream, PipeStream, a.o.) - // 3 - WinRT dispatch operations - // info - any additional information user code might consider interesting - [Event(151, Level = EventLevel.Informational, Keywords = Keywords.ThreadTransfer, Task = Tasks.ThreadTransfer, Opcode = EventOpcode.Receive)] - public void ThreadTransferReceive(long id, int kind, string? info) - { - WriteEvent(151, id, kind, info); - } - // id - is a managed object. it gets translated to the object's address. ETW listeners must - // keep track of GC movements in order to correlate the value passed to XyzSend with the - // (possibly changed) value passed to XyzReceive - [NonEvent] - public unsafe void ThreadTransferReceiveObj(object id, int kind, string? info) - { - ThreadTransferReceive((long)*((void**)Unsafe.AsPointer(ref id)), kind, info); - } - } -} diff --git a/netcore/System.Private.CoreLib/shared/System/Diagnostics/Tracing/IEventProvider.cs b/netcore/System.Private.CoreLib/shared/System/Diagnostics/Tracing/IEventProvider.cs deleted file mode 100644 index 2ceb92f99c0..00000000000 --- a/netcore/System.Private.CoreLib/shared/System/Diagnostics/Tracing/IEventProvider.cs +++ /dev/null @@ -1,44 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -#if ES_BUILD_STANDALONE -using System; -#endif - -#if ES_BUILD_STANDALONE -namespace Microsoft.Diagnostics.Tracing -#else -namespace System.Diagnostics.Tracing -#endif -{ - // Represents the interface between EventProvider and an external logging mechanism. - internal interface IEventProvider - { - // Register an event provider. - unsafe uint EventRegister( - EventSource eventSource, - Interop.Advapi32.EtwEnableCallback enableCallback, - void* callbackContext, - ref long registrationHandle); - - // Unregister an event provider. - uint EventUnregister(long registrationHandle); - - // Write an event. - unsafe EventProvider.WriteEventErrorCode EventWriteTransfer( - long registrationHandle, - in EventDescriptor eventDescriptor, - IntPtr eventHandle, - Guid* activityId, - Guid* relatedActivityId, - int userDataCount, - EventProvider.EventData* userData); - - // Get or set the per-thread activity ID. - int EventActivityIdControl(Interop.Advapi32.ActivityControl ControlCode, ref Guid ActivityId); - - // Define an EventPipeEvent handle. - unsafe IntPtr DefineEventHandle(uint eventID, string eventName, long keywords, uint eventVersion, uint level, byte* pMetadata, uint metadataLength); - } -} diff --git a/netcore/System.Private.CoreLib/shared/System/Diagnostics/Tracing/IncrementingEventCounter.cs b/netcore/System.Private.CoreLib/shared/System/Diagnostics/Tracing/IncrementingEventCounter.cs deleted file mode 100644 index 895ebe59fb2..00000000000 --- a/netcore/System.Private.CoreLib/shared/System/Diagnostics/Tracing/IncrementingEventCounter.cs +++ /dev/null @@ -1,94 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -#if ES_BUILD_STANDALONE -using System; -using System.Diagnostics; -#endif - -#if ES_BUILD_STANDALONE -namespace Microsoft.Diagnostics.Tracing -#else -namespace System.Diagnostics.Tracing -#endif -{ - /// <summary> - /// IncrementingEventCounter is a variant of EventCounter for variables that are ever-increasing. - /// Ex) # of exceptions in the runtime. - /// It does not calculate statistics like mean, standard deviation, etc. because it only accumulates - /// the counter value. - /// </summary> - public partial class IncrementingEventCounter : DiagnosticCounter - { - /// <summary> - /// Initializes a new instance of the <see cref="IncrementingEventCounter"/> class. - /// IncrementingEventCounter live as long as the EventSource that they are attached to unless they are - /// explicitly Disposed. - /// </summary> - /// <param name="name">The name.</param> - /// <param name="eventSource">The event source.</param> - public IncrementingEventCounter(string name, EventSource eventSource) : base(name, eventSource) - { - Publish(); - } - - /// <summary> - /// Writes 'value' to the stream of values tracked by the counter. This updates the sum and other statistics that will - /// be logged on the next timer interval. - /// </summary> - /// <param name="increment">The value to increment by.</param> - public void Increment(double increment = 1) - { - lock (this) - { - _increment += increment; - } - } - - public TimeSpan DisplayRateTimeScale { get; set; } - private double _increment; - private double _prevIncrement; - - public override string ToString() => $"IncrementingEventCounter '{Name}' Increment {_increment}"; - - internal override void WritePayload(float intervalSec, int pollingIntervalMillisec) - { - lock (this) // Lock the counter - { - IncrementingCounterPayload payload = new IncrementingCounterPayload(); - payload.Name = Name; - payload.IntervalSec = intervalSec; - payload.DisplayName = DisplayName ?? ""; - payload.DisplayRateTimeScale = (DisplayRateTimeScale == TimeSpan.Zero) ? "" : DisplayRateTimeScale.ToString("c"); - payload.Series = $"Interval={pollingIntervalMillisec}"; // TODO: This may need to change when we support multi-session - payload.CounterType = "Sum"; - payload.Metadata = GetMetadataString(); - payload.Increment = _increment - _prevIncrement; - payload.DisplayUnits = DisplayUnits ?? ""; - _prevIncrement = _increment; - EventSource.Write("EventCounters", new EventSourceOptions() { Level = EventLevel.LogAlways }, new IncrementingEventCounterPayloadType(payload)); - } - } - - // Updates the value. - internal void UpdateMetric() - { - lock (this) - { - _prevIncrement = _increment; - } - } - } - - - /// <summary> - /// This is the payload that is sent in the with EventSource.Write - /// </summary> - [EventData] - internal class IncrementingEventCounterPayloadType - { - public IncrementingEventCounterPayloadType(IncrementingCounterPayload payload) { Payload = payload; } - public IncrementingCounterPayload Payload { get; set; } - } -} diff --git a/netcore/System.Private.CoreLib/shared/System/Diagnostics/Tracing/IncrementingPollingCounter.cs b/netcore/System.Private.CoreLib/shared/System/Diagnostics/Tracing/IncrementingPollingCounter.cs deleted file mode 100644 index 0cdbac7e6a0..00000000000 --- a/netcore/System.Private.CoreLib/shared/System/Diagnostics/Tracing/IncrementingPollingCounter.cs +++ /dev/null @@ -1,96 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -#if ES_BUILD_STANDALONE -using System; -#endif - -#if ES_BUILD_STANDALONE -namespace Microsoft.Diagnostics.Tracing -#else -namespace System.Diagnostics.Tracing -#endif -{ - /// <summary> - /// IncrementingPollingCounter is a variant of EventCounter for variables that are ever-increasing. - /// Ex) # of exceptions in the runtime. - /// It does not calculate statistics like mean, standard deviation, etc. because it only accumulates - /// the counter value. - /// Unlike IncrementingEventCounter, this takes in a polling callback that it can call to update - /// its own metric periodically. - /// </summary> - public partial class IncrementingPollingCounter : DiagnosticCounter - { - /// <summary> - /// Initializes a new instance of the <see cref="IncrementingPollingCounter"/> class. - /// IncrementingPollingCounter live as long as the EventSource that they are attached to unless they are - /// explicitly Disposed. - /// </summary> - /// <param name="name">The name.</param> - /// <param name="eventSource">The event source.</param> - public IncrementingPollingCounter(string name, EventSource eventSource, Func<double> totalValueProvider) : base(name, eventSource) - { - if (totalValueProvider == null) - throw new ArgumentNullException(nameof(totalValueProvider)); - - _totalValueProvider = totalValueProvider; - Publish(); - } - - public override string ToString() => $"IncrementingPollingCounter '{Name}' Increment {_increment}"; - - public TimeSpan DisplayRateTimeScale { get; set; } - private double _increment; - private double _prevIncrement; - private readonly Func<double> _totalValueProvider; - - /// <summary> - /// Calls "_totalValueProvider" to enqueue the counter value to the queue. - /// </summary> - internal void UpdateMetric() - { - try - { - lock (this) - { - _prevIncrement = _increment; - _increment = _totalValueProvider(); - } - } - catch (Exception ex) - { - ReportOutOfBandMessage($"ERROR: Exception during EventCounter {Name} getMetricFunction callback: " + ex.Message); - } - } - - internal override void WritePayload(float intervalSec, int pollingIntervalMillisec) - { - UpdateMetric(); - lock (this) // Lock the counter - { - IncrementingCounterPayload payload = new IncrementingCounterPayload(); - payload.Name = Name; - payload.DisplayName = DisplayName ?? ""; - payload.DisplayRateTimeScale = (DisplayRateTimeScale == TimeSpan.Zero) ? "" : DisplayRateTimeScale.ToString("c"); - payload.IntervalSec = intervalSec; - payload.Series = $"Interval={pollingIntervalMillisec}"; // TODO: This may need to change when we support multi-session - payload.CounterType = "Sum"; - payload.Metadata = GetMetadataString(); - payload.Increment = _increment - _prevIncrement; - payload.DisplayUnits = DisplayUnits ?? ""; - EventSource.Write("EventCounters", new EventSourceOptions() { Level = EventLevel.LogAlways }, new IncrementingPollingCounterPayloadType(payload)); - } - } - } - - /// <summary> - /// This is the payload that is sent in the with EventSource.Write - /// </summary> - [EventData] - internal class IncrementingPollingCounterPayloadType - { - public IncrementingPollingCounterPayloadType(IncrementingCounterPayload payload) { Payload = payload; } - public IncrementingCounterPayload Payload { get; set; } - } -} diff --git a/netcore/System.Private.CoreLib/shared/System/Diagnostics/Tracing/PollingCounter.cs b/netcore/System.Private.CoreLib/shared/System/Diagnostics/Tracing/PollingCounter.cs deleted file mode 100644 index 3dd3d832ea9..00000000000 --- a/netcore/System.Private.CoreLib/shared/System/Diagnostics/Tracing/PollingCounter.cs +++ /dev/null @@ -1,86 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -#if ES_BUILD_STANDALONE -using System; -#endif - -#if ES_BUILD_STANDALONE -namespace Microsoft.Diagnostics.Tracing -#else -namespace System.Diagnostics.Tracing -#endif -{ - /// <summary> - /// PollingCounter is a variant of EventCounter - it collects and calculates similar statistics - /// as EventCounter. PollingCounter differs from EventCounter in that it takes in a callback - /// function to collect metrics on its own rather than the user having to call WriteMetric() - /// every time. - /// </summary> - public partial class PollingCounter : DiagnosticCounter - { - /// <summary> - /// Initializes a new instance of the <see cref="PollingCounter"/> class. - /// PollingCounter live as long as the EventSource that they are attached to unless they are - /// explicitly Disposed. - /// </summary> - /// <param name="name">The name.</param> - /// <param name="eventSource">The event source.</param> - public PollingCounter(string name, EventSource eventSource, Func<double> metricProvider) : base(name, eventSource) - { - if (metricProvider == null) - throw new ArgumentNullException(nameof(metricProvider)); - - _metricProvider = metricProvider; - Publish(); - } - - public override string ToString() => $"PollingCounter '{Name}' Count 1 Mean {_lastVal.ToString("n3")}"; - - private readonly Func<double> _metricProvider; - private double _lastVal; - - internal override void WritePayload(float intervalSec, int pollingIntervalMillisec) - { - lock (this) - { - double value = 0; - try - { - value = _metricProvider(); - } - catch (Exception ex) - { - ReportOutOfBandMessage($"ERROR: Exception during EventCounter {Name} metricProvider callback: " + ex.Message); - } - - CounterPayload payload = new CounterPayload(); - payload.Name = Name; - payload.DisplayName = DisplayName ?? ""; - payload.Count = 1; // NOTE: These dumb-looking statistics is intentional - payload.IntervalSec = intervalSec; - payload.Series = $"Interval={pollingIntervalMillisec}"; // TODO: This may need to change when we support multi-session - payload.CounterType = "Mean"; - payload.Mean = value; - payload.Max = value; - payload.Min = value; - payload.Metadata = GetMetadataString(); - payload.StandardDeviation = 0; - payload.DisplayUnits = DisplayUnits ?? ""; - _lastVal = value; - EventSource.Write("EventCounters", new EventSourceOptions() { Level = EventLevel.LogAlways }, new PollingPayloadType(payload)); - } - } - } - - /// <summary> - /// This is the payload that is sent in the with EventSource.Write - /// </summary> - [EventData] - internal class PollingPayloadType - { - public PollingPayloadType(CounterPayload payload) { Payload = payload; } - public CounterPayload Payload { get; set; } - } -} diff --git a/netcore/System.Private.CoreLib/shared/System/Diagnostics/Tracing/StubEnvironment.cs b/netcore/System.Private.CoreLib/shared/System/Diagnostics/Tracing/StubEnvironment.cs deleted file mode 100644 index 236e0cbbc40..00000000000 --- a/netcore/System.Private.CoreLib/shared/System/Diagnostics/Tracing/StubEnvironment.cs +++ /dev/null @@ -1,376 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; -#if ES_BUILD_PCL || ES_BUILD_PN -using System.Collections.Generic; -#endif -using System.Reflection; -#if ES_BUILD_STANDALONE -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; -using System.Security; -#endif -#if ES_BUILD_AGAINST_DOTNET_V35 -using Microsoft.Internal; -#endif -using Microsoft.Reflection; - -#if ES_BUILD_STANDALONE -namespace Microsoft.Diagnostics.Tracing.Internal -#else -namespace System.Diagnostics.Tracing.Internal -#endif -{ - internal static class Environment - { - public static readonly string NewLine = System.Environment.NewLine; - - public static int TickCount => System.Environment.TickCount; - - public static string GetResourceString(string key, params object?[] args) - { - string? fmt = rm.GetString(key); - if (fmt != null) - return string.Format(fmt, args); - - string sargs = string.Join(", ", args); - - return key + " (" + sargs + ")"; - } - - public static string GetRuntimeResourceString(string key, params object?[] args) - { - return GetResourceString(key, args); - } - - private static readonly System.Resources.ResourceManager rm = new System.Resources.ResourceManager("Microsoft.Diagnostics.Tracing.Messages", typeof(Environment).Assembly()); - } - -#if ES_BUILD_STANDALONE - internal static class BitOperations - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static uint RotateLeft(uint value, int offset) - => (value << offset) | (value >> (32 - offset)); - - public static int PopCount(uint value) - { - const uint c1 = 0x_55555555u; - const uint c2 = 0x_33333333u; - const uint c3 = 0x_0F0F0F0Fu; - const uint c4 = 0x_01010101u; - - value = value - ((value >> 1) & c1); - value = (value & c2) + ((value >> 2) & c2); - value = (((value + (value >> 4)) & c3) * c4) >> 24; - - return (int)value; - } - - public static int TrailingZeroCount(uint value) - { - if (value == 0) - return 32; - - int count = 0; - while ((value & 1) == 0) - { - value >>= 1; - count++; - } - return count; - } - } -#endif -} - -#if ES_BUILD_AGAINST_DOTNET_V35 - -namespace Microsoft.Internal -{ - using System.Text; - - internal static class Tuple - { - public static Tuple<T1> Create<T1>(T1 item1) - { - return new Tuple<T1>(item1); - } - - public static Tuple<T1, T2> Create<T1, T2>(T1 item1, T2 item2) - { - return new Tuple<T1, T2>(item1, item2); - } - } - - [Serializable] - internal class Tuple<T1> - { - private readonly T1 m_Item1; - - public T1 Item1 { get { return m_Item1; } } - - public Tuple(T1 item1) - { - m_Item1 = item1; - } - - public override string ToString() - { - StringBuilder sb = new StringBuilder(); - sb.Append("("); - sb.Append(m_Item1); - sb.Append(")"); - return sb.ToString(); - } - - int Size - { - get - { - return 1; - } - } - } - - [Serializable] - public class Tuple<T1, T2> - { - private readonly T1 m_Item1; - private readonly T2 m_Item2; - - public T1 Item1 { get { return m_Item1; } } - public T2 Item2 { get { return m_Item2; } } - - public Tuple(T1 item1, T2 item2) - { - m_Item1 = item1; - m_Item2 = item2; - } - - public override string ToString() - { - StringBuilder sb = new StringBuilder(); - sb.Append("("); - sb.Append(m_Item1); - sb.Append(", "); - sb.Append(m_Item2); - sb.Append(")"); - return sb.ToString(); - } - - int Size - { - get - { - return 2; - } - } - } -} - -#endif - -namespace Microsoft.Reflection -{ -#if ES_BUILD_PCL - [Flags] - public enum BindingFlags - { - DeclaredOnly = 0x02, // Only look at the members declared on the Type - Instance = 0x04, // Include Instance members in search - Static = 0x08, // Include Static members in search - Public = 0x10, // Include Public members in search - NonPublic = 0x20, // Include Non-Public members in search - } - - public enum TypeCode { - Empty = 0, // Null reference - Object = 1, // Instance that isn't a value - DBNull = 2, // Database null value - Boolean = 3, // Boolean - Char = 4, // Unicode character - SByte = 5, // Signed 8-bit integer - Byte = 6, // Unsigned 8-bit integer - Int16 = 7, // Signed 16-bit integer - UInt16 = 8, // Unsigned 16-bit integer - Int32 = 9, // Signed 32-bit integer - UInt32 = 10, // Unsigned 32-bit integer - Int64 = 11, // Signed 64-bit integer - UInt64 = 12, // Unsigned 64-bit integer - Single = 13, // IEEE 32-bit float - Double = 14, // IEEE 64-bit double - Decimal = 15, // Decimal - DateTime = 16, // DateTime - String = 18, // Unicode character string - } -#endif - internal static class ReflectionExtensions - { -#if (!ES_BUILD_PCL && !ES_BUILD_PN) - - // - // Type extension methods - // - public static bool IsEnum(this Type type) { return type.IsEnum; } - public static bool IsAbstract(this Type type) { return type.IsAbstract; } - public static bool IsSealed(this Type type) { return type.IsSealed; } - public static bool IsValueType(this Type type) { return type.IsValueType; } - public static bool IsGenericType(this Type type) { return type.IsGenericType; } - public static Type? BaseType(this Type type) { return type.BaseType; } - public static Assembly Assembly(this Type type) { return type.Assembly; } - public static TypeCode GetTypeCode(this Type type) { return Type.GetTypeCode(type); } - - public static bool ReflectionOnly(this Assembly assm) { return assm.ReflectionOnly; } - -#else - - // - // Type extension methods - // - public static bool IsEnum(this Type type) { return type.GetTypeInfo().IsEnum; } - public static bool IsAbstract(this Type type) { return type.GetTypeInfo().IsAbstract; } - public static bool IsSealed(this Type type) { return type.GetTypeInfo().IsSealed; } - public static bool IsValueType(this Type type) { return type.GetTypeInfo().IsValueType; } - public static bool IsGenericType(this Type type) { return type.IsConstructedGenericType; } - public static Type? BaseType(this Type type) { return type.GetTypeInfo().BaseType; } - public static Assembly Assembly(this Type type) { return type.GetTypeInfo().Assembly; } - public static IEnumerable<PropertyInfo> GetProperties(this Type type) - { -#if ES_BUILD_PN - return type.GetProperties(); -#else - return type.GetRuntimeProperties(); -#endif - } - public static MethodInfo? GetGetMethod(this PropertyInfo propInfo) { return propInfo.GetMethod; } - public static Type[] GetGenericArguments(this Type type) { return type.GenericTypeArguments; } - - public static MethodInfo[] GetMethods(this Type type, BindingFlags flags) - { - // Minimal implementation to cover only the cases we need - System.Diagnostics.Debug.Assert((flags & BindingFlags.DeclaredOnly) != 0); - System.Diagnostics.Debug.Assert((flags & ~(BindingFlags.DeclaredOnly|BindingFlags.Instance|BindingFlags.Static|BindingFlags.Public|BindingFlags.NonPublic)) == 0); - Func<MethodInfo, bool> visFilter; - Func<MethodInfo, bool> instFilter; - switch (flags & (BindingFlags.Public | BindingFlags.NonPublic)) - { - case 0: visFilter = mi => false; break; - case BindingFlags.Public: visFilter = mi => mi.IsPublic; break; - case BindingFlags.NonPublic: visFilter = mi => !mi.IsPublic; break; - default: visFilter = mi => true; break; - } - switch (flags & (BindingFlags.Instance | BindingFlags.Static)) - { - case 0: instFilter = mi => false; break; - case BindingFlags.Instance: instFilter = mi => !mi.IsStatic; break; - case BindingFlags.Static: instFilter = mi => mi.IsStatic; break; - default: instFilter = mi => true; break; - } - List<MethodInfo> methodInfos = new List<MethodInfo>(); - foreach (var declaredMethod in type.GetTypeInfo().DeclaredMethods) - { - if (visFilter(declaredMethod) && instFilter(declaredMethod)) - methodInfos.Add(declaredMethod); - } - return methodInfos.ToArray(); - } - public static FieldInfo[] GetFields(this Type type, BindingFlags flags) - { - // Minimal implementation to cover only the cases we need - System.Diagnostics.Debug.Assert((flags & BindingFlags.DeclaredOnly) != 0); - System.Diagnostics.Debug.Assert((flags & ~(BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic)) == 0); - Func<FieldInfo, bool> visFilter; - Func<FieldInfo, bool> instFilter; - switch (flags & (BindingFlags.Public | BindingFlags.NonPublic)) - { - case 0: visFilter = fi => false; break; - case BindingFlags.Public: visFilter = fi => fi.IsPublic; break; - case BindingFlags.NonPublic: visFilter = fi => !fi.IsPublic; break; - default: visFilter = fi => true; break; - } - switch (flags & (BindingFlags.Instance | BindingFlags.Static)) - { - case 0: instFilter = fi => false; break; - case BindingFlags.Instance: instFilter = fi => !fi.IsStatic; break; - case BindingFlags.Static: instFilter = fi => fi.IsStatic; break; - default: instFilter = fi => true; break; - } - List<FieldInfo> fieldInfos = new List<FieldInfo>(); - foreach (var declaredField in type.GetTypeInfo().DeclaredFields) - { - if (visFilter(declaredField) && instFilter(declaredField)) - fieldInfos.Add(declaredField); - } - return fieldInfos.ToArray(); - } - public static Type? GetNestedType(this Type type, string nestedTypeName) - { - TypeInfo? ti = null; - foreach (var nt in type.GetTypeInfo().DeclaredNestedTypes) - { - if (nt.Name == nestedTypeName) - { - ti = nt; - break; - } - } - return ti == null ? null : ti.AsType(); - } - public static TypeCode GetTypeCode(this Type type) - { - if (type == typeof(bool)) return TypeCode.Boolean; - else if (type == typeof(byte)) return TypeCode.Byte; - else if (type == typeof(char)) return TypeCode.Char; - else if (type == typeof(ushort)) return TypeCode.UInt16; - else if (type == typeof(uint)) return TypeCode.UInt32; - else if (type == typeof(ulong)) return TypeCode.UInt64; - else if (type == typeof(sbyte)) return TypeCode.SByte; - else if (type == typeof(short)) return TypeCode.Int16; - else if (type == typeof(int)) return TypeCode.Int32; - else if (type == typeof(long)) return TypeCode.Int64; - else if (type == typeof(string)) return TypeCode.String; - else if (type == typeof(float)) return TypeCode.Single; - else if (type == typeof(double)) return TypeCode.Double; - else if (type == typeof(DateTime)) return TypeCode.DateTime; - else if (type == (typeof(decimal))) return TypeCode.Decimal; - else return TypeCode.Object; - } - - // - // FieldInfo extension methods - // - public static object? GetRawConstantValue(this FieldInfo fi) - { return fi.GetValue(null); } - - // - // Assembly extension methods - // - public static bool ReflectionOnly(this Assembly assm) - { - // In PCL we can't load in reflection-only context - return false; - } - -#endif - } -} - -#if ES_BUILD_STANDALONE -internal static partial class Interop -{ - [SuppressUnmanagedCodeSecurityAttribute] - internal static partial class Kernel32 - { - [DllImport("kernel32.dll", CharSet = CharSet.Auto)] - internal static extern int GetCurrentThreadId(); - - [DllImport("kernel32.dll", CharSet = CharSet.Auto)] - internal static extern uint GetCurrentProcessId(); - } - - internal static uint GetCurrentProcessId() => Kernel32.GetCurrentProcessId(); -} -#endif diff --git a/netcore/System.Private.CoreLib/shared/System/Diagnostics/Tracing/TraceLogging/ArrayTypeInfo.cs b/netcore/System.Private.CoreLib/shared/System/Diagnostics/Tracing/TraceLogging/ArrayTypeInfo.cs deleted file mode 100644 index b9f4864089b..00000000000 --- a/netcore/System.Private.CoreLib/shared/System/Diagnostics/Tracing/TraceLogging/ArrayTypeInfo.cs +++ /dev/null @@ -1,66 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -#if ES_BUILD_STANDALONE -using System; -using System.Diagnostics; -#endif - -#if ES_BUILD_STANDALONE -namespace Microsoft.Diagnostics.Tracing -#else -namespace System.Diagnostics.Tracing -#endif -{ - internal sealed class ArrayTypeInfo : TraceLoggingTypeInfo - { - private readonly TraceLoggingTypeInfo elementInfo; - - public ArrayTypeInfo(Type type, TraceLoggingTypeInfo elementInfo) - : base(type) - { - this.elementInfo = elementInfo; - } - - public override void WriteMetadata( - TraceLoggingMetadataCollector collector, - string? name, - EventFieldFormat format) - { - collector.BeginBufferedArray(); - this.elementInfo.WriteMetadata(collector, name, format); - collector.EndBufferedArray(); - } - - public override void WriteData(TraceLoggingDataCollector collector, PropertyValue value) - { - int bookmark = collector.BeginBufferedArray(); - - int count = 0; - Array? array = (Array?)value.ReferenceValue; - if (array != null) - { - count = array.Length; - for (int i = 0; i < array.Length; i++) - { - this.elementInfo.WriteData(collector, elementInfo.PropertyValueFactory(array.GetValue(i))); - } - } - - collector.EndBufferedArray(bookmark, count); - } - - public override object? GetData(object? value) - { - Debug.Assert(value != null, "null accepted only for some overrides"); - var array = (Array)value; - var serializedArray = new object?[array.Length]; - for (int i = 0; i < array.Length; i++) - { - serializedArray[i] = this.elementInfo.GetData(array.GetValue(i)); - } - return serializedArray; - } - } -} diff --git a/netcore/System.Private.CoreLib/shared/System/Diagnostics/Tracing/TraceLogging/ConcurrentSet.cs b/netcore/System.Private.CoreLib/shared/System/Diagnostics/Tracing/TraceLogging/ConcurrentSet.cs deleted file mode 100644 index f87d6070050..00000000000 --- a/netcore/System.Private.CoreLib/shared/System/Diagnostics/Tracing/TraceLogging/ConcurrentSet.cs +++ /dev/null @@ -1,129 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -#if ES_BUILD_STANDALONE -using System; -#endif -using System.Threading; - -#if ES_BUILD_STANDALONE -namespace Microsoft.Diagnostics.Tracing -#else -namespace System.Diagnostics.Tracing -#endif -{ - /// <summary> - /// TraceLogging: A very simple lock-free add-only dictionary. - /// Warning: this is a copy-by-value type. Copying performs a snapshot. - /// Accessing a readonly field always makes a copy of the field, so the - /// GetOrAdd method will not work as expected if called on a readonly field. - /// </summary> - /// <typeparam name="KeyType"> - /// The type of the key, used for TryGet. - /// </typeparam> - /// <typeparam name="ItemType"> - /// The type of the item, used for GetOrAdd. - /// </typeparam> - internal struct ConcurrentSet<KeyType, ItemType> - where ItemType : ConcurrentSetItem<KeyType, ItemType> - { - private ItemType[]? items; - - public ItemType? TryGet(KeyType key) - { - ItemType? item; - ItemType[]? oldItems = this.items; - - if (oldItems != null) - { - int lo = 0; - int hi = oldItems.Length; - do - { - int i = (lo + hi) / 2; - item = oldItems[i]; - - int cmp = item.Compare(key); - if (cmp == 0) - { - goto Done; - } - else if (cmp < 0) - { - lo = i + 1; - } - else - { - hi = i; - } - } - while (lo != hi); - } - - item = null; - - Done: - - return item; - } - - public ItemType GetOrAdd(ItemType newItem) - { - ItemType item; - ItemType[]? oldItems = this.items; - ItemType[] newItems; - - Retry: - - if (oldItems == null) - { - newItems = new ItemType[] { newItem }; - } - else - { - int lo = 0; - int hi = oldItems.Length; - do - { - int i = (lo + hi) / 2; - item = oldItems[i]; - - int cmp = item.Compare(newItem); - if (cmp == 0) - { - goto Done; - } - else if (cmp < 0) - { - lo = i + 1; - } - else - { - hi = i; - } - } - while (lo != hi); - - int oldLength = oldItems.Length; - newItems = new ItemType[oldLength + 1]; - Array.Copy(oldItems, newItems, lo); - newItems[lo] = newItem; - Array.Copy(oldItems, lo, newItems, lo + 1, oldLength - lo); - } - - newItems = Interlocked.CompareExchange(ref this.items, newItems, oldItems)!; - if (oldItems != newItems) - { - oldItems = newItems; - goto Retry; - } - - item = newItem; - - Done: - - return item; - } - } -} diff --git a/netcore/System.Private.CoreLib/shared/System/Diagnostics/Tracing/TraceLogging/ConcurrentSetItem.cs b/netcore/System.Private.CoreLib/shared/System/Diagnostics/Tracing/TraceLogging/ConcurrentSetItem.cs deleted file mode 100644 index 7ef078f9090..00000000000 --- a/netcore/System.Private.CoreLib/shared/System/Diagnostics/Tracing/TraceLogging/ConcurrentSetItem.cs +++ /dev/null @@ -1,23 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -#if ES_BUILD_STANDALONE -namespace Microsoft.Diagnostics.Tracing -#else -namespace System.Diagnostics.Tracing -#endif -{ - /// <summary> - /// TraceLogging: Abstract base class that must be inherited by items in a - /// ConcurrentSet. - /// </summary> - /// <typeparam name="KeyType">Type of the set's key.</typeparam> - /// <typeparam name="ItemType">Type of the derived class.</typeparam> - internal abstract class ConcurrentSetItem<KeyType, ItemType> - where ItemType : ConcurrentSetItem<KeyType, ItemType> - { - public abstract int Compare(ItemType other); - public abstract int Compare(KeyType key); - } -} diff --git a/netcore/System.Private.CoreLib/shared/System/Diagnostics/Tracing/TraceLogging/DataCollector.cs b/netcore/System.Private.CoreLib/shared/System/Diagnostics/Tracing/TraceLogging/DataCollector.cs deleted file mode 100644 index d6886b8b105..00000000000 --- a/netcore/System.Private.CoreLib/shared/System/Diagnostics/Tracing/TraceLogging/DataCollector.cs +++ /dev/null @@ -1,368 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -#if ES_BUILD_STANDALONE -using System; -using System.Diagnostics; -using Environment = Microsoft.Diagnostics.Tracing.Internal.Environment; -#endif -using System.Runtime.InteropServices; - -#if ES_BUILD_STANDALONE -namespace Microsoft.Diagnostics.Tracing -#else -namespace System.Diagnostics.Tracing -#endif -{ - /// <summary> - /// TraceLogging: This is the implementation of the DataCollector - /// functionality. To enable safe access to the DataCollector from - /// untrusted code, there is one thread-local instance of this structure - /// per thread. The instance must be Enabled before any data is written to - /// it. The instance must be Finished before the data is passed to - /// EventWrite. The instance must be Disabled before the arrays referenced - /// by the pointers are freed or unpinned. - /// </summary> - internal unsafe struct DataCollector - { - [ThreadStatic] - internal static DataCollector ThreadInstance; - - private byte* scratchEnd; - private EventSource.EventData* datasEnd; - private GCHandle* pinsEnd; - private EventSource.EventData* datasStart; - private byte* scratch; - private EventSource.EventData* datas; - private GCHandle* pins; - private byte[]? buffer; - private int bufferPos; - private int bufferNesting; // We may merge many fields int a single blob. If we are doing this we increment this. - private bool writingScalars; - - internal void Enable( - byte* scratch, - int scratchSize, - EventSource.EventData* datas, - int dataCount, - GCHandle* pins, - int pinCount) - { - this.datasStart = datas; - this.scratchEnd = scratch + scratchSize; - this.datasEnd = datas + dataCount; - this.pinsEnd = pins + pinCount; - this.scratch = scratch; - this.datas = datas; - this.pins = pins; - this.writingScalars = false; - } - - internal void Disable() - { - this = default; - } - - /// <summary> - /// Completes the list of scalars. Finish must be called before the data - /// descriptor array is passed to EventWrite. - /// </summary> - /// <returns> - /// A pointer to the next unused data descriptor, or datasEnd if they were - /// all used. (Descriptors may be unused if a string or array was null.) - /// </returns> - internal EventSource.EventData* Finish() - { - this.ScalarsEnd(); - return this.datas; - } - - internal void AddScalar(void* value, int size) - { - var pb = (byte*)value; - if (this.bufferNesting == 0) - { - byte* scratchOld = this.scratch; - byte* scratchNew = scratchOld + size; - if (this.scratchEnd < scratchNew) - { - throw new IndexOutOfRangeException(SR.EventSource_AddScalarOutOfRange); - } - - this.ScalarsBegin(); - this.scratch = scratchNew; - - for (int i = 0; i != size; i++) - { - scratchOld[i] = pb[i]; - } - } - else - { - int oldPos = this.bufferPos; - this.bufferPos = checked(this.bufferPos + size); - this.EnsureBuffer(); - Debug.Assert(buffer != null); - - for (int i = 0; i != size; i++, oldPos++) - { - this.buffer[oldPos] = pb[i]; - } - } - } - - internal void AddBinary(string? value, int size) - { - if (size > ushort.MaxValue) - { - size = ushort.MaxValue - 1; - } - - if (this.bufferNesting != 0) - { - this.EnsureBuffer(size + 2); - } - - this.AddScalar(&size, 2); - - if (size != 0) - { - if (this.bufferNesting == 0) - { - this.ScalarsEnd(); - this.PinArray(value, size); - } - else - { - int oldPos = this.bufferPos; - this.bufferPos = checked(this.bufferPos + size); - this.EnsureBuffer(); - Debug.Assert(buffer != null); - - fixed (void* p = value) - { - Marshal.Copy((IntPtr)p, buffer, oldPos, size); - } - } - } - } - - internal void AddNullTerminatedString(string? value) - { - // Treat null strings as empty strings. - if (value == null) - { - value = string.Empty; - } - - // Calculate the size of the string including the trailing NULL char. - // Don't use value.Length here because string allows for embedded NULL characters. - int nullCharIndex = value.IndexOf((char)0); - if (nullCharIndex < 0) - { - nullCharIndex = value.Length; - } - int size = (nullCharIndex + 1) * 2; - - if (this.bufferNesting != 0) - { - this.EnsureBuffer(size); - } - - if (this.bufferNesting == 0) - { - this.ScalarsEnd(); - this.PinArray(value, size); - } - else - { - int oldPos = this.bufferPos; - this.bufferPos = checked(this.bufferPos + size); - this.EnsureBuffer(); - Debug.Assert(buffer != null); - - fixed (void* p = value) - { - Marshal.Copy((IntPtr)p, buffer, oldPos, size); - } - } - } - - internal void AddBinary(Array value, int size) - { - this.AddArray(value, size, 1); - } - - internal void AddArray(Array? value, int length, int itemSize) - { - if (length > ushort.MaxValue) - { - length = ushort.MaxValue; - } - - int size = length * itemSize; - if (this.bufferNesting != 0) - { - this.EnsureBuffer(size + 2); - } - - this.AddScalar(&length, 2); - - if (length != 0) - { - if (this.bufferNesting == 0) - { - this.ScalarsEnd(); - this.PinArray(value, size); - } - else - { - int oldPos = this.bufferPos; - this.bufferPos = checked(this.bufferPos + size); - this.EnsureBuffer(); - Debug.Assert(value != null && buffer != null); - Buffer.BlockCopy(value, 0, this.buffer, oldPos, size); - } - } - } - - /// <summary> - /// Marks the start of a non-blittable array or enumerable. - /// </summary> - /// <returns>Bookmark to be passed to EndBufferedArray.</returns> - internal int BeginBufferedArray() - { - this.BeginBuffered(); - this.bufferPos += 2; // Reserve space for the array length (filled in by EndEnumerable) - return this.bufferPos; - } - - /// <summary> - /// Marks the end of a non-blittable array or enumerable. - /// </summary> - /// <param name="bookmark">The value returned by BeginBufferedArray.</param> - /// <param name="count">The number of items in the array.</param> - internal void EndBufferedArray(int bookmark, int count) - { - this.EnsureBuffer(); - Debug.Assert(buffer != null); - this.buffer[bookmark - 2] = unchecked((byte)count); - this.buffer[bookmark - 1] = unchecked((byte)(count >> 8)); - this.EndBuffered(); - } - - /// <summary> - /// Marks the start of dynamically-buffered data. - /// </summary> - internal void BeginBuffered() - { - this.ScalarsEnd(); - this.bufferNesting++; - } - - /// <summary> - /// Marks the end of dynamically-buffered data. - /// </summary> - internal void EndBuffered() - { - this.bufferNesting--; - - if (this.bufferNesting == 0) - { - /* - TODO (perf): consider coalescing adjacent buffered regions into a - single buffer, similar to what we're already doing for adjacent - scalars. In addition, if a type contains a buffered region adjacent - to a blittable array, and the blittable array is small, it would be - more efficient to buffer the array instead of pinning it. - */ - - this.EnsureBuffer(); - Debug.Assert(buffer != null); - this.PinArray(this.buffer, this.bufferPos); - this.buffer = null; - this.bufferPos = 0; - } - } - - private void EnsureBuffer() - { - int required = this.bufferPos; - if (this.buffer == null || this.buffer.Length < required) - { - this.GrowBuffer(required); - } - } - - private void EnsureBuffer(int additionalSize) - { - int required = this.bufferPos + additionalSize; - if (this.buffer == null || this.buffer.Length < required) - { - this.GrowBuffer(required); - } - } - - private void GrowBuffer(int required) - { - int newSize = this.buffer == null ? 64 : this.buffer.Length; - - do - { - newSize *= 2; - } - while (newSize < required); - - Array.Resize(ref this.buffer, newSize); - } - - private void PinArray(object? value, int size) - { - GCHandle* pinsTemp = this.pins; - if (this.pinsEnd <= pinsTemp) - { - throw new IndexOutOfRangeException(SR.EventSource_PinArrayOutOfRange); - } - - EventSource.EventData* datasTemp = this.datas; - if (this.datasEnd <= datasTemp) - { - throw new IndexOutOfRangeException(SR.EventSource_DataDescriptorsOutOfRange); - } - - this.pins = pinsTemp + 1; - this.datas = datasTemp + 1; - - *pinsTemp = GCHandle.Alloc(value, GCHandleType.Pinned); - datasTemp->DataPointer = pinsTemp->AddrOfPinnedObject(); - datasTemp->m_Size = size; - } - - private void ScalarsBegin() - { - if (!this.writingScalars) - { - EventSource.EventData* datasTemp = this.datas; - if (this.datasEnd <= datasTemp) - { - throw new IndexOutOfRangeException(SR.EventSource_DataDescriptorsOutOfRange); - } - - datasTemp->DataPointer = (IntPtr)this.scratch; - this.writingScalars = true; - } - } - - private void ScalarsEnd() - { - if (this.writingScalars) - { - EventSource.EventData* datasTemp = this.datas; - datasTemp->m_Size = checked((int)(this.scratch - (byte*)datasTemp->m_Ptr)); - this.datas = datasTemp + 1; - this.writingScalars = false; - } - } - } -} diff --git a/netcore/System.Private.CoreLib/shared/System/Diagnostics/Tracing/TraceLogging/EmptyStruct.cs b/netcore/System.Private.CoreLib/shared/System/Diagnostics/Tracing/TraceLogging/EmptyStruct.cs deleted file mode 100644 index bc7fb8c3462..00000000000 --- a/netcore/System.Private.CoreLib/shared/System/Diagnostics/Tracing/TraceLogging/EmptyStruct.cs +++ /dev/null @@ -1,17 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -#if ES_BUILD_STANDALONE -namespace Microsoft.Diagnostics.Tracing -#else -namespace System.Diagnostics.Tracing -#endif -{ - /// <summary> - /// TraceLogging: Empty struct indicating no payload data. - /// </summary> - internal struct EmptyStruct - { - } -} diff --git a/netcore/System.Private.CoreLib/shared/System/Diagnostics/Tracing/TraceLogging/EnumHelper.cs b/netcore/System.Private.CoreLib/shared/System/Diagnostics/Tracing/TraceLogging/EnumHelper.cs deleted file mode 100644 index 33c085dcd17..00000000000 --- a/netcore/System.Private.CoreLib/shared/System/Diagnostics/Tracing/TraceLogging/EnumHelper.cs +++ /dev/null @@ -1,31 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -#if EVENTSOURCE_GENERICS -?using System; -using System.Reflection; - -#if ES_BUILD_STANDALONE -namespace Microsoft.Diagnostics.Tracing -#else -namespace System.Diagnostics.Tracing -#endif -{ - /// <summary> - /// Provides support for casting enums to their underlying type - /// from within generic context. - /// </summary> - /// <typeparam name="UnderlyingType"> - /// The underlying type of the enum. - /// </typeparam> - internal static class EnumHelper<UnderlyingType> - { - public static UnderlyingType Cast<ValueType>(ValueType value) - { - return (UnderlyingType)(object)value; - } - } - -} -#endif diff --git a/netcore/System.Private.CoreLib/shared/System/Diagnostics/Tracing/TraceLogging/EnumerableTypeInfo.cs b/netcore/System.Private.CoreLib/shared/System/Diagnostics/Tracing/TraceLogging/EnumerableTypeInfo.cs deleted file mode 100644 index 92a1a823f94..00000000000 --- a/netcore/System.Private.CoreLib/shared/System/Diagnostics/Tracing/TraceLogging/EnumerableTypeInfo.cs +++ /dev/null @@ -1,68 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -#if ES_BUILD_STANDALONE -using System; -using System.Diagnostics; -#endif -using System.Collections; -using System.Collections.Generic; - -#if ES_BUILD_STANDALONE -namespace Microsoft.Diagnostics.Tracing -#else -namespace System.Diagnostics.Tracing -#endif -{ - internal sealed class EnumerableTypeInfo : TraceLoggingTypeInfo - { - private readonly TraceLoggingTypeInfo elementInfo; - - public EnumerableTypeInfo(Type type, TraceLoggingTypeInfo elementInfo) - : base(type) - { - this.elementInfo = elementInfo; - } - - public override void WriteMetadata( - TraceLoggingMetadataCollector collector, - string? name, - EventFieldFormat format) - { - collector.BeginBufferedArray(); - this.elementInfo.WriteMetadata(collector, name, format); - collector.EndBufferedArray(); - } - - public override void WriteData(TraceLoggingDataCollector collector, PropertyValue value) - { - int bookmark = collector.BeginBufferedArray(); - - int count = 0; - IEnumerable? enumerable = (IEnumerable?)value.ReferenceValue; - if (enumerable != null) - { - foreach (object? element in enumerable) - { - this.elementInfo.WriteData(collector, elementInfo.PropertyValueFactory(element)); - count++; - } - } - - collector.EndBufferedArray(bookmark, count); - } - - public override object? GetData(object? value) - { - Debug.Assert(value != null, "null accepted only for some overrides"); - var iterType = (IEnumerable)value; - List<object?> serializedEnumerable = new List<object?>(); - foreach (object? element in iterType) - { - serializedEnumerable.Add(elementInfo.GetData(element)); - } - return serializedEnumerable.ToArray(); - } - } -} diff --git a/netcore/System.Private.CoreLib/shared/System/Diagnostics/Tracing/TraceLogging/EventDataAttribute.cs b/netcore/System.Private.CoreLib/shared/System/Diagnostics/Tracing/TraceLogging/EventDataAttribute.cs deleted file mode 100644 index cd88c81c3dc..00000000000 --- a/netcore/System.Private.CoreLib/shared/System/Diagnostics/Tracing/TraceLogging/EventDataAttribute.cs +++ /dev/null @@ -1,148 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -#if ES_BUILD_STANDALONE -using System; -#endif - -#if ES_BUILD_STANDALONE -namespace Microsoft.Diagnostics.Tracing -#else -namespace System.Diagnostics.Tracing -#endif -{ - /// <summary> - /// Used when authoring types that will be passed to EventSource.Write. - /// EventSource.Write<T> only works when T is either an anonymous type - /// or a type with an [EventData] attribute. In addition, the properties - /// of T must be supported property types. Supported property types include - /// simple built-in types (int, string, Guid, DateTime, DateTimeOffset, - /// KeyValuePair, etc.), anonymous types that only contain supported types, - /// types with an [EventData] attribute, arrays of the above, and IEnumerable - /// of the above. - /// </summary> - [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct, Inherited = false)] - public class EventDataAttribute - : Attribute - { - private EventLevel level = (EventLevel)(-1); - private EventOpcode opcode = (EventOpcode)(-1); - - /// <summary> - /// Gets or sets the name to use if this type is used for an - /// implicitly-named event or an implicitly-named property. - /// - /// Example 1: - /// - /// EventSource.Write(null, new T()); // implicitly-named event - /// - /// The name of the event will be determined as follows: - /// - /// if (T has an EventData attribute and attribute.Name != null) - /// eventName = attribute.Name; - /// else - /// eventName = typeof(T).Name; - /// - /// Example 2: - /// - /// EventSource.Write(name, new { _1 = new T() }); // implicitly-named field - /// - /// The name of the field will be determined as follows: - /// - /// if (T has an EventData attribute and attribute.Name != null) - /// fieldName = attribute.Name; - /// else - /// fieldName = typeof(T).Name; - /// </summary> - public string? Name - { - get; - set; - } - - /// <summary> - /// Gets or sets the level to use for the event. - /// Invalid levels (outside the range 0..255) are treated as unset. - /// Note that the Level attribute can bubble-up, i.e. if a type contains - /// a sub-object (a field or property), and the sub-object's type has a - /// TraceLoggingEvent attribute, the Level from the sub-object's attribute - /// can affect the event's level. - /// - /// Example: for EventSource.Write(name, options, data), the level of the - /// event will be determined as follows: - /// - /// if (options.Level has been set) - /// eventLevel = options.Level; - /// else if (data.GetType() has a TraceLoggingEvent attribute and attribute.Level has been set) - /// eventLevel = attribute.Level; - /// else if (a field/property contained in data has a TraceLoggingEvent attribute and attribute.Level has been set) - /// eventLevel = attribute.Level; - /// else - /// eventLevel = EventLevel.LogAlways; - /// </summary> - internal EventLevel Level - { - get => this.level; - set => this.level = value; - } - - /// <summary> - /// Gets or sets the opcode to use for the event. - /// Invalid opcodes (outside the range 0..255) are treated as unset. - /// Note that the Opcode attribute can bubble-up, i.e. if a type contains - /// a sub-object (a field or property), and the sub-object's type has a - /// TraceLoggingEvent attribute, the Opcode from the sub-object's attribute - /// can affect the event's opcode. - /// - /// Example: for EventSource.Write(name, options, data), the opcode of the - /// event will be determined as follows: - /// - /// if (options.Opcode has been set) - /// eventOpcode = options.Opcode; - /// else if (data.GetType() has a TraceLoggingEvent attribute and attribute.Opcode has been set) - /// eventOpcode = attribute.Opcode; - /// else if (a field/property contained in data has a TraceLoggingEvent attribute and attribute.Opcode has been set) - /// eventOpcode = attribute.Opcode; - /// else - /// eventOpcode = EventOpcode.Info; - /// </summary> - internal EventOpcode Opcode - { - get => this.opcode; - set => this.opcode = value; - } - - /// <summary> - /// Gets or sets the keywords to use for the event. - /// Note that the Keywords attribute can bubble-up, i.e. if a type contains - /// a sub-object (a field or property), and the sub-object's type has a - /// TraceLoggingEvent attribute, the Keywords from the sub-object's attribute - /// can affect the event's keywords. - /// - /// Example: for EventSource.Write(name, options, data), the keywords of the - /// event will be determined as follows: - /// - /// eventKeywords = options.Keywords; - /// if (data.GetType() has a TraceLoggingEvent attribute) - /// eventKeywords |= attribute.Keywords; - /// if (a field/property contained in data has a TraceLoggingEvent attribute) - /// eventKeywords |= attribute.Keywords; - /// </summary> - internal EventKeywords Keywords - { - get; - set; - } - - /// <summary> - /// Gets or sets the flags for an event. These flags are ignored by ETW, - /// but can have meaning to the event consumer. - /// </summary> - internal EventTags Tags - { - get; - set; - } - } -} diff --git a/netcore/System.Private.CoreLib/shared/System/Diagnostics/Tracing/TraceLogging/EventFieldAttribute.cs b/netcore/System.Private.CoreLib/shared/System/Diagnostics/Tracing/TraceLogging/EventFieldAttribute.cs deleted file mode 100644 index cad4a9a0683..00000000000 --- a/netcore/System.Private.CoreLib/shared/System/Diagnostics/Tracing/TraceLogging/EventFieldAttribute.cs +++ /dev/null @@ -1,78 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -#if ES_BUILD_STANDALONE -using System; -#endif - -#if ES_BUILD_STANDALONE -namespace Microsoft.Diagnostics.Tracing -#else -namespace System.Diagnostics.Tracing -#endif -{ - /// <summary> - /// Tags are flags that are not interpreted by EventSource but are passed along - /// to the EventListener. The EventListener determines the semantics of the flags. - /// </summary> - [Flags] - public enum EventFieldTags - { - /// <summary> - /// No special traits are added to the field. - /// </summary> - None = 0, - - /* Bits below 0x10000 are available for any use by the provider. */ - /* Bits at or above 0x10000 are reserved for definition by Microsoft. */ - } - - /// <summary> - /// TraceLogging: used when authoring types that will be passed to EventSource.Write. - /// Controls how a field or property is handled when it is written as a - /// field in a TraceLogging event. Apply this attribute to a field or - /// property if the default handling is not correct. (Apply the - /// TraceLoggingIgnore attribute if the property should not be - /// included as a field in the event.) - /// The default for Name is null, which means that the name of the - /// underlying field or property will be used as the event field's name. - /// The default for PiiTag is 0, which means that the event field does not - /// contain personally-identifiable information. - /// </summary> - [AttributeUsage(AttributeTargets.Property)] - public class EventFieldAttribute - : Attribute - { - /// <summary> - /// User defined options for the field. These are not interpreted by the EventSource - /// but are available to the Listener. See EventFieldSettings for details - /// </summary> - public EventFieldTags Tags - { - get; - set; - } - - /// <summary> - /// Gets or sets the name to use for the field. This defaults to null. - /// If null, the name of the corresponding property will be used - /// as the event field's name. - /// TODO REMOVE - /// </summary> - internal string? Name - { - get; - set; - } - - /// <summary> - /// Gets or sets a field formatting hint. - /// </summary> - public EventFieldFormat Format - { - get; - set; - } - } -} diff --git a/netcore/System.Private.CoreLib/shared/System/Diagnostics/Tracing/TraceLogging/EventFieldFormat.cs b/netcore/System.Private.CoreLib/shared/System/Diagnostics/Tracing/TraceLogging/EventFieldFormat.cs deleted file mode 100644 index 20672e68aa9..00000000000 --- a/netcore/System.Private.CoreLib/shared/System/Diagnostics/Tracing/TraceLogging/EventFieldFormat.cs +++ /dev/null @@ -1,130 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -#if ES_BUILD_STANDALONE -namespace Microsoft.Diagnostics.Tracing -#else -namespace System.Diagnostics.Tracing -#endif -{ - /// <summary> - /// Provides a hint that may be used by an event listener when formatting - /// an event field for display. Note that the event listener may ignore the - /// hint if it does not recognize a particular combination of type and format. - /// Similar to TDH_OUTTYPE. - /// </summary> - public enum EventFieldFormat - { - /// <summary> - /// Field receives default formatting based on the field's underlying type. - /// </summary> - Default = 0, -#if false - /// <summary> - /// Field should not be displayed. - /// </summary> - NoPrint = 1, -#endif - /// <summary> - /// Field should be formatted as character or string data. - /// Typically applied to 8-bit or 16-bit integers. - /// This is the default format for String and Char types. - /// </summary> - String = 2, - - /// <summary> - /// Field should be formatted as boolean data. Typically applied to 8-bit - /// or 32-bit integers. This is the default format for the Boolean type. - /// </summary> - Boolean = 3, - - /// <summary> - /// Field should be formatted as hexadecimal data. Typically applied to - /// integer types. - /// </summary> - Hexadecimal = 4, - -#if false - /// <summary> - /// Field should be formatted as a process identifier. Typically applied to - /// 32-bit integer types. - /// </summary> - ProcessId = 5, - - /// <summary> - /// Field should be formatted as a thread identifier. Typically applied to - /// 32-bit integer types. - /// </summary> - ThreadId = 6, - - /// <summary> - /// Field should be formatted as an Internet port. Typically applied to 16-bit integer - /// types. - /// </summary> - Port = 7, - /// <summary> - /// Field should be formatted as an Internet Protocol v4 address. Typically applied to - /// 32-bit integer types. - /// </summary> - Ipv4Address = 8, - - /// <summary> - /// Field should be formatted as an Internet Protocol v6 address. Typically applied to - /// byte[] types. - /// </summary> - Ipv6Address = 9, - /// <summary> - /// Field should be formatted as a SOCKADDR. Typically applied to byte[] types. - /// </summary> - SocketAddress = 10, -#endif - /// <summary> - /// Field should be formatted as XML string data. Typically applied to - /// strings or arrays of 8-bit or 16-bit integers. - /// </summary> - Xml = 11, - - /// <summary> - /// Field should be formatted as JSON string data. Typically applied to - /// strings or arrays of 8-bit or 16-bit integers. - /// </summary> - Json = 12, -#if false - /// <summary> - /// Field should be formatted as a Win32 error code. Typically applied to - /// 32-bit integer types. - /// </summary> - Win32Error = 13, - - /// <summary> - /// Field should be formatted as an NTSTATUS code. Typically applied to - /// 32-bit integer types. - /// </summary> - NTStatus = 14, -#endif - /// <summary> - /// Field should be formatted as an HRESULT code. Typically applied to - /// 32-bit integer types. - /// </summary> - HResult = 15, -#if false - /// <summary> - /// Field should be formatted as a FILETIME. Typically applied to 64-bit - /// integer types. This is the default format for DateTime types. - /// </summary> - FileTime = 16, - /// <summary> - /// When applied to a numeric type, indicates that the type should be formatted - /// as a signed integer. This is the default format for signed integer types. - /// </summary> - Signed = 17, - - /// <summary> - /// When applied to a numeric type, indicates that the type should be formatted - /// as an unsigned integer. This is the default format for unsigned integer types. - /// </summary> - Unsigned = 18, -#endif - } -} diff --git a/netcore/System.Private.CoreLib/shared/System/Diagnostics/Tracing/TraceLogging/EventIgnoreAttribute.cs b/netcore/System.Private.CoreLib/shared/System/Diagnostics/Tracing/TraceLogging/EventIgnoreAttribute.cs deleted file mode 100644 index 81411ebe2d3..00000000000 --- a/netcore/System.Private.CoreLib/shared/System/Diagnostics/Tracing/TraceLogging/EventIgnoreAttribute.cs +++ /dev/null @@ -1,27 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -#if ES_BUILD_STANDALONE -using System; -#endif - -#if ES_BUILD_STANDALONE -namespace Microsoft.Diagnostics.Tracing -#else -namespace System.Diagnostics.Tracing -#endif -{ - /// <summary> - /// Used when authoring types that will be passed to EventSource.Write. - /// By default, EventSource.Write will write all of an object's public - /// properties to the event payload. Apply [EventIgnore] to a public - /// property to prevent EventSource.Write from including the property in - /// the event. - /// </summary> - [AttributeUsage(AttributeTargets.Property)] - public class EventIgnoreAttribute - : Attribute - { - } -} diff --git a/netcore/System.Private.CoreLib/shared/System/Diagnostics/Tracing/TraceLogging/EventPayload.cs b/netcore/System.Private.CoreLib/shared/System/Diagnostics/Tracing/TraceLogging/EventPayload.cs deleted file mode 100644 index b177a7526d3..00000000000 --- a/netcore/System.Private.CoreLib/shared/System/Diagnostics/Tracing/TraceLogging/EventPayload.cs +++ /dev/null @@ -1,150 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -#if ES_BUILD_STANDALONE -using System; -using System.Diagnostics; -#endif -using System.Collections; -using System.Collections.Generic; -using System.Diagnostics.CodeAnalysis; - -#if ES_BUILD_STANDALONE -namespace Microsoft.Diagnostics.Tracing -#else -namespace System.Diagnostics.Tracing -#endif -{ - /// <summary> - /// EventPayload class holds the list of parameters and their corresponding values for user defined types passed to - /// EventSource APIs. - /// Preserving the order of the elements as they were found inside user defined types is the most important characteristic of this class. - /// </summary> - internal class EventPayload : IDictionary<string, object?> - { - internal EventPayload(List<string> payloadNames, List<object?> payloadValues) - { - Debug.Assert(payloadNames.Count == payloadValues.Count); - - m_names = payloadNames; - m_values = payloadValues; - } - - public ICollection<string> Keys => m_names; - public ICollection<object?> Values => m_values; - - public object? this[string key] - { - get - { - if (key == null) - throw new System.ArgumentNullException(nameof(key)); - - int position = 0; - foreach (string name in m_names) - { - if (name == key) - { - return m_values[position]; - } - position++; - } - - throw new System.Collections.Generic.KeyNotFoundException(SR.Format(SR.Arg_KeyNotFoundWithKey, key)); - } - set => throw new System.NotSupportedException(); - } - - public void Add(string key, object? value) - { - throw new System.NotSupportedException(); - } - - public void Add(KeyValuePair<string, object?> payloadEntry) - { - throw new System.NotSupportedException(); - } - - public void Clear() - { - throw new System.NotSupportedException(); - } - - public bool Contains(KeyValuePair<string, object?> entry) - { - return ContainsKey(entry.Key); - } - - public bool ContainsKey(string key) - { - if (key == null) - throw new System.ArgumentNullException(nameof(key)); - - foreach (string item in m_names) - { - if (item == key) - return true; - } - return false; - } - - public int Count => m_names.Count; - - public bool IsReadOnly => true; - - public IEnumerator<KeyValuePair<string, object?>> GetEnumerator() - { - for (int i = 0; i < Keys.Count; i++) - { - yield return new KeyValuePair<string, object?>(this.m_names[i], this.m_values[i]); - } - } - - IEnumerator IEnumerable.GetEnumerator() - { - var instance = this as IEnumerable<KeyValuePair<string, object?>>; - return instance.GetEnumerator(); - } - - public void CopyTo(KeyValuePair<string, object?>[] payloadEntries, int count) - { - throw new System.NotSupportedException(); - } - - public bool Remove(string key) - { - throw new System.NotSupportedException(); - } - - public bool Remove(KeyValuePair<string, object?> entry) - { - throw new System.NotSupportedException(); - } - - public bool TryGetValue(string key, [MaybeNullWhen(false)] out object? value) - { - if (key == null) - throw new System.ArgumentNullException(nameof(key)); - - int position = 0; - foreach (string name in m_names) - { - if (name == key) - { - value = m_values[position]; - return true; - } - position++; - } - - value = default; - return false; - } - -#region private - private readonly List<string> m_names; - private readonly List<object?> m_values; -#endregion - } -} diff --git a/netcore/System.Private.CoreLib/shared/System/Diagnostics/Tracing/TraceLogging/EventSourceActivity.cs b/netcore/System.Private.CoreLib/shared/System/Diagnostics/Tracing/TraceLogging/EventSourceActivity.cs deleted file mode 100644 index 25c8563346a..00000000000 --- a/netcore/System.Private.CoreLib/shared/System/Diagnostics/Tracing/TraceLogging/EventSourceActivity.cs +++ /dev/null @@ -1,314 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -#if ES_BUILD_STANDALONE -using System; -using System.Diagnostics; -#endif - -#if ES_BUILD_STANDALONE -namespace Microsoft.Diagnostics.Tracing -#else -namespace System.Diagnostics.Tracing -#endif -{ - /// <summary> - /// Provides support for EventSource activities by marking the start and - /// end of a particular operation. - /// </summary> - internal sealed class EventSourceActivity - : IDisposable - { - /// <summary> - /// Initializes a new instance of the EventSourceActivity class that - /// is attached to the specified event source. The new activity will - /// not be attached to any related (parent) activity. - /// The activity is created in the Initialized state. - /// </summary> - /// <param name="eventSource"> - /// The event source to which the activity information is written. - /// </param> - public EventSourceActivity(EventSource eventSource) - { - if (eventSource == null) - throw new ArgumentNullException(nameof(eventSource)); - - this.eventSource = eventSource; - } - - /// <summary> - /// You can make an activity out of just an EventSource. - /// </summary> - public static implicit operator EventSourceActivity(EventSource eventSource) => - new EventSourceActivity(eventSource); - - /* Properties */ - /// <summary> - /// Gets the event source to which this activity writes events. - /// </summary> - public EventSource EventSource => this.eventSource; - - /// <summary> - /// Gets this activity's unique identifier, or the default Guid if the - /// event source was disabled when the activity was initialized. - /// </summary> - public Guid Id => this.activityId; - -#if false // don't expose RelatedActivityId unless there is a need. - /// <summary> - /// Gets the unique identifier of this activity's related (parent) - /// activity. - /// </summary> - public Guid RelatedId - { - get { return this.relatedActivityId; } - } -#endif - - /// <summary> - /// Writes a Start event with the specified name and data. If the start event is not active (because the provider - /// is not on or keyword-level indicates the event is off, then the returned activity is simply the 'this' pointer - /// and it is effectively like start did not get called. - /// - /// A new activityID GUID is generated and the returned - /// EventSourceActivity remembers this activity and will mark every event (including the start stop and any writes) - /// with this activityID. In addition the Start activity will log a 'relatedActivityID' that was the activity - /// ID before the start event. This way event processors can form a linked list of all the activities that - /// caused this one (directly or indirectly). - /// </summary> - /// <param name="eventName"> - /// The name to use for the event. It is strongly suggested that this name end in 'Start' (e.g. DownloadStart). - /// If you do this, then the Stop() method will automatically replace the 'Start' suffix with a 'Stop' suffix. - /// </param> - /// <param name="options">Allow options (keywords, level) to be set for the write associated with this start - /// These will also be used for the stop event.</param> - /// <param name="data">The data to include in the event.</param> - public EventSourceActivity Start<T>(string? eventName, EventSourceOptions options, T data) - { - return this.Start(eventName, ref options, ref data); - } - /// <summary> - /// Shortcut version see Start(string eventName, EventSourceOptions options, T data) Options is empty (no keywords - /// and level==Info) Data payload is empty. - /// </summary> - public EventSourceActivity Start(string? eventName) - { - EventSourceOptions options = default; - EmptyStruct data = default; - return this.Start(eventName, ref options, ref data); - } - /// <summary> - /// Shortcut version see Start(string eventName, EventSourceOptions options, T data). Data payload is empty. - /// </summary> - public EventSourceActivity Start(string? eventName, EventSourceOptions options) - { - EmptyStruct data = default; - return this.Start(eventName, ref options, ref data); - } - /// <summary> - /// Shortcut version see Start(string eventName, EventSourceOptions options, T data) Options is empty (no keywords - /// and level==Info) - /// </summary> - public EventSourceActivity Start<T>(string? eventName, T data) - { - EventSourceOptions options = default; - return this.Start(eventName, ref options, ref data); - } - - /// <summary> - /// Writes a Stop event with the specified data, and sets the activity - /// to the Stopped state. The name is determined by the eventName used in Start. - /// If that Start event name is suffixed with 'Start' that is removed, and regardless - /// 'Stop' is appended to the result to form the Stop event name. - /// May only be called when the activity is in the Started state. - /// </summary> - /// <param name="data">The data to include in the event.</param> - public void Stop<T>(T data) - { - this.Stop(null, ref data); - } - /// <summary> - /// Used if you wish to use the non-default stop name (which is the start name with Start replace with 'Stop') - /// This can be useful to indicate unusual ways of stopping (but it is still STRONGLY recommended that - /// you start with the same prefix used for the start event and you end with the 'Stop' suffix. - /// </summary> - public void Stop<T>(string? eventName) - { - EmptyStruct data = default; - this.Stop(eventName, ref data); - } - /// <summary> - /// Used if you wish to use the non-default stop name (which is the start name with Start replace with 'Stop') - /// This can be useful to indicate unusual ways of stopping (but it is still STRONGLY recommended that - /// you start with the same prefix used for the start event and you end with the 'Stop' suffix. - /// </summary> - public void Stop<T>(string? eventName, T data) - { - this.Stop(eventName, ref data); - } - - /// <summary> - /// Writes an event associated with this activity to the eventSource associated with this activity. - /// May only be called when the activity is in the Started state. - /// </summary> - /// <param name="eventName"> - /// The name to use for the event. If null, the name is determined from - /// data's type. - /// </param> - /// <param name="options"> - /// The options to use for the event. - /// </param> - /// <param name="data">The data to include in the event.</param> - public void Write<T>(string? eventName, EventSourceOptions options, T data) - { - this.Write(this.eventSource, eventName, ref options, ref data); - } - /// <summary> - /// Writes an event associated with this activity. - /// May only be called when the activity is in the Started state. - /// </summary> - /// <param name="eventName"> - /// The name to use for the event. If null, the name is determined from - /// data's type. - /// </param> - /// <param name="data">The data to include in the event.</param> - public void Write<T>(string? eventName, T data) - { - EventSourceOptions options = default; - this.Write(this.eventSource, eventName, ref options, ref data); - } - /// <summary> - /// Writes a trivial event associated with this activity. - /// May only be called when the activity is in the Started state. - /// </summary> - /// <param name="eventName"> - /// The name to use for the event. Must not be null. - /// </param> - /// <param name="options"> - /// The options to use for the event. - /// </param> - public void Write(string? eventName, EventSourceOptions options) - { - EmptyStruct data = default; - this.Write(this.eventSource, eventName, ref options, ref data); - } - /// <summary> - /// Writes a trivial event associated with this activity. - /// May only be called when the activity is in the Started state. - /// </summary> - /// <param name="eventName"> - /// The name to use for the event. Must not be null. - /// </param> - public void Write(string? eventName) - { - EventSourceOptions options = default; - EmptyStruct data = default; - this.Write(this.eventSource, eventName, ref options, ref data); - } - /// <summary> - /// Writes an event to a arbitrary eventSource stamped with the activity ID of this activity. - /// </summary> - public void Write<T>(EventSource source, string? eventName, EventSourceOptions options, T data) - { - this.Write(source, eventName, ref options, ref data); - } - - /// <summary> - /// Releases any unmanaged resources associated with this object. - /// If the activity is in the Started state, calls Stop(). - /// </summary> - public void Dispose() - { - if (this.state == State.Started) - { - EmptyStruct data = default; - this.Stop(null, ref data); - } - } - -#region private - private EventSourceActivity Start<T>(string? eventName, ref EventSourceOptions options, ref T data) - { - if (this.state != State.Started) - throw new InvalidOperationException(); - - // If the source is not on at all, then we don't need to do anything and we can simply return ourselves. - if (!this.eventSource.IsEnabled()) - return this; - - var newActivity = new EventSourceActivity(eventSource); - if (!this.eventSource.IsEnabled(options.Level, options.Keywords)) - { - // newActivity.relatedActivityId = this.Id; - Guid relatedActivityId = this.Id; - newActivity.activityId = Guid.NewGuid(); - newActivity.startStopOptions = options; - newActivity.eventName = eventName; - newActivity.startStopOptions.Opcode = EventOpcode.Start; - this.eventSource.Write(eventName, ref newActivity.startStopOptions, ref newActivity.activityId, ref relatedActivityId, ref data); - } - else - { - // If we are not active, we don't set the eventName, which basically also turns off the Stop event as well. - newActivity.activityId = this.Id; - } - - return newActivity; - } - - private void Write<T>(EventSource eventSource, string? eventName, ref EventSourceOptions options, ref T data) - { - if (this.state != State.Started) - throw new InvalidOperationException(); // Write after stop. - if (eventName == null) - throw new ArgumentNullException(); - - eventSource.Write(eventName, ref options, ref this.activityId, ref s_empty, ref data); - } - - private void Stop<T>(string? eventName, ref T data) - { - if (this.state != State.Started) - throw new InvalidOperationException(); - - // If start was not fired, then stop isn't as well. - if (!StartEventWasFired) - return; - - Debug.Assert(this.eventName != null); - - this.state = State.Stopped; - if (eventName == null) - { - eventName = this.eventName; - if (eventName.EndsWith("Start")) - eventName = eventName.Substring(0, eventName.Length - 5); - eventName += "Stop"; - } - this.startStopOptions.Opcode = EventOpcode.Stop; - this.eventSource.Write(eventName, ref this.startStopOptions, ref this.activityId, ref s_empty, ref data); - } - - private enum State - { - Started, - Stopped - } - - /// <summary> - /// If eventName is non-null then we logged a start event - /// </summary> - private bool StartEventWasFired => eventName != null; - - private readonly EventSource eventSource; - private EventSourceOptions startStopOptions; - internal Guid activityId; - // internal Guid relatedActivityId; - private State state; - private string? eventName; - - internal static Guid s_empty; -#endregion - } -} diff --git a/netcore/System.Private.CoreLib/shared/System/Diagnostics/Tracing/TraceLogging/EventSourceOptions.cs b/netcore/System.Private.CoreLib/shared/System/Diagnostics/Tracing/TraceLogging/EventSourceOptions.cs deleted file mode 100644 index c6b675a420a..00000000000 --- a/netcore/System.Private.CoreLib/shared/System/Diagnostics/Tracing/TraceLogging/EventSourceOptions.cs +++ /dev/null @@ -1,107 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -#if ES_BUILD_STANDALONE -using System; -#endif - -#if ES_BUILD_STANDALONE -namespace Microsoft.Diagnostics.Tracing -#else -namespace System.Diagnostics.Tracing -#endif -{ - /// <summary> - /// Used when calling EventSource.Write. - /// Optional overrides for event settings such as Level, Keywords, or Opcode. - /// If overrides are not provided for a setting, default values will be used. - /// </summary> - public struct EventSourceOptions - { - internal EventKeywords keywords; - internal EventTags tags; - internal EventActivityOptions activityOptions; - internal byte level; - internal byte opcode; - internal byte valuesSet; - - internal const byte keywordsSet = 0x1; - internal const byte tagsSet = 0x2; - internal const byte levelSet = 0x4; - internal const byte opcodeSet = 0x8; - internal const byte activityOptionsSet = 0x10; - - /// <summary> - /// Gets or sets the level to use for the specified event. If this property - /// is unset, the event's level will be 5 (Verbose). - /// </summary> - public EventLevel Level - { - get => (EventLevel)this.level; - set - { - this.level = checked((byte)value); - this.valuesSet |= levelSet; - } - } - - /// <summary> - /// Gets or sets the opcode to use for the specified event. If this property - /// is unset, the event's opcode will 0 (Info). - /// </summary> - public EventOpcode Opcode - { - get => (EventOpcode)this.opcode; - set - { - this.opcode = checked((byte)value); - this.valuesSet |= opcodeSet; - } - } - - internal bool IsOpcodeSet => (this.valuesSet & opcodeSet) != 0; - - /// <summary> - /// Gets or sets the keywords to use for the specified event. If this - /// property is unset, the event's keywords will be 0. - /// </summary> - public EventKeywords Keywords - { - get => this.keywords; - set - { - this.keywords = value; - this.valuesSet |= keywordsSet; - } - } - - /// <summary> - /// Gets or sets the tags to use for the specified event. If this property is - /// unset, the event's tags will be 0. - /// </summary> - public EventTags Tags - { - get => this.tags; - set - { - this.tags = value; - this.valuesSet |= tagsSet; - } - } - - /// <summary> - /// Gets or sets the activity options for this specified events. If this property is - /// unset, the event's activity options will be 0. - /// </summary> - public EventActivityOptions ActivityOptions - { - get => this.activityOptions; - set - { - this.activityOptions = value; - this.valuesSet |= activityOptionsSet; - } - } - } -} diff --git a/netcore/System.Private.CoreLib/shared/System/Diagnostics/Tracing/TraceLogging/FieldMetadata.cs b/netcore/System.Private.CoreLib/shared/System/Diagnostics/Tracing/TraceLogging/FieldMetadata.cs deleted file mode 100644 index c42e84afdc7..00000000000 --- a/netcore/System.Private.CoreLib/shared/System/Diagnostics/Tracing/TraceLogging/FieldMetadata.cs +++ /dev/null @@ -1,230 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -#if ES_BUILD_STANDALONE -using System; -using System.Diagnostics; -#endif -using System.Text; - -#if ES_BUILD_STANDALONE -using Environment = Microsoft.Diagnostics.Tracing.Internal.Environment; -namespace Microsoft.Diagnostics.Tracing -#else -namespace System.Diagnostics.Tracing -#endif -{ - /// <summary> - /// TraceLogging: Contains the information needed to generate tracelogging - /// metadata for an event field. - /// </summary> - internal class FieldMetadata - { - /// <summary> - /// Name of the field - /// </summary> - private readonly string name; - - /// <summary> - /// The number of bytes in the UTF8 Encoding of 'name' INCLUDING a null terminator. - /// </summary> - private readonly int nameSize; - private readonly EventFieldTags tags; - private readonly byte[]? custom; - - /// <summary> - /// ETW supports fixed sized arrays. If inType has the InTypeFixedCountFlag then this is the - /// statically known count for the array. It is also used to encode the number of bytes of - /// custom meta-data if InTypeCustomCountFlag set. - /// </summary> - private readonly ushort fixedCount; - - private byte inType; - private byte outType; - - /// <summary> - /// Scalar or variable-length array. - /// </summary> - public FieldMetadata( - string name, - TraceLoggingDataType type, - EventFieldTags tags, - bool variableCount) - : this( - name, - type, - tags, - variableCount ? Statics.InTypeVariableCountFlag : (byte)0, - 0, - null) - { - } - - /// <summary> - /// Fixed-length array. - /// </summary> - public FieldMetadata( - string name, - TraceLoggingDataType type, - EventFieldTags tags, - ushort fixedCount) - : this( - name, - type, - tags, - Statics.InTypeFixedCountFlag, - fixedCount, - null) - { - } - - /// <summary> - /// Custom serializer - /// </summary> - public FieldMetadata( - string name, - TraceLoggingDataType type, - EventFieldTags tags, - byte[]? custom) - : this( - name, - type, - tags, - Statics.InTypeCustomCountFlag, - checked((ushort)(custom == null ? 0 : custom.Length)), - custom) - { - } - - private FieldMetadata( - string name, - TraceLoggingDataType dataType, - EventFieldTags tags, - byte countFlags, - ushort fixedCount = 0, - byte[]? custom = null) - { - if (name == null) - { - throw new ArgumentNullException( - nameof(name), - "This usually means that the object passed to Write is of a type that" - + " does not support being used as the top-level object in an event," - + " e.g. a primitive or built-in type."); - } - - Statics.CheckName(name); - int coreType = (int)dataType & Statics.InTypeMask; - this.name = name; - this.nameSize = Encoding.UTF8.GetByteCount(this.name) + 1; - this.inType = (byte)(coreType | countFlags); - this.outType = (byte)(((int)dataType >> 8) & Statics.OutTypeMask); - this.tags = tags; - this.fixedCount = fixedCount; - this.custom = custom; - - if (countFlags != 0) - { - if (coreType == (int)TraceLoggingDataType.Nil) - { - throw new NotSupportedException(SR.EventSource_NotSupportedArrayOfNil); - } - if (coreType == (int)TraceLoggingDataType.Binary) - { - throw new NotSupportedException(SR.EventSource_NotSupportedArrayOfBinary); - } - if (coreType == (int)TraceLoggingDataType.Utf16String || - coreType == (int)TraceLoggingDataType.MbcsString) - { - throw new NotSupportedException(SR.EventSource_NotSupportedArrayOfNullTerminatedString); - } - } - - if (((int)this.tags & 0xfffffff) != 0) - { - this.outType |= Statics.OutTypeChainFlag; - } - - if (this.outType != 0) - { - this.inType |= Statics.InTypeChainFlag; - } - } - - public void IncrementStructFieldCount() - { - this.inType |= Statics.InTypeChainFlag; - this.outType++; - if ((this.outType & Statics.OutTypeMask) == 0) - { - throw new NotSupportedException(SR.EventSource_TooManyFields); - } - } - - /// <summary> - /// This is the main routine for FieldMetaData. Basically it will serialize the data in - /// this structure as TraceLogging style meta-data into the array 'metaArray' starting at - /// 'pos' (pos is updated to reflect the bytes written). - /// - /// Note that 'metaData' can be null, in which case it only updates 'pos'. This is useful - /// for a 'two pass' approach where you figure out how big to make the array, and then you - /// fill it in. - /// </summary> - public void Encode(ref int pos, byte[]? metadata) - { - // Write out the null terminated UTF8 encoded name - if (metadata != null) - { - Encoding.UTF8.GetBytes(this.name, 0, this.name.Length, metadata, pos); - } - pos += this.nameSize; - - // Write 1 byte for inType - if (metadata != null) - { - metadata[pos] = this.inType; - } - pos++; - - // If InTypeChainFlag set, then write out the outType - if (0 != (this.inType & Statics.InTypeChainFlag)) - { - if (metadata != null) - { - metadata[pos] = this.outType; - } - pos++; - - // If OutTypeChainFlag set, then write out tags - if (0 != (this.outType & Statics.OutTypeChainFlag)) - { - Statics.EncodeTags((int)this.tags, ref pos, metadata); - } - } - - // If InTypeFixedCountFlag set, write out the fixedCount (2 bytes little endian) - if (0 != (this.inType & Statics.InTypeFixedCountFlag)) - { - if (metadata != null) - { - metadata[pos + 0] = unchecked((byte)this.fixedCount); - metadata[pos + 1] = (byte)(this.fixedCount >> 8); - } - pos += 2; - - // If InTypeCustomCountFlag set, write out the blob of custom meta-data. - if (Statics.InTypeCustomCountFlag == (this.inType & Statics.InTypeCountMask) && - this.fixedCount != 0) - { - if (metadata != null) - { - Debug.Assert(custom != null); - Buffer.BlockCopy(custom, 0, metadata, pos, this.fixedCount); - } - pos += this.fixedCount; - } - } - } - } -} diff --git a/netcore/System.Private.CoreLib/shared/System/Diagnostics/Tracing/TraceLogging/InvokeTypeInfo.cs b/netcore/System.Private.CoreLib/shared/System/Diagnostics/Tracing/TraceLogging/InvokeTypeInfo.cs deleted file mode 100644 index 3c5cf2541c1..00000000000 --- a/netcore/System.Private.CoreLib/shared/System/Diagnostics/Tracing/TraceLogging/InvokeTypeInfo.cs +++ /dev/null @@ -1,95 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -#if ES_BUILD_STANDALONE -using System; -#endif -using System.Collections.Generic; - -#if ES_BUILD_STANDALONE -namespace Microsoft.Diagnostics.Tracing -#else -namespace System.Diagnostics.Tracing -#endif -{ - /// <summary> - /// TraceLogging: An implementation of TraceLoggingTypeInfo that works - /// for arbitrary types. It writes all public instance properties of - /// the type. - /// </summary> - internal sealed class InvokeTypeInfo : TraceLoggingTypeInfo - { - internal readonly PropertyAnalysis[]? properties; - - public InvokeTypeInfo( - Type type, - TypeAnalysis typeAnalysis) - : base( - type, - typeAnalysis.name!, - typeAnalysis.level, - typeAnalysis.opcode, - typeAnalysis.keywords, - typeAnalysis.tags) - { - if (typeAnalysis.properties.Length != 0) - this.properties = typeAnalysis.properties; - } - - public override void WriteMetadata( - TraceLoggingMetadataCollector collector, - string? name, - EventFieldFormat format) - { - TraceLoggingMetadataCollector groupCollector = collector.AddGroup(name); - if (this.properties != null) - { - foreach (PropertyAnalysis property in this.properties) - { - EventFieldFormat propertyFormat = EventFieldFormat.Default; - EventFieldAttribute? propertyAttribute = property.fieldAttribute; - if (propertyAttribute != null) - { - groupCollector.Tags = propertyAttribute.Tags; - propertyFormat = propertyAttribute.Format; - } - - property.typeInfo.WriteMetadata( - groupCollector, - property.name, - propertyFormat); - } - } - } - - public override void WriteData(TraceLoggingDataCollector collector, PropertyValue value) - { - if (this.properties != null) - { - foreach (PropertyAnalysis property in this.properties) - { - property.typeInfo.WriteData(collector, property.getter(value)); - } - } - } - - public override object? GetData(object? value) - { - if (this.properties != null) - { - var membersNames = new List<string>(); - var membersValues = new List<object?>(); - for (int i = 0; i < this.properties.Length; i++) - { - object? propertyValue = properties[i].propertyInfo.GetValue(value); - membersNames.Add(properties[i].name); - membersValues.Add(properties[i].typeInfo.GetData(propertyValue)); - } - return new EventPayload(membersNames, membersValues); - } - - return null; - } - } -} diff --git a/netcore/System.Private.CoreLib/shared/System/Diagnostics/Tracing/TraceLogging/NameInfo.cs b/netcore/System.Private.CoreLib/shared/System/Diagnostics/Tracing/TraceLogging/NameInfo.cs deleted file mode 100644 index 17bf2eb2aed..00000000000 --- a/netcore/System.Private.CoreLib/shared/System/Diagnostics/Tracing/TraceLogging/NameInfo.cs +++ /dev/null @@ -1,126 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -#if ES_BUILD_STANDALONE -using System; -#endif -using System.Collections.Generic; -using System.Threading; - -#if ES_BUILD_STANDALONE -namespace Microsoft.Diagnostics.Tracing -#else -namespace System.Diagnostics.Tracing -#endif -{ - /// <summary> - /// TraceLogging: Stores the metadata and event identifier corresponding - /// to a tracelogging event type+name+tags combination. - /// </summary> - internal sealed class NameInfo - : ConcurrentSetItem<KeyValuePair<string, EventTags>, NameInfo> - { - /// <summary> - /// Insure that eventIds strictly less than 'eventId' will not be - /// used by the SelfDescribing events. - /// </summary> - internal static void ReserveEventIDsBelow(int eventId) - { - while (true) - { - int snapshot = lastIdentity; - int newIdentity = (lastIdentity & ~0xFFFFFF) + eventId; - newIdentity = Math.Max(newIdentity, snapshot); // Should be redundant. as we only create descriptors once. - if (Interlocked.CompareExchange(ref lastIdentity, newIdentity, snapshot) == snapshot) - break; - } - } - - private static int lastIdentity = Statics.TraceLoggingChannel << 24; - internal readonly string name; - internal readonly EventTags tags; - internal readonly int identity; - internal readonly byte[] nameMetadata; - - public NameInfo(string name, EventTags tags, int typeMetadataSize) - { - this.name = name; - this.tags = tags & Statics.EventTagsMask; - this.identity = Interlocked.Increment(ref lastIdentity); - - int tagsPos = 0; - Statics.EncodeTags((int)this.tags, ref tagsPos, null); - - this.nameMetadata = Statics.MetadataForString(name, tagsPos, 0, typeMetadataSize); - - tagsPos = 2; - Statics.EncodeTags((int)this.tags, ref tagsPos, this.nameMetadata); - } - - public override int Compare(NameInfo other) - { - return this.Compare(other.name, other.tags); - } - - public override int Compare(KeyValuePair<string, EventTags> key) - { - return this.Compare(key.Key, key.Value & Statics.EventTagsMask); - } - - private int Compare(string otherName, EventTags otherTags) - { - int result = StringComparer.Ordinal.Compare(this.name, otherName); - if (result == 0 && this.tags != otherTags) - { - result = this.tags < otherTags ? -1 : 1; - } - return result; - } - -#if FEATURE_PERFTRACING - public IntPtr GetOrCreateEventHandle(EventProvider provider, TraceLoggingEventHandleTable eventHandleTable, EventDescriptor descriptor, TraceLoggingEventTypes eventTypes) - { - IntPtr eventHandle; - if ((eventHandle = eventHandleTable[descriptor.EventId]) == IntPtr.Zero) - { - lock (eventHandleTable) - { - if ((eventHandle = eventHandleTable[descriptor.EventId]) == IntPtr.Zero) - { - byte[]? metadataBlob = EventPipeMetadataGenerator.Instance.GenerateEventMetadata( - descriptor.EventId, - name, - (EventKeywords)descriptor.Keywords, - (EventLevel)descriptor.Level, - descriptor.Version, - eventTypes); - uint metadataLength = (metadataBlob != null) ? (uint)metadataBlob.Length : 0; - - unsafe - { - fixed (byte* pMetadataBlob = metadataBlob) - { - // Define the event. - eventHandle = provider.m_eventProvider.DefineEventHandle( - (uint)descriptor.EventId, - name, - descriptor.Keywords, - descriptor.Version, - descriptor.Level, - pMetadataBlob, - metadataLength); - } - } - - // Cache the event handle. - eventHandleTable.SetEventHandle(descriptor.EventId, eventHandle); - } - } - } - - return eventHandle; - } -#endif - } -} diff --git a/netcore/System.Private.CoreLib/shared/System/Diagnostics/Tracing/TraceLogging/PropertyAnalysis.cs b/netcore/System.Private.CoreLib/shared/System/Diagnostics/Tracing/TraceLogging/PropertyAnalysis.cs deleted file mode 100644 index e7c2c08b275..00000000000 --- a/netcore/System.Private.CoreLib/shared/System/Diagnostics/Tracing/TraceLogging/PropertyAnalysis.cs +++ /dev/null @@ -1,41 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -#if ES_BUILD_STANDALONE -using System; -#endif -using System.Reflection; - -#if ES_BUILD_STANDALONE -namespace Microsoft.Diagnostics.Tracing -#else -namespace System.Diagnostics.Tracing -#endif -{ - /// <summary> - /// TraceLogging: stores the per-property information obtained by - /// reflecting over a type. - /// </summary> - internal sealed class PropertyAnalysis - { - internal readonly string name; - internal readonly PropertyInfo propertyInfo; - internal readonly Func<PropertyValue, PropertyValue> getter; - internal readonly TraceLoggingTypeInfo typeInfo; - internal readonly EventFieldAttribute? fieldAttribute; - - public PropertyAnalysis( - string name, - PropertyInfo propertyInfo, - TraceLoggingTypeInfo typeInfo, - EventFieldAttribute? fieldAttribute) - { - this.name = name; - this.propertyInfo = propertyInfo; - this.getter = PropertyValue.GetPropertyGetter(propertyInfo); - this.typeInfo = typeInfo; - this.fieldAttribute = fieldAttribute; - } - } -} diff --git a/netcore/System.Private.CoreLib/shared/System/Diagnostics/Tracing/TraceLogging/PropertyValue.cs b/netcore/System.Private.CoreLib/shared/System/Diagnostics/Tracing/TraceLogging/PropertyValue.cs deleted file mode 100644 index 645aab27244..00000000000 --- a/netcore/System.Private.CoreLib/shared/System/Diagnostics/Tracing/TraceLogging/PropertyValue.cs +++ /dev/null @@ -1,272 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -#if ES_BUILD_STANDALONE -using System; -using System.Diagnostics; -#endif -using System.Reflection; -using System.Runtime.InteropServices; - -#if ES_BUILD_STANDALONE -namespace Microsoft.Diagnostics.Tracing -#else -namespace System.Diagnostics.Tracing -#endif -{ - /// <summary> - /// Holds property values of any type. For common value types, we have inline storage so that we don't need - /// to box the values. For all other types, we store the value in a single object reference field. - /// - /// To get the value of a property quickly, use a delegate produced by <see cref="PropertyValue.GetPropertyGetter(PropertyInfo)"/>. - /// </summary> -#if ES_BUILD_PN - [CLSCompliant(false)] - public -#else - internal -#endif - readonly unsafe struct PropertyValue - { - /// <summary> - /// Union of well-known value types, to avoid boxing those types. - /// </summary> - [StructLayout(LayoutKind.Explicit)] - public struct Scalar - { - [FieldOffset(0)] - public bool AsBoolean; - [FieldOffset(0)] - public byte AsByte; - [FieldOffset(0)] - public sbyte AsSByte; - [FieldOffset(0)] - public char AsChar; - [FieldOffset(0)] - public short AsInt16; - [FieldOffset(0)] - public ushort AsUInt16; - [FieldOffset(0)] - public int AsInt32; - [FieldOffset(0)] - public uint AsUInt32; - [FieldOffset(0)] - public long AsInt64; - [FieldOffset(0)] - public ulong AsUInt64; - [FieldOffset(0)] - public IntPtr AsIntPtr; - [FieldOffset(0)] - public UIntPtr AsUIntPtr; - [FieldOffset(0)] - public float AsSingle; - [FieldOffset(0)] - public double AsDouble; - [FieldOffset(0)] - public Guid AsGuid; - [FieldOffset(0)] - public DateTime AsDateTime; - [FieldOffset(0)] - public DateTimeOffset AsDateTimeOffset; - [FieldOffset(0)] - public TimeSpan AsTimeSpan; - [FieldOffset(0)] - public decimal AsDecimal; - } - - // Anything not covered by the Scalar union gets stored in this reference. - private readonly object? _reference; - private readonly Scalar _scalar; - private readonly int _scalarLength; - - private PropertyValue(object? value) - { - _reference = value; - _scalar = default; - _scalarLength = 0; - } - - private PropertyValue(Scalar scalar, int scalarLength) - { - _reference = null; - _scalar = scalar; - _scalarLength = scalarLength; - } - - private PropertyValue(bool value) : this(new Scalar() { AsBoolean = value }, sizeof(bool)) { } - private PropertyValue(byte value) : this(new Scalar() { AsByte = value }, sizeof(byte)) { } - private PropertyValue(sbyte value) : this(new Scalar() { AsSByte = value }, sizeof(sbyte)) { } - private PropertyValue(char value) : this(new Scalar() { AsChar = value }, sizeof(char)) { } - private PropertyValue(short value) : this(new Scalar() { AsInt16 = value }, sizeof(short)) { } - private PropertyValue(ushort value) : this(new Scalar() { AsUInt16 = value }, sizeof(ushort)) { } - private PropertyValue(int value) : this(new Scalar() { AsInt32 = value }, sizeof(int)) { } - private PropertyValue(uint value) : this(new Scalar() { AsUInt32 = value }, sizeof(uint)) { } - private PropertyValue(long value) : this(new Scalar() { AsInt64 = value }, sizeof(long)) { } - private PropertyValue(ulong value) : this(new Scalar() { AsUInt64 = value }, sizeof(ulong)) { } - private PropertyValue(IntPtr value) : this(new Scalar() { AsIntPtr = value }, sizeof(IntPtr)) { } - private PropertyValue(UIntPtr value) : this(new Scalar() { AsUIntPtr = value }, sizeof(UIntPtr)) { } - private PropertyValue(float value) : this(new Scalar() { AsSingle = value }, sizeof(float)) { } - private PropertyValue(double value) : this(new Scalar() { AsDouble = value }, sizeof(double)) { } - private PropertyValue(Guid value) : this(new Scalar() { AsGuid = value }, sizeof(Guid)) { } - private PropertyValue(DateTime value) : this(new Scalar() { AsDateTime = value }, sizeof(DateTime)) { } - private PropertyValue(DateTimeOffset value) : this(new Scalar() { AsDateTimeOffset = value }, sizeof(DateTimeOffset)) { } - private PropertyValue(TimeSpan value) : this(new Scalar() { AsTimeSpan = value }, sizeof(TimeSpan)) { } - private PropertyValue(decimal value) : this(new Scalar() { AsDecimal = value }, sizeof(decimal)) { } - - public static Func<object?, PropertyValue> GetFactory(Type type) - { - if (type == typeof(bool)) return value => new PropertyValue((bool)value!); - if (type == typeof(byte)) return value => new PropertyValue((byte)value!); - if (type == typeof(sbyte)) return value => new PropertyValue((sbyte)value!); - if (type == typeof(char)) return value => new PropertyValue((char)value!); - if (type == typeof(short)) return value => new PropertyValue((short)value!); - if (type == typeof(ushort)) return value => new PropertyValue((ushort)value!); - if (type == typeof(int)) return value => new PropertyValue((int)value!); - if (type == typeof(uint)) return value => new PropertyValue((uint)value!); - if (type == typeof(long)) return value => new PropertyValue((long)value!); - if (type == typeof(ulong)) return value => new PropertyValue((ulong)value!); - if (type == typeof(IntPtr)) return value => new PropertyValue((IntPtr)value!); - if (type == typeof(UIntPtr)) return value => new PropertyValue((UIntPtr)value!); - if (type == typeof(float)) return value => new PropertyValue((float)value!); - if (type == typeof(double)) return value => new PropertyValue((double)value!); - if (type == typeof(Guid)) return value => new PropertyValue((Guid)value!); - if (type == typeof(DateTime)) return value => new PropertyValue((DateTime)value!); - if (type == typeof(DateTimeOffset)) return value => new PropertyValue((DateTimeOffset)value!); - if (type == typeof(TimeSpan)) return value => new PropertyValue((TimeSpan)value!); - if (type == typeof(decimal)) return value => new PropertyValue((decimal)value!); - - return value => new PropertyValue(value); - } - - public object? ReferenceValue - { - get - { - Debug.Assert(_scalarLength == 0, "This ReflectedValue refers to an unboxed value type, not a reference type or boxed value type."); - return _reference; - } - } - - public Scalar ScalarValue - { - get - { - Debug.Assert(_scalarLength > 0, "This ReflectedValue refers to a reference type or boxed value type, not an unboxed value type"); - return _scalar; - } - } - - public int ScalarLength - { - get - { - Debug.Assert(_scalarLength > 0, "This ReflectedValue refers to a reference type or boxed value type, not an unboxed value type"); - return _scalarLength; - } - } - - /// <summary> - /// Gets a delegate that gets the value of a given property. - /// </summary> - public static Func<PropertyValue, PropertyValue> GetPropertyGetter(PropertyInfo property) - { - if (property.DeclaringType!.GetTypeInfo().IsValueType) - return GetBoxedValueTypePropertyGetter(property); - else - return GetReferenceTypePropertyGetter(property); - } - - /// <summary> - /// Gets a delegate that gets the value of a property of a value type. We unfortunately cannot avoid boxing the value type, - /// without making this generic over the value type. That would result in a large number of generic instantiations, and furthermore - /// does not work correctly on .NET Native (we cannot express the needed instantiations in an rd.xml file). We expect that user-defined - /// value types will be rare, and in any case the boxing only happens for events that are actually enabled. - /// </summary> - private static Func<PropertyValue, PropertyValue> GetBoxedValueTypePropertyGetter(PropertyInfo property) - { - Type type = property.PropertyType; - - if (type.GetTypeInfo().IsEnum) - type = Enum.GetUnderlyingType(type); - - Func<object?, PropertyValue> factory = GetFactory(type); - - return container => factory(property.GetValue(container.ReferenceValue)); - } - - /// <summary> - /// For properties of reference types, we use a generic helper class to get the value. This enables us to use MethodInfo.CreateDelegate - /// to build a fast getter. We can get away with this on .NET Native, because we really only need one runtime instantiation of the - /// generic type, since it's only instantiated over reference types (and thus all instances are shared). - /// </summary> - /// <param name="property"></param> - /// <returns></returns> - private static Func<PropertyValue, PropertyValue> GetReferenceTypePropertyGetter(PropertyInfo property) - { - var helper = (TypeHelper)Activator.CreateInstance(typeof(ReferenceTypeHelper<>).MakeGenericType(property.DeclaringType!))!; - return helper.GetPropertyGetter(property); - } - -#if ES_BUILD_PN - public -#else - private -#endif - abstract class TypeHelper - { - public abstract Func<PropertyValue, PropertyValue> GetPropertyGetter(PropertyInfo property); - - protected Delegate GetGetMethod(PropertyInfo property, Type propertyType) - { - return property.GetMethod!.CreateDelegate(typeof(Func<,>).MakeGenericType(property.DeclaringType!, propertyType)); - } - } - -#if ES_BUILD_PN - public -#else - private -#endif - sealed class ReferenceTypeHelper<TContainer> : TypeHelper where TContainer : class? - { - public override Func<PropertyValue, PropertyValue> GetPropertyGetter(PropertyInfo property) - { - Type type = property.PropertyType; - - if (!Statics.IsValueType(type)) - { - var getter = (Func<TContainer, object?>)GetGetMethod(property, type); - return container => new PropertyValue(getter((TContainer)container.ReferenceValue!)); - } - else - { - if (type.GetTypeInfo().IsEnum) - type = Enum.GetUnderlyingType(type); - - if (type == typeof(bool)) { var f = (Func<TContainer, bool>)GetGetMethod(property, type); return container => new PropertyValue(f((TContainer)container.ReferenceValue!)); } - if (type == typeof(byte)) { var f = (Func<TContainer, byte>)GetGetMethod(property, type); return container => new PropertyValue(f((TContainer)container.ReferenceValue!)); } - if (type == typeof(sbyte)) { var f = (Func<TContainer, sbyte>)GetGetMethod(property, type); return container => new PropertyValue(f((TContainer)container.ReferenceValue!)); } - if (type == typeof(char)) { var f = (Func<TContainer, char>)GetGetMethod(property, type); return container => new PropertyValue(f((TContainer)container.ReferenceValue!)); } - if (type == typeof(short)) { var f = (Func<TContainer, short>)GetGetMethod(property, type); return container => new PropertyValue(f((TContainer)container.ReferenceValue!)); } - if (type == typeof(ushort)) { var f = (Func<TContainer, ushort>)GetGetMethod(property, type); return container => new PropertyValue(f((TContainer)container.ReferenceValue!)); } - if (type == typeof(int)) { var f = (Func<TContainer, int>)GetGetMethod(property, type); return container => new PropertyValue(f((TContainer)container.ReferenceValue!)); } - if (type == typeof(uint)) { var f = (Func<TContainer, uint>)GetGetMethod(property, type); return container => new PropertyValue(f((TContainer)container.ReferenceValue!)); } - if (type == typeof(long)) { var f = (Func<TContainer, long>)GetGetMethod(property, type); return container => new PropertyValue(f((TContainer)container.ReferenceValue!)); } - if (type == typeof(ulong)) { var f = (Func<TContainer, ulong>)GetGetMethod(property, type); return container => new PropertyValue(f((TContainer)container.ReferenceValue!)); } - if (type == typeof(IntPtr)) { var f = (Func<TContainer, IntPtr>)GetGetMethod(property, type); return container => new PropertyValue(f((TContainer)container.ReferenceValue!)); } - if (type == typeof(UIntPtr)) { var f = (Func<TContainer, UIntPtr>)GetGetMethod(property, type); return container => new PropertyValue(f((TContainer)container.ReferenceValue!)); } - if (type == typeof(float)) { var f = (Func<TContainer, float>)GetGetMethod(property, type); return container => new PropertyValue(f((TContainer)container.ReferenceValue!)); } - if (type == typeof(double)) { var f = (Func<TContainer, double>)GetGetMethod(property, type); return container => new PropertyValue(f((TContainer)container.ReferenceValue!)); } - if (type == typeof(Guid)) { var f = (Func<TContainer, Guid>)GetGetMethod(property, type); return container => new PropertyValue(f((TContainer)container.ReferenceValue!)); } - if (type == typeof(DateTime)) { var f = (Func<TContainer, DateTime>)GetGetMethod(property, type); return container => new PropertyValue(f((TContainer)container.ReferenceValue!)); } - if (type == typeof(DateTimeOffset)) { var f = (Func<TContainer, DateTimeOffset>)GetGetMethod(property, type); return container => new PropertyValue(f((TContainer)container.ReferenceValue!)); } - if (type == typeof(TimeSpan)) { var f = (Func<TContainer, TimeSpan>)GetGetMethod(property, type); return container => new PropertyValue(f((TContainer)container.ReferenceValue!)); } - if (type == typeof(decimal)) { var f = (Func<TContainer, decimal>)GetGetMethod(property, type); return container => new PropertyValue(f((TContainer)container.ReferenceValue!)); } - - return container => new PropertyValue(property.GetValue(container.ReferenceValue)); - } - } - } - } -} diff --git a/netcore/System.Private.CoreLib/shared/System/Diagnostics/Tracing/TraceLogging/SimpleEventTypes.cs b/netcore/System.Private.CoreLib/shared/System/Diagnostics/Tracing/TraceLogging/SimpleEventTypes.cs deleted file mode 100644 index c85ec92febf..00000000000 --- a/netcore/System.Private.CoreLib/shared/System/Diagnostics/Tracing/TraceLogging/SimpleEventTypes.cs +++ /dev/null @@ -1,40 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -#if ES_BUILD_STANDALONE -using System; -using System.Diagnostics; -#endif -using System.Threading; - -#if ES_BUILD_STANDALONE -namespace Microsoft.Diagnostics.Tracing -#else -namespace System.Diagnostics.Tracing -#endif -{ - /// <summary> - /// TraceLogging: Contains the metadata needed to emit an event, optimized - /// for events with one top-level compile-time-typed payload object. - /// </summary> - /// <typeparam name="T"> - /// Type of the top-level payload object. Should be EmptyStruct if the - /// event has no payload. - /// </typeparam> - internal static class SimpleEventTypes<T> - { - private static TraceLoggingEventTypes? instance; - - public static TraceLoggingEventTypes Instance => instance ?? InitInstance(); - - private static TraceLoggingEventTypes InitInstance() - { - var info = TraceLoggingTypeInfo.GetInstance(typeof(T), null); - var newInstance = new TraceLoggingEventTypes(info.Name, info.Tags, new TraceLoggingTypeInfo[] { info }); - Interlocked.CompareExchange(ref instance, newInstance, null); - Debug.Assert(instance != null); - return instance; - } - } -} diff --git a/netcore/System.Private.CoreLib/shared/System/Diagnostics/Tracing/TraceLogging/SimpleTypeInfos.cs b/netcore/System.Private.CoreLib/shared/System/Diagnostics/Tracing/TraceLogging/SimpleTypeInfos.cs deleted file mode 100644 index 0525413ccd9..00000000000 --- a/netcore/System.Private.CoreLib/shared/System/Diagnostics/Tracing/TraceLogging/SimpleTypeInfos.cs +++ /dev/null @@ -1,299 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -#if ES_BUILD_STANDALONE -using System; -using System.Diagnostics; -#endif -using System.Collections.Generic; -using System.Reflection; - -#if ES_BUILD_STANDALONE -namespace Microsoft.Diagnostics.Tracing -#else -namespace System.Diagnostics.Tracing -#endif -{ - /// <summary> - /// TraceLogging: Type handler for empty or unsupported types. - /// </summary> - internal sealed class NullTypeInfo : TraceLoggingTypeInfo - { - public NullTypeInfo() : base(typeof(EmptyStruct)) { } - - public override void WriteMetadata( - TraceLoggingMetadataCollector collector, - string? name, - EventFieldFormat format) - { - collector.AddGroup(name); - } - - public override void WriteData(TraceLoggingDataCollector collector, PropertyValue value) - { - return; - } - - public override object? GetData(object? value) - { - return null; - } - } - - /// <summary> - /// Type handler for simple scalar types. - /// </summary> - internal sealed class ScalarTypeInfo : TraceLoggingTypeInfo - { - private readonly Func<EventFieldFormat, TraceLoggingDataType, TraceLoggingDataType> formatFunc; - private readonly TraceLoggingDataType nativeFormat; - - private ScalarTypeInfo( - Type type, - Func<EventFieldFormat, TraceLoggingDataType, TraceLoggingDataType> formatFunc, - TraceLoggingDataType nativeFormat) - : base(type) - { - this.formatFunc = formatFunc; - this.nativeFormat = nativeFormat; - } - - public override void WriteMetadata(TraceLoggingMetadataCollector collector, string? name, EventFieldFormat format) - { - collector.AddScalar(name!, formatFunc(format, nativeFormat)); - } - - public override void WriteData(TraceLoggingDataCollector collector, PropertyValue value) - { - collector.AddScalar(value); - } - - public static TraceLoggingTypeInfo Boolean() { return new ScalarTypeInfo(typeof(bool), Statics.Format8, TraceLoggingDataType.Boolean8); } - public static TraceLoggingTypeInfo Byte() { return new ScalarTypeInfo(typeof(byte), Statics.Format8, TraceLoggingDataType.UInt8); } - public static TraceLoggingTypeInfo SByte() { return new ScalarTypeInfo(typeof(sbyte), Statics.Format8, TraceLoggingDataType.Int8); } - public static TraceLoggingTypeInfo Char() { return new ScalarTypeInfo(typeof(char), Statics.Format16, TraceLoggingDataType.Char16); } - public static TraceLoggingTypeInfo Int16() { return new ScalarTypeInfo(typeof(short), Statics.Format16, TraceLoggingDataType.Int16); } - public static TraceLoggingTypeInfo UInt16() { return new ScalarTypeInfo(typeof(ushort), Statics.Format16, TraceLoggingDataType.UInt16); } - public static TraceLoggingTypeInfo Int32() { return new ScalarTypeInfo(typeof(int), Statics.Format32, TraceLoggingDataType.Int32); } - public static TraceLoggingTypeInfo UInt32() { return new ScalarTypeInfo(typeof(uint), Statics.Format32, TraceLoggingDataType.UInt32); } - public static TraceLoggingTypeInfo Int64() { return new ScalarTypeInfo(typeof(long), Statics.Format64, TraceLoggingDataType.Int64); } - public static TraceLoggingTypeInfo UInt64() { return new ScalarTypeInfo(typeof(ulong), Statics.Format64, TraceLoggingDataType.UInt64); } - public static TraceLoggingTypeInfo IntPtr() { return new ScalarTypeInfo(typeof(IntPtr), Statics.FormatPtr, Statics.IntPtrType); } - public static TraceLoggingTypeInfo UIntPtr() { return new ScalarTypeInfo(typeof(UIntPtr), Statics.FormatPtr, Statics.UIntPtrType); } - public static TraceLoggingTypeInfo Single() { return new ScalarTypeInfo(typeof(float), Statics.Format32, TraceLoggingDataType.Float); } - public static TraceLoggingTypeInfo Double() { return new ScalarTypeInfo(typeof(double), Statics.Format64, TraceLoggingDataType.Double); } - public static TraceLoggingTypeInfo Guid() { return new ScalarTypeInfo(typeof(Guid), (f, t) => Statics.MakeDataType(TraceLoggingDataType.Guid, f), TraceLoggingDataType.Guid); } - } - - - /// <summary> - /// Type handler for arrays of scalars - /// </summary> - internal sealed class ScalarArrayTypeInfo : TraceLoggingTypeInfo - { - private readonly Func<EventFieldFormat, TraceLoggingDataType, TraceLoggingDataType> formatFunc; - private readonly TraceLoggingDataType nativeFormat; - private readonly int elementSize; - - private ScalarArrayTypeInfo( - Type type, - Func<EventFieldFormat, TraceLoggingDataType, TraceLoggingDataType> formatFunc, - TraceLoggingDataType nativeFormat, - int elementSize) - : base(type) - { - this.formatFunc = formatFunc; - this.nativeFormat = nativeFormat; - this.elementSize = elementSize; - } - - public override void WriteMetadata(TraceLoggingMetadataCollector collector, string? name, EventFieldFormat format) - { - collector.AddArray(name!, formatFunc(format, nativeFormat)); - } - - public override void WriteData(TraceLoggingDataCollector collector, PropertyValue value) - { - collector.AddArray(value, elementSize); - } - - public static TraceLoggingTypeInfo Boolean() { return new ScalarArrayTypeInfo(typeof(bool[]), Statics.Format8, TraceLoggingDataType.Boolean8, sizeof(bool)); } - public static TraceLoggingTypeInfo Byte() { return new ScalarArrayTypeInfo(typeof(byte[]), Statics.Format8, TraceLoggingDataType.UInt8, sizeof(byte)); } - public static TraceLoggingTypeInfo SByte() { return new ScalarArrayTypeInfo(typeof(sbyte[]), Statics.Format8, TraceLoggingDataType.Int8, sizeof(sbyte)); } - public static TraceLoggingTypeInfo Char() { return new ScalarArrayTypeInfo(typeof(char[]), Statics.Format16, TraceLoggingDataType.Char16, sizeof(char)); } - public static TraceLoggingTypeInfo Int16() { return new ScalarArrayTypeInfo(typeof(short[]), Statics.Format16, TraceLoggingDataType.Int16, sizeof(short)); } - public static TraceLoggingTypeInfo UInt16() { return new ScalarArrayTypeInfo(typeof(ushort[]), Statics.Format16, TraceLoggingDataType.UInt16, sizeof(ushort)); } - public static TraceLoggingTypeInfo Int32() { return new ScalarArrayTypeInfo(typeof(int[]), Statics.Format32, TraceLoggingDataType.Int32, sizeof(int)); } - public static TraceLoggingTypeInfo UInt32() { return new ScalarArrayTypeInfo(typeof(uint[]), Statics.Format32, TraceLoggingDataType.UInt32, sizeof(uint)); } - public static TraceLoggingTypeInfo Int64() { return new ScalarArrayTypeInfo(typeof(long[]), Statics.Format64, TraceLoggingDataType.Int64, sizeof(long)); } - public static TraceLoggingTypeInfo UInt64() { return new ScalarArrayTypeInfo(typeof(ulong[]), Statics.Format64, TraceLoggingDataType.UInt64, sizeof(ulong)); } - public static TraceLoggingTypeInfo IntPtr() { return new ScalarArrayTypeInfo(typeof(IntPtr[]), Statics.FormatPtr, Statics.IntPtrType, System.IntPtr.Size); } - public static TraceLoggingTypeInfo UIntPtr() { return new ScalarArrayTypeInfo(typeof(UIntPtr[]), Statics.FormatPtr, Statics.UIntPtrType, System.IntPtr.Size); } - public static TraceLoggingTypeInfo Single() { return new ScalarArrayTypeInfo(typeof(float[]), Statics.Format32, TraceLoggingDataType.Float, sizeof(float)); } - public static TraceLoggingTypeInfo Double() { return new ScalarArrayTypeInfo(typeof(double[]), Statics.Format64, TraceLoggingDataType.Double, sizeof(double)); } - public static unsafe TraceLoggingTypeInfo Guid() { return new ScalarArrayTypeInfo(typeof(Guid), (f, t) => Statics.MakeDataType(TraceLoggingDataType.Guid, f), TraceLoggingDataType.Guid, sizeof(Guid)); } - } - - /// <summary> - /// TraceLogging: Type handler for String. - /// </summary> - internal sealed class StringTypeInfo : TraceLoggingTypeInfo - { - public StringTypeInfo() : base(typeof(string)) { } - - public override void WriteMetadata( - TraceLoggingMetadataCollector collector, - string? name, - EventFieldFormat format) - { - collector.AddNullTerminatedString(name!, Statics.MakeDataType(TraceLoggingDataType.Utf16String, format)); - } - - public override void WriteData(TraceLoggingDataCollector collector, PropertyValue value) - { - collector.AddNullTerminatedString((string?)value.ReferenceValue); - } - - public override object GetData(object? value) - { - if (value == null) - { - return ""; - } - - return value; - } - } - - /// <summary> - /// TraceLogging: Type handler for DateTime. - /// </summary> - internal sealed class DateTimeTypeInfo : TraceLoggingTypeInfo - { - public DateTimeTypeInfo() : base(typeof(DateTime)) { } - - public override void WriteMetadata( - TraceLoggingMetadataCollector collector, - string? name, - EventFieldFormat format) - { - collector.AddScalar(name!, Statics.MakeDataType(TraceLoggingDataType.FileTime, format)); - } - - public override void WriteData(TraceLoggingDataCollector collector, PropertyValue value) - { - DateTime dateTime = value.ScalarValue.AsDateTime; - const long UTCMinTicks = 504911232000000000; - long dateTimeTicks = 0; - // We cannot translate dates sooner than 1/1/1601 in UTC. - // To avoid getting an ArgumentOutOfRangeException we compare with 1/1/1601 DateTime ticks - if (dateTime.Ticks > UTCMinTicks) - dateTimeTicks = dateTime.ToFileTimeUtc(); - collector.AddScalar(dateTimeTicks); - } - } - - /// <summary> - /// TraceLogging: Type handler for DateTimeOffset. - /// </summary> - internal sealed class DateTimeOffsetTypeInfo : TraceLoggingTypeInfo - { - public DateTimeOffsetTypeInfo() : base(typeof(DateTimeOffset)) { } - - public override void WriteMetadata(TraceLoggingMetadataCollector collector, string? name, EventFieldFormat format) - { - TraceLoggingMetadataCollector group = collector.AddGroup(name); - group.AddScalar("Ticks", Statics.MakeDataType(TraceLoggingDataType.FileTime, format)); - group.AddScalar("Offset", TraceLoggingDataType.Int64); - } - - public override void WriteData(TraceLoggingDataCollector collector, PropertyValue value) - { - DateTimeOffset dateTimeOffset = value.ScalarValue.AsDateTimeOffset; - long ticks = dateTimeOffset.Ticks; - collector.AddScalar(ticks < 504911232000000000 ? 0 : ticks - 504911232000000000); - collector.AddScalar(dateTimeOffset.Offset.Ticks); - } - } - - /// <summary> - /// TraceLogging: Type handler for TimeSpan. - /// </summary> - internal sealed class TimeSpanTypeInfo : TraceLoggingTypeInfo - { - public TimeSpanTypeInfo() : base(typeof(TimeSpan)) { } - - public override void WriteMetadata( - TraceLoggingMetadataCollector collector, - string? name, - EventFieldFormat format) - { - collector.AddScalar(name!, Statics.MakeDataType(TraceLoggingDataType.Int64, format)); - } - - public override void WriteData(TraceLoggingDataCollector collector, PropertyValue value) - { - collector.AddScalar(value.ScalarValue.AsTimeSpan.Ticks); - } - } - - /// <summary> - /// TraceLogging: Type handler for decimal. (Note: not full-fidelity, exposed as Double.) - /// </summary> - internal sealed class DecimalTypeInfo : TraceLoggingTypeInfo - { - public DecimalTypeInfo() : base(typeof(decimal)) { } - - public override void WriteMetadata( - TraceLoggingMetadataCollector collector, - string? name, - EventFieldFormat format) - { - collector.AddScalar(name!, Statics.MakeDataType(TraceLoggingDataType.Double, format)); - } - - public override void WriteData(TraceLoggingDataCollector collector, PropertyValue value) - { - collector.AddScalar((double)value.ScalarValue.AsDecimal); - } - } - - /// <summary> - /// TraceLogging: Type handler for Nullable. - /// </summary> - internal sealed class NullableTypeInfo : TraceLoggingTypeInfo - { - private readonly TraceLoggingTypeInfo valueInfo; - private readonly Func<PropertyValue, PropertyValue> valueGetter; - - public NullableTypeInfo(Type type, List<Type> recursionCheck) - : base(type) - { - Type[] typeArgs = type.GenericTypeArguments; - Debug.Assert(typeArgs.Length == 1); - this.valueInfo = TraceLoggingTypeInfo.GetInstance(typeArgs[0], recursionCheck); - this.valueGetter = PropertyValue.GetPropertyGetter(type.GetTypeInfo().GetDeclaredProperty("Value")!); - } - - public override void WriteMetadata( - TraceLoggingMetadataCollector collector, - string? name, - EventFieldFormat format) - { - TraceLoggingMetadataCollector group = collector.AddGroup(name); - group.AddScalar("HasValue", TraceLoggingDataType.Boolean8); - this.valueInfo.WriteMetadata(group, "Value", format); - } - - public override void WriteData(TraceLoggingDataCollector collector, PropertyValue value) - { - // It's not currently possible to get the HasValue property of a nullable type through reflection when the - // value is null. Instead, we simply check that the nullable is not null. - bool hasValue = value.ReferenceValue != null; - collector.AddScalar(hasValue); - PropertyValue val = hasValue ? valueGetter(value) : valueInfo.PropertyValueFactory(Activator.CreateInstance(valueInfo.DataType)); - this.valueInfo.WriteData(collector, val); - } - } -} diff --git a/netcore/System.Private.CoreLib/shared/System/Diagnostics/Tracing/TraceLogging/Statics.cs b/netcore/System.Private.CoreLib/shared/System/Diagnostics/Tracing/TraceLogging/Statics.cs deleted file mode 100644 index 6d4dd919981..00000000000 --- a/netcore/System.Private.CoreLib/shared/System/Diagnostics/Tracing/TraceLogging/Statics.cs +++ /dev/null @@ -1,693 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -#if ES_BUILD_STANDALONE -using System; -using Environment = Microsoft.Diagnostics.Tracing.Internal.Environment; -#endif -using System.Collections.Generic; -using System.Reflection; -using System.Runtime.CompilerServices; -using System.Text; -using Microsoft.Reflection; - -#if ES_BUILD_STANDALONE -namespace Microsoft.Diagnostics.Tracing -#else -namespace System.Diagnostics.Tracing -#endif -{ - /// <summary> - /// TraceLogging: Constants and utility functions. - /// </summary> - internal static class Statics - { - #region Constants - - public const byte DefaultLevel = 5; - public const byte TraceLoggingChannel = 0xb; - public const byte InTypeMask = 31; - public const byte InTypeFixedCountFlag = 32; - public const byte InTypeVariableCountFlag = 64; - public const byte InTypeCustomCountFlag = 96; - public const byte InTypeCountMask = 96; - public const byte InTypeChainFlag = 128; - public const byte OutTypeMask = 127; - public const byte OutTypeChainFlag = 128; - public const EventTags EventTagsMask = (EventTags)0xfffffff; - - public static readonly TraceLoggingDataType IntPtrType = IntPtr.Size == 8 - ? TraceLoggingDataType.Int64 - : TraceLoggingDataType.Int32; - public static readonly TraceLoggingDataType UIntPtrType = IntPtr.Size == 8 - ? TraceLoggingDataType.UInt64 - : TraceLoggingDataType.UInt32; - public static readonly TraceLoggingDataType HexIntPtrType = IntPtr.Size == 8 - ? TraceLoggingDataType.HexInt64 - : TraceLoggingDataType.HexInt32; - - #endregion - - #region Metadata helpers - - /// <summary> - /// A complete metadata chunk can be expressed as: - /// length16 + prefix + null-terminated-utf8-name + suffix + additionalData. - /// We assume that excludedData will be provided by some other means, - /// but that its size is known. This function returns a blob containing - /// length16 + prefix + name + suffix, with prefix and suffix initialized - /// to 0's. The length16 value is initialized to the length of the returned - /// blob plus additionalSize, so that the concatenation of the returned blob - /// plus a blob of size additionalSize constitutes a valid metadata blob. - /// </summary> - /// <param name="name"> - /// The name to include in the blob. - /// </param> - /// <param name="prefixSize"> - /// Amount of space to reserve before name. For provider or field blobs, this - /// should be 0. For event blobs, this is used for the tags field and will vary - /// from 1 to 4, depending on how large the tags field needs to be. - /// </param> - /// <param name="suffixSize"> - /// Amount of space to reserve after name. For example, a provider blob with no - /// traits would reserve 0 extra bytes, but a provider blob with a single GroupId - /// field would reserve 19 extra bytes. - /// </param> - /// <param name="additionalSize"> - /// Amount of additional data in another blob. This value will be counted in the - /// blob's length field, but will not be included in the returned byte[] object. - /// The complete blob would then be the concatenation of the returned byte[] object - /// with another byte[] object of length additionalSize. - /// </param> - /// <returns> - /// A byte[] object with the length and name fields set, with room reserved for - /// prefix and suffix. If additionalSize was 0, the byte[] object is a complete - /// blob. Otherwise, another byte[] of size additionalSize must be concatenated - /// with this one to form a complete blob. - /// </returns> - public static byte[] MetadataForString( - string name, - int prefixSize, - int suffixSize, - int additionalSize) - { - Statics.CheckName(name); - int metadataSize = Encoding.UTF8.GetByteCount(name) + 3 + prefixSize + suffixSize; - var metadata = new byte[metadataSize]; - ushort totalSize = checked((ushort)(metadataSize + additionalSize)); - metadata[0] = unchecked((byte)totalSize); - metadata[1] = unchecked((byte)(totalSize >> 8)); - Encoding.UTF8.GetBytes(name, 0, name.Length, metadata, 2 + prefixSize); - return metadata; - } - - /// <summary> - /// Serialize the low 28 bits of the tags value into the metadata stream, - /// starting at the index given by pos. Updates pos. Writes 1 to 4 bytes, - /// depending on the value of the tags variable. Usable for event tags and - /// field tags. - /// - /// Note that 'metadata' can be null, in which case it only updates 'pos'. - /// This is useful for a two pass approach where you figure out how big to - /// make the array, and then you fill it in. - /// </summary> - public static void EncodeTags(int tags, ref int pos, byte[]? metadata) - { - // We transmit the low 28 bits of tags, high bits first, 7 bits at a time. - int tagsLeft = tags & 0xfffffff; - bool more; - do - { - byte current = (byte)((tagsLeft >> 21) & 0x7f); - more = (tagsLeft & 0x1fffff) != 0; - current |= (byte)(more ? 0x80 : 0x00); - tagsLeft <<= 7; - - if (metadata != null) - { - metadata[pos] = current; - } - pos++; - } - while (more); - } - - public static byte Combine( - int settingValue, - byte defaultValue) - { - unchecked - { - return (byte)settingValue == settingValue - ? (byte)settingValue - : defaultValue; - } - } - - public static byte Combine( - int settingValue1, - int settingValue2, - byte defaultValue) - { - unchecked - { - return (byte)settingValue1 == settingValue1 - ? (byte)settingValue1 - : (byte)settingValue2 == settingValue2 - ? (byte)settingValue2 - : defaultValue; - } - } - - public static int Combine( - int settingValue1, - int settingValue2) - { - unchecked - { - return (byte)settingValue1 == settingValue1 - ? settingValue1 - : settingValue2; - } - } - - public static void CheckName(string? name) - { - if (name != null && 0 <= name.IndexOf('\0')) - { - throw new ArgumentOutOfRangeException(nameof(name)); - } - } - - public static bool ShouldOverrideFieldName(string fieldName) - { - return fieldName.Length <= 2 && fieldName[0] == '_'; - } - - public static TraceLoggingDataType MakeDataType( - TraceLoggingDataType baseType, - EventFieldFormat format) - { - return (TraceLoggingDataType)(((int)baseType & 0x1f) | ((int)format << 8)); - } - - /// <summary> - /// Adjusts the native type based on format. - /// - If format is default, return native. - /// - If format is recognized, return the canonical type for that format. - /// - Otherwise remove existing format from native and apply the requested format. - /// </summary> - public static TraceLoggingDataType Format8( - EventFieldFormat format, - TraceLoggingDataType native) - { - return format switch - { - EventFieldFormat.Default => native, - EventFieldFormat.String => TraceLoggingDataType.Char8, - EventFieldFormat.Boolean => TraceLoggingDataType.Boolean8, - EventFieldFormat.Hexadecimal => TraceLoggingDataType.HexInt8, -#if false - EventSourceFieldFormat.Signed => TraceLoggingDataType.Int8, - EventSourceFieldFormat.Unsigned => TraceLoggingDataType.UInt8, -#endif - _ => MakeDataType(native, format), - }; - } - - /// <summary> - /// Adjusts the native type based on format. - /// - If format is default, return native. - /// - If format is recognized, return the canonical type for that format. - /// - Otherwise remove existing format from native and apply the requested format. - /// </summary> - public static TraceLoggingDataType Format16( - EventFieldFormat format, - TraceLoggingDataType native) - { - return format switch - { - EventFieldFormat.Default => native, - EventFieldFormat.String => TraceLoggingDataType.Char16, - EventFieldFormat.Hexadecimal => TraceLoggingDataType.HexInt16, -#if false - EventSourceFieldFormat.Port => TraceLoggingDataType.Port, - EventSourceFieldFormat.Signed => TraceLoggingDataType.Int16, - EventSourceFieldFormat.Unsigned => TraceLoggingDataType.UInt16, -#endif - _ => MakeDataType(native, format), - }; - } - - /// <summary> - /// Adjusts the native type based on format. - /// - If format is default, return native. - /// - If format is recognized, return the canonical type for that format. - /// - Otherwise remove existing format from native and apply the requested format. - /// </summary> - public static TraceLoggingDataType Format32( - EventFieldFormat format, - TraceLoggingDataType native) - { - return format switch - { - EventFieldFormat.Default => native, - EventFieldFormat.Boolean => TraceLoggingDataType.Boolean32, - EventFieldFormat.Hexadecimal => TraceLoggingDataType.HexInt32, -#if false - EventSourceFieldFormat.Ipv4Address => TraceLoggingDataType.Ipv4Address, - EventSourceFieldFormat.ProcessId => TraceLoggingDataType.ProcessId, - EventSourceFieldFormat.ThreadId => TraceLoggingDataType.ThreadId, - EventSourceFieldFormat.Win32Error => TraceLoggingDataType.Win32Error, - EventSourceFieldFormat.NTStatus => TraceLoggingDataType.NTStatus, -#endif - EventFieldFormat.HResult => TraceLoggingDataType.HResult, -#if false - case EventSourceFieldFormat.Signed: - return TraceLoggingDataType.Int32; - case EventSourceFieldFormat.Unsigned: - return TraceLoggingDataType.UInt32; -#endif - _ => MakeDataType(native, format), - }; - } - - /// <summary> - /// Adjusts the native type based on format. - /// - If format is default, return native. - /// - If format is recognized, return the canonical type for that format. - /// - Otherwise remove existing format from native and apply the requested format. - /// </summary> - public static TraceLoggingDataType Format64( - EventFieldFormat format, - TraceLoggingDataType native) - { - return format switch - { - EventFieldFormat.Default => native, - EventFieldFormat.Hexadecimal => TraceLoggingDataType.HexInt64, -#if false - EventSourceFieldFormat.FileTime => TraceLoggingDataType.FileTime, - EventSourceFieldFormat.Signed => TraceLoggingDataType.Int64, - EventSourceFieldFormat.Unsigned => TraceLoggingDataType.UInt64, -#endif - _ => MakeDataType(native, format), - }; - } - - /// <summary> - /// Adjusts the native type based on format. - /// - If format is default, return native. - /// - If format is recognized, return the canonical type for that format. - /// - Otherwise remove existing format from native and apply the requested format. - /// </summary> - public static TraceLoggingDataType FormatPtr( - EventFieldFormat format, - TraceLoggingDataType native) - { - return format switch - { - EventFieldFormat.Default => native, - EventFieldFormat.Hexadecimal => HexIntPtrType, -#if false - EventSourceFieldFormat.Signed => IntPtrType, - EventSourceFieldFormat.Unsigned => UIntPtrType, -#endif - _ => MakeDataType(native, format), - }; - } - - #endregion - - #region Reflection helpers - - /* - All TraceLogging use of reflection APIs should go through wrappers here. - This helps with portability, and it also makes it easier to audit what - kinds of reflection operations are being done. - */ - - public static object? CreateInstance(Type type, params object?[]? parameters) - { - return Activator.CreateInstance(type, parameters); - } - - public static bool IsValueType(Type type) - { - bool result = type.IsValueType(); - return result; - } - - public static bool IsEnum(Type type) - { - bool result = type.IsEnum(); - return result; - } - - public static IEnumerable<PropertyInfo> GetProperties(Type type) - { - IEnumerable<PropertyInfo> result = type.GetProperties(); - return result; - } - - public static MethodInfo? GetGetMethod(PropertyInfo propInfo) - { - MethodInfo? result = propInfo.GetGetMethod(); - return result; - } - - public static MethodInfo? GetDeclaredStaticMethod(Type declaringType, string name) - { - MethodInfo? result; -#if (ES_BUILD_PCL || ES_BUILD_PN) - result = declaringType.GetTypeInfo().GetDeclaredMethod(name); -#else - result = declaringType.GetMethod( - name, - BindingFlags.DeclaredOnly | BindingFlags.Static | BindingFlags.NonPublic); -#endif - return result; - } - - public static bool HasCustomAttribute( - PropertyInfo propInfo, - Type attributeType) - { - bool result; -#if (ES_BUILD_PCL || ES_BUILD_PN) - result = propInfo.IsDefined(attributeType); -#else - object[] attributes = propInfo.GetCustomAttributes( - attributeType, - false); - result = attributes.Length != 0; -#endif - return result; - } - - public static AttributeType? GetCustomAttribute<AttributeType>(PropertyInfo propInfo) - where AttributeType : Attribute - { - AttributeType? result = null; -#if (ES_BUILD_PCL || ES_BUILD_PN) - foreach (var attrib in propInfo.GetCustomAttributes<AttributeType>(false)) - { - result = attrib; - break; - } -#else - object[] attributes = propInfo.GetCustomAttributes(typeof(AttributeType), false); - if (attributes.Length != 0) - { - result = (AttributeType)attributes[0]; - } -#endif - return result; - } - - public static AttributeType? GetCustomAttribute<AttributeType>(Type type) - where AttributeType : Attribute - { - AttributeType? result = null; -#if (ES_BUILD_PCL || ES_BUILD_PN) - foreach (var attrib in type.GetTypeInfo().GetCustomAttributes<AttributeType>(false)) - { - result = attrib; - break; - } -#else - object[] attributes = type.GetCustomAttributes(typeof(AttributeType), false); - if (attributes.Length != 0) - { - result = (AttributeType)attributes[0]; - } -#endif - return result; - } - - public static Type[] GetGenericArguments(Type type) - { - return type.GetGenericArguments(); - } - - public static Type? FindEnumerableElementType(Type type) - { - Type? elementType = null; - - if (IsGenericMatch(type, typeof(IEnumerable<>))) - { - elementType = GetGenericArguments(type)[0]; - } - else - { -#if (ES_BUILD_PCL || ES_BUILD_PN) - IEnumerable<Type> ifaceTypes = type.GetTypeInfo().ImplementedInterfaces; -#else - Type[] ifaceTypes = type.FindInterfaces(IsGenericMatch, typeof(IEnumerable<>)); -#endif - - foreach (Type ifaceType in ifaceTypes) - { -#if (ES_BUILD_PCL || ES_BUILD_PN) - if (!IsGenericMatch(ifaceType, typeof(IEnumerable<>))) - { - continue; - } -#endif - - if (elementType != null) - { - // ambiguous match. report no match at all. - elementType = null; - break; - } - - elementType = GetGenericArguments(ifaceType)[0]; - } - } - - return elementType; - } - - public static bool IsGenericMatch(Type type, object? openType) - { - return type.IsGenericType() && type.GetGenericTypeDefinition() == (Type?)openType; - } - - public static Delegate CreateDelegate(Type delegateType, MethodInfo methodInfo) - { - Delegate result; -#if (ES_BUILD_PCL || ES_BUILD_PN) - result = methodInfo.CreateDelegate( - delegateType); -#else - result = Delegate.CreateDelegate( - delegateType, - methodInfo); -#endif - return result; - } - - public static TraceLoggingTypeInfo CreateDefaultTypeInfo( - Type dataType, - List<Type> recursionCheck) - { - TraceLoggingTypeInfo result; - - if (recursionCheck.Contains(dataType)) - { - throw new NotSupportedException(SR.EventSource_RecursiveTypeDefinition); - } - - recursionCheck.Add(dataType); - - EventDataAttribute? eventAttrib = Statics.GetCustomAttribute<EventDataAttribute>(dataType); - if (eventAttrib != null || - Statics.GetCustomAttribute<CompilerGeneratedAttribute>(dataType) != null || - IsGenericMatch(dataType, typeof(KeyValuePair<,>))) - { - var analysis = new TypeAnalysis(dataType, eventAttrib, recursionCheck); - result = new InvokeTypeInfo(dataType, analysis); - } - else if (dataType.IsArray) - { - Type elementType = dataType.GetElementType()!; - if (elementType == typeof(bool)) - { - result = ScalarArrayTypeInfo.Boolean(); - } - else if (elementType == typeof(byte)) - { - result = ScalarArrayTypeInfo.Byte(); - } - else if (elementType == typeof(sbyte)) - { - result = ScalarArrayTypeInfo.SByte(); - } - else if (elementType == typeof(short)) - { - result = ScalarArrayTypeInfo.Int16(); - } - else if (elementType == typeof(ushort)) - { - result = ScalarArrayTypeInfo.UInt16(); - } - else if (elementType == typeof(int)) - { - result = ScalarArrayTypeInfo.Int32(); - } - else if (elementType == typeof(uint)) - { - result = ScalarArrayTypeInfo.UInt32(); - } - else if (elementType == typeof(long)) - { - result = ScalarArrayTypeInfo.Int64(); - } - else if (elementType == typeof(ulong)) - { - result = ScalarArrayTypeInfo.UInt64(); - } - else if (elementType == typeof(char)) - { - result = ScalarArrayTypeInfo.Char(); - } - else if (elementType == typeof(double)) - { - result = ScalarArrayTypeInfo.Double(); - } - else if (elementType == typeof(float)) - { - result = ScalarArrayTypeInfo.Single(); - } - else if (elementType == typeof(IntPtr)) - { - result = ScalarArrayTypeInfo.IntPtr(); - } - else if (elementType == typeof(UIntPtr)) - { - result = ScalarArrayTypeInfo.UIntPtr(); - } - else if (elementType == typeof(Guid)) - { - result = ScalarArrayTypeInfo.Guid(); - } - else - { - result = new ArrayTypeInfo(dataType, TraceLoggingTypeInfo.GetInstance(elementType, recursionCheck)); - } - } - else - { - if (Statics.IsEnum(dataType)) - dataType = Enum.GetUnderlyingType(dataType); - - if (dataType == typeof(string)) - { - result = new StringTypeInfo(); - } - else if (dataType == typeof(bool)) - { - result = ScalarTypeInfo.Boolean(); - } - else if (dataType == typeof(byte)) - { - result = ScalarTypeInfo.Byte(); - } - else if (dataType == typeof(sbyte)) - { - result = ScalarTypeInfo.SByte(); - } - else if (dataType == typeof(short)) - { - result = ScalarTypeInfo.Int16(); - } - else if (dataType == typeof(ushort)) - { - result = ScalarTypeInfo.UInt16(); - } - else if (dataType == typeof(int)) - { - result = ScalarTypeInfo.Int32(); - } - else if (dataType == typeof(uint)) - { - result = ScalarTypeInfo.UInt32(); - } - else if (dataType == typeof(long)) - { - result = ScalarTypeInfo.Int64(); - } - else if (dataType == typeof(ulong)) - { - result = ScalarTypeInfo.UInt64(); - } - else if (dataType == typeof(char)) - { - result = ScalarTypeInfo.Char(); - } - else if (dataType == typeof(double)) - { - result = ScalarTypeInfo.Double(); - } - else if (dataType == typeof(float)) - { - result = ScalarTypeInfo.Single(); - } - else if (dataType == typeof(DateTime)) - { - result = new DateTimeTypeInfo(); - } - else if (dataType == typeof(decimal)) - { - result = new DecimalTypeInfo(); - } - else if (dataType == typeof(IntPtr)) - { - result = ScalarTypeInfo.IntPtr(); - } - else if (dataType == typeof(UIntPtr)) - { - result = ScalarTypeInfo.UIntPtr(); - } - else if (dataType == typeof(Guid)) - { - result = ScalarTypeInfo.Guid(); - } - else if (dataType == typeof(TimeSpan)) - { - result = new TimeSpanTypeInfo(); - } - else if (dataType == typeof(DateTimeOffset)) - { - result = new DateTimeOffsetTypeInfo(); - } - else if (dataType == typeof(EmptyStruct)) - { - result = new NullTypeInfo(); - } - else if (IsGenericMatch(dataType, typeof(Nullable<>))) - { - result = new NullableTypeInfo(dataType, recursionCheck); - } - else - { - Type? elementType = FindEnumerableElementType(dataType); - if (elementType != null) - { - result = new EnumerableTypeInfo(dataType, TraceLoggingTypeInfo.GetInstance(elementType, recursionCheck)); - } - else - { - throw new ArgumentException(SR.Format(SR.EventSource_NonCompliantTypeError, dataType.Name)); - } - } - } - - return result; - } - - #endregion - } -} diff --git a/netcore/System.Private.CoreLib/shared/System/Diagnostics/Tracing/TraceLogging/TraceLoggingDataCollector.cs b/netcore/System.Private.CoreLib/shared/System/Diagnostics/Tracing/TraceLogging/TraceLoggingDataCollector.cs deleted file mode 100644 index a5b7621b647..00000000000 --- a/netcore/System.Private.CoreLib/shared/System/Diagnostics/Tracing/TraceLogging/TraceLoggingDataCollector.cs +++ /dev/null @@ -1,125 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -#if ES_BUILD_STANDALONE -using System; -#endif - -#if ES_BUILD_STANDALONE -namespace Microsoft.Diagnostics.Tracing -#else -namespace System.Diagnostics.Tracing -#endif -{ - /// <summary> - /// TraceLogging: Used when implementing a custom TraceLoggingTypeInfo. - /// The instance of this type is provided to the TypeInfo.WriteData method. - /// All operations are forwarded to the current thread's DataCollector. - /// Note that this abstraction would allow us to expose the custom - /// serialization system to partially-trusted code. If we end up not - /// making custom serialization public, or if we only expose it to - /// full-trust code, this abstraction is unnecessary (though it probably - /// doesn't hurt anything). - /// </summary> - internal unsafe class TraceLoggingDataCollector - { - internal static readonly TraceLoggingDataCollector Instance = new TraceLoggingDataCollector(); - - private TraceLoggingDataCollector() - { - return; - } - - /// <summary> - /// Marks the start of a non-blittable array or enumerable. - /// </summary> - /// <returns>Bookmark to be passed to EndBufferedArray.</returns> - public int BeginBufferedArray() - { - return DataCollector.ThreadInstance.BeginBufferedArray(); - } - - /// <summary> - /// Marks the end of a non-blittable array or enumerable. - /// </summary> - /// <param name="bookmark">The value returned by BeginBufferedArray.</param> - /// <param name="count">The number of items in the array.</param> - public void EndBufferedArray(int bookmark, int count) - { - DataCollector.ThreadInstance.EndBufferedArray(bookmark, count); - } - - /// <summary> - /// Adds the start of a group to the event. - /// This has no effect on the event payload, but is provided to allow - /// WriteMetadata and WriteData implementations to have similar - /// sequences of calls, allowing for easier verification of correctness. - /// </summary> - public TraceLoggingDataCollector AddGroup() - { - return this; - } - - public void AddScalar(PropertyValue value) - { - PropertyValue.Scalar scalar = value.ScalarValue; - DataCollector.ThreadInstance.AddScalar(&scalar, value.ScalarLength); - } - - /// <summary> - /// Adds an Int64 value to the event payload. - /// </summary> - /// <param name="value">Value to be added.</param> - public void AddScalar(long value) - { - DataCollector.ThreadInstance.AddScalar(&value, sizeof(long)); - } - - /// <summary> - /// Adds a Double value to the event payload. - /// </summary> - /// <param name="value">Value to be added.</param> - public void AddScalar(double value) - { - DataCollector.ThreadInstance.AddScalar(&value, sizeof(double)); - } - - /// <summary> - /// Adds a Boolean value to the event payload. - /// </summary> - /// <param name="value">Value to be added.</param> - public void AddScalar(bool value) - { - DataCollector.ThreadInstance.AddScalar(&value, sizeof(bool)); - } - - /// <summary> - /// Adds a null-terminated String value to the event payload. - /// </summary> - /// <param name="value"> - /// Value to be added. A null value is treated as a zero-length string. - /// </param> - public void AddNullTerminatedString(string? value) - { - DataCollector.ThreadInstance.AddNullTerminatedString(value); - } - - /// <summary> - /// Adds a counted String value to the event payload. - /// </summary> - /// <param name="value"> - /// Value to be added. A null value is treated as a zero-length string. - /// </param> - public void AddBinary(string? value) - { - DataCollector.ThreadInstance.AddBinary(value, value == null ? 0 : value.Length * 2); - } - - public void AddArray(PropertyValue value, int elementSize) - { - Array? array = (Array?)value.ReferenceValue; - DataCollector.ThreadInstance.AddArray(array, array == null ? 0 : array.Length, elementSize); - } - } -} diff --git a/netcore/System.Private.CoreLib/shared/System/Diagnostics/Tracing/TraceLogging/TraceLoggingDataType.cs b/netcore/System.Private.CoreLib/shared/System/Diagnostics/Tracing/TraceLogging/TraceLoggingDataType.cs deleted file mode 100644 index a71bf635756..00000000000 --- a/netcore/System.Private.CoreLib/shared/System/Diagnostics/Tracing/TraceLogging/TraceLoggingDataType.cs +++ /dev/null @@ -1,351 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -#if ES_BUILD_STANDALONE -using System; -#endif - -#if ES_BUILD_STANDALONE -namespace Microsoft.Diagnostics.Tracing -#else -namespace System.Diagnostics.Tracing -#endif -{ - /// <summary> - /// TraceLogging: Used when implementing a custom TraceLoggingTypeInfo. - /// These are passed to metadataCollector.Add to specify the low-level - /// type of a field in the event payload. Note that a "formatted" - /// TraceLoggingDataType consists of a core TraceLoggingDataType value - /// (a TraceLoggingDataType with a value less than 32) plus an OutType. - /// Any combination of TraceLoggingDataType + OutType is valid, but not - /// all are useful. In particular, combinations not explicitly listed - /// below are unlikely to be recognized by decoders, and will typically - /// be decoded as the corresponding core type (i.e. the decoder will - /// mask off any unrecognized OutType value). - /// </summary> - internal enum TraceLoggingDataType - { - /// <summary> - /// Core type. - /// Data type with no value (0-length payload). - /// NOTE: arrays of Nil are illegal. - /// NOTE: a fixed-length array of Nil is interpreted by the decoder as - /// a struct (obsolete but retained for backwards-compatibility). - /// </summary> - Nil = 0, - - /// <summary> - /// Core type. - /// Encoding assumes null-terminated Char16 string. - /// Decoding treats as UTF-16LE string. - /// </summary> - Utf16String = 1, - - /// <summary> - /// Core type. - /// Encoding assumes null-terminated Char8 string. - /// Decoding treats as MBCS string. - /// </summary> - MbcsString = 2, - - /// <summary> - /// Core type. - /// Encoding assumes 8-bit value. - /// Decoding treats as signed integer. - /// </summary> - Int8 = 3, - - /// <summary> - /// Core type. - /// Encoding assumes 8-bit value. - /// Decoding treats as unsigned integer. - /// </summary> - UInt8 = 4, - - /// <summary> - /// Core type. - /// Encoding assumes 16-bit value. - /// Decoding treats as signed integer. - /// </summary> - Int16 = 5, - - /// <summary> - /// Core type. - /// Encoding assumes 16-bit value. - /// Decoding treats as unsigned integer. - /// </summary> - UInt16 = 6, - - /// <summary> - /// Core type. - /// Encoding assumes 32-bit value. - /// Decoding treats as signed integer. - /// </summary> - Int32 = 7, - - /// <summary> - /// Core type. - /// Encoding assumes 32-bit value. - /// Decoding treats as unsigned integer. - /// </summary> - UInt32 = 8, - - /// <summary> - /// Core type. - /// Encoding assumes 64-bit value. - /// Decoding treats as signed integer. - /// </summary> - Int64 = 9, - - /// <summary> - /// Core type. - /// Encoding assumes 64-bit value. - /// Decoding treats as unsigned integer. - /// </summary> - UInt64 = 10, - - /// <summary> - /// Core type. - /// Encoding assumes 32-bit value. - /// Decoding treats as Float. - /// </summary> - Float = 11, - - /// <summary> - /// Core type. - /// Encoding assumes 64-bit value. - /// Decoding treats as Double. - /// </summary> - Double = 12, - - /// <summary> - /// Core type. - /// Encoding assumes 32-bit value. - /// Decoding treats as Boolean. - /// </summary> - Boolean32 = 13, - - /// <summary> - /// Core type. - /// Encoding assumes 16-bit bytecount followed by binary data. - /// Decoding treats as binary data. - /// </summary> - Binary = 14, - - /// <summary> - /// Core type. - /// Encoding assumes 16-byte value. - /// Decoding treats as GUID. - /// </summary> - Guid = 15, - - /// <summary> - /// Core type. - /// Encoding assumes 64-bit value. - /// Decoding treats as FILETIME. - /// </summary> - FileTime = 17, - - /// <summary> - /// Core type. - /// Encoding assumes 16-byte value. - /// Decoding treats as SYSTEMTIME. - /// </summary> - SystemTime = 18, - - /// <summary> - /// Core type. - /// Encoding assumes 32-bit value. - /// Decoding treats as hexadecimal unsigned integer. - /// </summary> - HexInt32 = 20, - - /// <summary> - /// Core type. - /// Encoding assumes 64-bit value. - /// Decoding treats as hexadecimal unsigned integer. - /// </summary> - HexInt64 = 21, - - /// <summary> - /// Core type. - /// Encoding assumes 16-bit bytecount followed by Char16 data. - /// Decoding treats as UTF-16LE string. - /// </summary> - CountedUtf16String = 22, - - /// <summary> - /// Core type. - /// Encoding assumes 16-bit bytecount followed by Char8 data. - /// Decoding treats as MBCS string. - /// </summary> - CountedMbcsString = 23, - - /// <summary> - /// Core type. - /// Special case: Struct indicates that this field plus the - /// subsequent N logical fields are to be considered as one logical - /// field (i.e. a nested structure). The OutType is used to encode N. - /// The maximum value for N is 127. This field has no payload by - /// itself, but logically contains the payload of the following N - /// fields. It is legal to have an array of Struct. - /// </summary> - Struct = 24, - - /// <summary> - /// Formatted type. - /// Encoding assumes 16-bit value. - /// Decoding treats as UTF-16LE character. - /// </summary> - Char16 = UInt16 + (EventFieldFormat.String << 8), - - /// <summary> - /// Formatted type. - /// Encoding assumes 8-bit value. - /// Decoding treats as character. - /// </summary> - Char8 = UInt8 + (EventFieldFormat.String << 8), - - /// <summary> - /// Formatted type. - /// Encoding assumes 8-bit value. - /// Decoding treats as Boolean. - /// </summary> - Boolean8 = UInt8 + (EventFieldFormat.Boolean << 8), - - /// <summary> - /// Formatted type. - /// Encoding assumes 8-bit value. - /// Decoding treats as hexadecimal unsigned integer. - /// </summary> - HexInt8 = UInt8 + (EventFieldFormat.Hexadecimal << 8), - - /// <summary> - /// Formatted type. - /// Encoding assumes 16-bit value. - /// Decoding treats as hexadecimal unsigned integer. - /// </summary> - HexInt16 = UInt16 + (EventFieldFormat.Hexadecimal << 8), - -#if false - /// <summary> - /// Formatted type. - /// Encoding assumes 32-bit value. - /// Decoding treats as process identifier. - /// </summary> - ProcessId = UInt32 + (EventSourceFieldFormat.ProcessId << 8), - - /// <summary> - /// Formatted type. - /// Encoding assumes 32-bit value. - /// Decoding treats as thread identifier. - /// </summary> - ThreadId = UInt32 + (EventSourceFieldFormat.ThreadId << 8), - - /// <summary> - /// Formatted type. - /// Encoding assumes 16-bit value. - /// Decoding treats as IP port. - /// </summary> - Port = UInt16 + (EventSourceFieldFormat.Port << 8), - - /// <summary> - /// Formatted type. - /// Encoding assumes 32-bit value. - /// Decoding treats as IPv4 address. - /// </summary> - Ipv4Address = UInt32 + (EventSourceFieldFormat.Ipv4Address << 8), - - /// <summary> - /// Formatted type. - /// Encoding assumes 16-bit bytecount followed by binary data. - /// Decoding treats as IPv6 address. - /// </summary> - Ipv6Address = Binary + (EventSourceFieldFormat.Ipv6Address << 8), - - /// <summary> - /// Formatted type. - /// Encoding assumes 16-bit bytecount followed by binary data. - /// Decoding treats as SOCKADDR. - /// </summary> - SocketAddress = Binary + (EventSourceFieldFormat.SocketAddress << 8), -#endif - /// <summary> - /// Formatted type. - /// Encoding assumes null-terminated Char16 string. - /// Decoding treats as UTF-16LE XML string. - /// </summary> - Utf16Xml = Utf16String + (EventFieldFormat.Xml << 8), - - /// <summary> - /// Formatted type. - /// Encoding assumes null-terminated Char8 string. - /// Decoding treats as MBCS XML string. - /// </summary> - MbcsXml = MbcsString + (EventFieldFormat.Xml << 8), - - /// <summary> - /// Formatted type. - /// Encoding assumes 16-bit bytecount followed by Char16 data. - /// Decoding treats as UTF-16LE XML. - /// </summary> - CountedUtf16Xml = CountedUtf16String + (EventFieldFormat.Xml << 8), - - /// <summary> - /// Formatted type. - /// Encoding assumes 16-bit bytecount followed by Char8 data. - /// Decoding treats as MBCS XML. - /// </summary> - CountedMbcsXml = CountedMbcsString + (EventFieldFormat.Xml << 8), - - /// <summary> - /// Formatted type. - /// Encoding assumes null-terminated Char16 string. - /// Decoding treats as UTF-16LE JSON string. - /// </summary> - Utf16Json = Utf16String + (EventFieldFormat.Json << 8), - - /// <summary> - /// Formatted type. - /// Encoding assumes null-terminated Char8 string. - /// Decoding treats as MBCS JSON string. - /// </summary> - MbcsJson = MbcsString + (EventFieldFormat.Json << 8), - - /// <summary> - /// Formatted type. - /// Encoding assumes 16-bit bytecount followed by Char16 data. - /// Decoding treats as UTF-16LE JSON. - /// </summary> - CountedUtf16Json = CountedUtf16String + (EventFieldFormat.Json << 8), - - /// <summary> - /// Formatted type. - /// Encoding assumes 16-bit bytecount followed by Char8 data. - /// Decoding treats as MBCS JSON. - /// </summary> - CountedMbcsJson = CountedMbcsString + (EventFieldFormat.Json << 8), -#if false - /// <summary> - /// Formatted type. - /// Encoding assumes 32-bit value. - /// Decoding treats as Win32 error. - /// </summary> - Win32Error = UInt32 + (EventSourceFieldFormat.Win32Error << 8), - - /// <summary> - /// Formatted type. - /// Encoding assumes 32-bit value. - /// Decoding treats as NTSTATUS. - /// </summary> - NTStatus = UInt32 + (EventSourceFieldFormat.NTStatus << 8), -#endif - /// <summary> - /// Formatted type. - /// Encoding assumes 32-bit value. - /// Decoding treats as HRESULT. - /// </summary> - HResult = Int32 + (EventFieldFormat.HResult << 8) - } -} diff --git a/netcore/System.Private.CoreLib/shared/System/Diagnostics/Tracing/TraceLogging/TraceLoggingEventSource.cs b/netcore/System.Private.CoreLib/shared/System/Diagnostics/Tracing/TraceLogging/TraceLoggingEventSource.cs deleted file mode 100644 index 3302018a214..00000000000 --- a/netcore/System.Private.CoreLib/shared/System/Diagnostics/Tracing/TraceLogging/TraceLoggingEventSource.cs +++ /dev/null @@ -1,890 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -// This program uses code hyperlinks available as part of the HyperAddin Visual Studio plug-in. -// It is available from http://www.codeplex.com/hyperAddin - -#if PLATFORM_WINDOWS -#define FEATURE_MANAGED_ETW -#endif // PLATFORM_WINDOWS - -#if ES_BUILD_STANDALONE -#define FEATURE_MANAGED_ETW_CHANNELS -// #define FEATURE_ADVANCED_MANAGED_ETW_CHANNELS -#endif - -#if ES_BUILD_STANDALONE -using System; -using System.Diagnostics; -using Environment = Microsoft.Diagnostics.Tracing.Internal.Environment; -using EventDescriptor = Microsoft.Diagnostics.Tracing.EventDescriptor; -#endif -using System.Collections.Generic; -using System.Collections.ObjectModel; -using System.Runtime.InteropServices; -using System.Text; - -#if ES_BUILD_STANDALONE -namespace Microsoft.Diagnostics.Tracing -#else -namespace System.Diagnostics.Tracing -#endif -{ - public partial class EventSource - { -#if FEATURE_MANAGED_ETW - private byte[] providerMetadata = null!; -#endif - -#if FEATURE_PERFTRACING - private readonly TraceLoggingEventHandleTable m_eventHandleTable = new TraceLoggingEventHandleTable(); -#endif - - /// <summary> - /// Construct an EventSource with a given name for non-contract based events (e.g. those using the Write() API). - /// </summary> - /// <param name="eventSourceName"> - /// The name of the event source. Must not be null. - /// </param> - public EventSource( - string eventSourceName) - : this(eventSourceName, EventSourceSettings.EtwSelfDescribingEventFormat) - { } - - /// <summary> - /// Construct an EventSource with a given name for non-contract based events (e.g. those using the Write() API). - /// </summary> - /// <param name="eventSourceName"> - /// The name of the event source. Must not be null. - /// </param> - /// <param name="config"> - /// Configuration options for the EventSource as a whole. - /// </param> - public EventSource( - string eventSourceName, - EventSourceSettings config) - : this(eventSourceName, config, null) { } - - /// <summary> - /// Construct an EventSource with a given name for non-contract based events (e.g. those using the Write() API). - /// - /// Also specify a list of key-value pairs called traits (you must pass an even number of strings). - /// The first string is the key and the second is the value. These are not interpreted by EventSource - /// itself but may be interpreted the listeners. Can be fetched with GetTrait(string). - /// </summary> - /// <param name="eventSourceName"> - /// The name of the event source. Must not be null. - /// </param> - /// <param name="config"> - /// Configuration options for the EventSource as a whole. - /// </param> - /// <param name="traits">A collection of key-value strings (must be an even number).</param> - public EventSource( - string eventSourceName, - EventSourceSettings config, - params string[]? traits) - : this( - eventSourceName == null ? default : GenerateGuidFromName(eventSourceName.ToUpperInvariant()), - eventSourceName!, - config, traits) - { - if (eventSourceName == null) - { - throw new ArgumentNullException(nameof(eventSourceName)); - } - } - - /// <summary> - /// Writes an event with no fields and default options. - /// (Native API: EventWriteTransfer) - /// </summary> - /// <param name="eventName">The name of the event.</param> - public unsafe void Write(string? eventName) - { - if (!this.IsEnabled()) - { - return; - } - - EventSourceOptions options = default; - this.WriteImpl(eventName, ref options, null, null, null, SimpleEventTypes<EmptyStruct>.Instance); - } - - /// <summary> - /// Writes an event with no fields. - /// (Native API: EventWriteTransfer) - /// </summary> - /// <param name="eventName">The name of the event.</param> - /// <param name="options"> - /// Options for the event, such as the level, keywords, and opcode. Unset - /// options will be set to default values. - /// </param> - public unsafe void Write(string? eventName, EventSourceOptions options) - { - if (!this.IsEnabled()) - { - return; - } - - this.WriteImpl(eventName, ref options, null, null, null, SimpleEventTypes<EmptyStruct>.Instance); - } - - /// <summary> - /// Writes an event. - /// (Native API: EventWriteTransfer) - /// </summary> - /// <typeparam name="T"> - /// The type that defines the event and its payload. This must be an - /// anonymous type or a type with an [EventData] attribute. - /// </typeparam> - /// <param name="eventName"> - /// The name for the event. If null, the event name is automatically - /// determined based on T, either from the Name property of T's EventData - /// attribute or from typeof(T).Name. - /// </param> - /// <param name="data"> - /// The object containing the event payload data. The type T must be - /// an anonymous type or a type with an [EventData] attribute. The - /// public instance properties of data will be written recursively to - /// create the fields of the event. - /// </param> - public unsafe void Write<T>( - string? eventName, - T data) - { - if (!this.IsEnabled()) - { - return; - } - - EventSourceOptions options = default; - this.WriteImpl(eventName, ref options, data, null, null, SimpleEventTypes<T>.Instance); - } - - /// <summary> - /// Writes an event. - /// (Native API: EventWriteTransfer) - /// </summary> - /// <typeparam name="T"> - /// The type that defines the event and its payload. This must be an - /// anonymous type or a type with an [EventData] attribute. - /// </typeparam> - /// <param name="eventName"> - /// The name for the event. If null, the event name is automatically - /// determined based on T, either from the Name property of T's EventData - /// attribute or from typeof(T).Name. - /// </param> - /// <param name="options"> - /// Options for the event, such as the level, keywords, and opcode. Unset - /// options will be set to default values. - /// </param> - /// <param name="data"> - /// The object containing the event payload data. The type T must be - /// an anonymous type or a type with an [EventData] attribute. The - /// public instance properties of data will be written recursively to - /// create the fields of the event. - /// </param> - public unsafe void Write<T>( - string? eventName, - EventSourceOptions options, - T data) - { - if (!this.IsEnabled()) - { - return; - } - - this.WriteImpl(eventName, ref options, data, null, null, SimpleEventTypes<T>.Instance); - } - - /// <summary> - /// Writes an event. - /// This overload is for use with extension methods that wish to efficiently - /// forward the options or data parameter without performing an extra copy. - /// (Native API: EventWriteTransfer) - /// </summary> - /// <typeparam name="T"> - /// The type that defines the event and its payload. This must be an - /// anonymous type or a type with an [EventData] attribute. - /// </typeparam> - /// <param name="eventName"> - /// The name for the event. If null, the event name is automatically - /// determined based on T, either from the Name property of T's EventData - /// attribute or from typeof(T).Name. - /// </param> - /// <param name="options"> - /// Options for the event, such as the level, keywords, and opcode. Unset - /// options will be set to default values. - /// </param> - /// <param name="data"> - /// The object containing the event payload data. The type T must be - /// an anonymous type or a type with an [EventData] attribute. The - /// public instance properties of data will be written recursively to - /// create the fields of the event. - /// </param> - public unsafe void Write<T>( - string? eventName, - ref EventSourceOptions options, - ref T data) - { - if (!this.IsEnabled()) - { - return; - } - - this.WriteImpl(eventName, ref options, data, null, null, SimpleEventTypes<T>.Instance); - } - - /// <summary> - /// Writes an event. - /// This overload is meant for clients that need to manipuate the activityId - /// and related ActivityId for the event. - /// </summary> - /// <typeparam name="T"> - /// The type that defines the event and its payload. This must be an - /// anonymous type or a type with an [EventData] attribute. - /// </typeparam> - /// <param name="eventName"> - /// The name for the event. If null, the event name is automatically - /// determined based on T, either from the Name property of T's EventData - /// attribute or from typeof(T).Name. - /// </param> - /// <param name="options"> - /// Options for the event, such as the level, keywords, and opcode. Unset - /// options will be set to default values. - /// </param> - /// <param name="activityId"> - /// The GUID of the activity associated with this event. - /// </param> - /// <param name="relatedActivityId"> - /// The GUID of another activity that is related to this activity, or Guid.Empty - /// if there is no related activity. Most commonly, the Start operation of a - /// new activity specifies a parent activity as its related activity. - /// </param> - /// <param name="data"> - /// The object containing the event payload data. The type T must be - /// an anonymous type or a type with an [EventData] attribute. The - /// public instance properties of data will be written recursively to - /// create the fields of the event. - /// </param> - public unsafe void Write<T>( - string? eventName, - ref EventSourceOptions options, - ref Guid activityId, - ref Guid relatedActivityId, - ref T data) - { - if (!this.IsEnabled()) - { - return; - } - - fixed (Guid* pActivity = &activityId, pRelated = &relatedActivityId) - { - this.WriteImpl( - eventName, - ref options, - data, - pActivity, - relatedActivityId == Guid.Empty ? null : pRelated, - SimpleEventTypes<T>.Instance); - } - } - - /// <summary> - /// Writes an extended event, where the values of the event are the - /// combined properties of any number of values. This method is - /// intended for use in advanced logging scenarios that support a - /// dynamic set of event context providers. - /// This method does a quick check on whether this event is enabled. - /// </summary> - /// <param name="eventName"> - /// The name for the event. If null, the name from eventTypes is used. - /// (Note that providing the event name via the name parameter is slightly - /// less efficient than using the name from eventTypes.) - /// </param> - /// <param name="options"> - /// Optional overrides for the event, such as the level, keyword, opcode, - /// activityId, and relatedActivityId. Any settings not specified by options - /// are obtained from eventTypes. - /// </param> - /// <param name="eventTypes"> - /// Information about the event and the types of the values in the event. - /// Must not be null. Note that the eventTypes object should be created once and - /// saved. It should not be recreated for each event. - /// </param> - /// <param name="activityID"> - /// A pointer to the activity ID GUID to log - /// </param> - /// <param name="childActivityID"> - /// A pointer to the child activity ID to log (can be null) </param> - /// <param name="values"> - /// The values to include in the event. Must not be null. The number and types of - /// the values must match the number and types of the fields described by the - /// eventTypes parameter. - /// </param> - private unsafe void WriteMultiMerge( - string? eventName, - ref EventSourceOptions options, - TraceLoggingEventTypes eventTypes, - Guid* activityID, - Guid* childActivityID, - params object?[] values) - { - if (!this.IsEnabled()) - { - return; - } - byte level = (options.valuesSet & EventSourceOptions.levelSet) != 0 - ? options.level - : eventTypes.level; - EventKeywords keywords = (options.valuesSet & EventSourceOptions.keywordsSet) != 0 - ? options.keywords - : eventTypes.keywords; - - if (this.IsEnabled((EventLevel)level, keywords)) - { - WriteMultiMergeInner(eventName, ref options, eventTypes, activityID, childActivityID, values); - } - } - - /// <summary> - /// Writes an extended event, where the values of the event are the - /// combined properties of any number of values. This method is - /// intended for use in advanced logging scenarios that support a - /// dynamic set of event context providers. - /// Attention: This API does not check whether the event is enabled or not. - /// Please use WriteMultiMerge to avoid spending CPU cycles for events that are - /// not enabled. - /// </summary> - /// <param name="eventName"> - /// The name for the event. If null, the name from eventTypes is used. - /// (Note that providing the event name via the name parameter is slightly - /// less efficient than using the name from eventTypes.) - /// </param> - /// <param name="options"> - /// Optional overrides for the event, such as the level, keyword, opcode, - /// activityId, and relatedActivityId. Any settings not specified by options - /// are obtained from eventTypes. - /// </param> - /// <param name="eventTypes"> - /// Information about the event and the types of the values in the event. - /// Must not be null. Note that the eventTypes object should be created once and - /// saved. It should not be recreated for each event. - /// </param> - /// <param name="activityID"> - /// A pointer to the activity ID GUID to log - /// </param> - /// <param name="childActivityID"> - /// A pointer to the child activity ID to log (can be null) - /// </param> - /// <param name="values"> - /// The values to include in the event. Must not be null. The number and types of - /// the values must match the number and types of the fields described by the - /// eventTypes parameter. - /// </param> - private unsafe void WriteMultiMergeInner( - string? eventName, - ref EventSourceOptions options, - TraceLoggingEventTypes eventTypes, - Guid* activityID, - Guid* childActivityID, - params object?[] values) - { -#if FEATURE_MANAGED_ETW - int identity = 0; - byte level = (options.valuesSet & EventSourceOptions.levelSet) != 0 - ? options.level - : eventTypes.level; - byte opcode = (options.valuesSet & EventSourceOptions.opcodeSet) != 0 - ? options.opcode - : eventTypes.opcode; - EventTags tags = (options.valuesSet & EventSourceOptions.tagsSet) != 0 - ? options.tags - : eventTypes.Tags; - EventKeywords keywords = (options.valuesSet & EventSourceOptions.keywordsSet) != 0 - ? options.keywords - : eventTypes.keywords; - - NameInfo nameInfo = eventTypes.GetNameInfo(eventName ?? eventTypes.Name, tags); - if (nameInfo == null) - { - return; - } - identity = nameInfo.identity; - EventDescriptor descriptor = new EventDescriptor(identity, level, opcode, (long)keywords); - -#if FEATURE_PERFTRACING - IntPtr eventHandle = nameInfo.GetOrCreateEventHandle(m_eventPipeProvider, m_eventHandleTable, descriptor, eventTypes); - Debug.Assert(eventHandle != IntPtr.Zero); -#else - IntPtr eventHandle = IntPtr.Zero; -#endif - - int pinCount = eventTypes.pinCount; - byte* scratch = stackalloc byte[eventTypes.scratchSize]; - EventData* descriptors = stackalloc EventData[eventTypes.dataCount + 3]; - for (int i = 0; i < eventTypes.dataCount + 3; i++) - descriptors[i] = default; - - GCHandle* pins = stackalloc GCHandle[pinCount]; - for (int i = 0; i < pinCount; i++) - pins[i] = default; - - fixed (byte* - pMetadata0 = this.providerMetadata, - pMetadata1 = nameInfo.nameMetadata, - pMetadata2 = eventTypes.typeMetadata) - { - descriptors[0].SetMetadata(pMetadata0, this.providerMetadata.Length, 2); - descriptors[1].SetMetadata(pMetadata1, nameInfo.nameMetadata.Length, 1); - descriptors[2].SetMetadata(pMetadata2, eventTypes.typeMetadata.Length, 1); - -#if (!ES_BUILD_PCL && !ES_BUILD_PN) - System.Runtime.CompilerServices.RuntimeHelpers.PrepareConstrainedRegions(); -#endif - try - { - DataCollector.ThreadInstance.Enable( - scratch, - eventTypes.scratchSize, - descriptors + 3, - eventTypes.dataCount, - pins, - pinCount); - - for (int i = 0; i < eventTypes.typeInfos.Length; i++) - { - TraceLoggingTypeInfo info = eventTypes.typeInfos[i]; - info.WriteData(TraceLoggingDataCollector.Instance, info.PropertyValueFactory(values[i])); - } - - this.WriteEventRaw( - eventName, - ref descriptor, - eventHandle, - activityID, - childActivityID, - (int)(DataCollector.ThreadInstance.Finish() - descriptors), - (IntPtr)descriptors); - } - finally - { - WriteCleanup(pins, pinCount); - } - } -#endif // FEATURE_MANAGED_ETW - } - - /// <summary> - /// Writes an extended event, where the values of the event have already - /// been serialized in "data". - /// </summary> - /// <param name="eventName"> - /// The name for the event. If null, the name from eventTypes is used. - /// (Note that providing the event name via the name parameter is slightly - /// less efficient than using the name from eventTypes.) - /// </param> - /// <param name="options"> - /// Optional overrides for the event, such as the level, keyword, opcode, - /// activityId, and relatedActivityId. Any settings not specified by options - /// are obtained from eventTypes. - /// </param> - /// <param name="eventTypes"> - /// Information about the event and the types of the values in the event. - /// Must not be null. Note that the eventTypes object should be created once and - /// saved. It should not be recreated for each event. - /// </param> - /// <param name="activityID"> - /// A pointer to the activity ID GUID to log - /// </param> - /// <param name="childActivityID"> - /// A pointer to the child activity ID to log (can be null) - /// </param> - /// <param name="data"> - /// The previously serialized values to include in the event. Must not be null. - /// The number and types of the values must match the number and types of the - /// fields described by the eventTypes parameter. - /// </param> - internal unsafe void WriteMultiMerge( - string? eventName, - ref EventSourceOptions options, - TraceLoggingEventTypes eventTypes, - Guid* activityID, - Guid* childActivityID, - EventData* data) - { -#if FEATURE_MANAGED_ETW - if (!this.IsEnabled()) - { - return; - } - - fixed (EventSourceOptions* pOptions = &options) - { - EventDescriptor descriptor; - NameInfo? nameInfo = this.UpdateDescriptor(eventName, eventTypes, ref options, out descriptor); - if (nameInfo == null) - { - return; - } - -#if FEATURE_PERFTRACING - IntPtr eventHandle = nameInfo.GetOrCreateEventHandle(m_eventPipeProvider, m_eventHandleTable, descriptor, eventTypes); - Debug.Assert(eventHandle != IntPtr.Zero); -#else - IntPtr eventHandle = IntPtr.Zero; -#endif - - // We make a descriptor for each EventData, and because we morph strings to counted strings - // we may have 2 for each arg, so we allocate enough for this. - int descriptorsLength = eventTypes.dataCount + eventTypes.typeInfos.Length * 2 + 3; - EventData* descriptors = stackalloc EventData[descriptorsLength]; - for (int i = 0; i < descriptorsLength; i++) - descriptors[i] = default; - - fixed (byte* - pMetadata0 = this.providerMetadata, - pMetadata1 = nameInfo.nameMetadata, - pMetadata2 = eventTypes.typeMetadata) - { - descriptors[0].SetMetadata(pMetadata0, this.providerMetadata.Length, 2); - descriptors[1].SetMetadata(pMetadata1, nameInfo.nameMetadata.Length, 1); - descriptors[2].SetMetadata(pMetadata2, eventTypes.typeMetadata.Length, 1); - int numDescrs = 3; - - for (int i = 0; i < eventTypes.typeInfos.Length; i++) - { - descriptors[numDescrs].m_Ptr = data[i].m_Ptr; - descriptors[numDescrs].m_Size = data[i].m_Size; - - // old conventions for bool is 4 bytes, but meta-data assumes 1. - if (data[i].m_Size == 4 && eventTypes.typeInfos[i].DataType == typeof(bool)) - descriptors[numDescrs].m_Size = 1; - - numDescrs++; - } - - this.WriteEventRaw( - eventName, - ref descriptor, - eventHandle, - activityID, - childActivityID, - numDescrs, - (IntPtr)descriptors); - } - } -#endif // FEATURE_MANAGED_ETW - } - - private unsafe void WriteImpl( - string? eventName, - ref EventSourceOptions options, - object? data, - Guid* pActivityId, - Guid* pRelatedActivityId, - TraceLoggingEventTypes eventTypes) - { - try - { - fixed (EventSourceOptions* pOptions = &options) - { - EventDescriptor descriptor; - options.Opcode = options.IsOpcodeSet ? options.Opcode : GetOpcodeWithDefault(options.Opcode, eventName); - NameInfo? nameInfo = this.UpdateDescriptor(eventName, eventTypes, ref options, out descriptor); - if (nameInfo == null) - { - return; - } - -#if FEATURE_PERFTRACING - IntPtr eventHandle = nameInfo.GetOrCreateEventHandle(m_eventPipeProvider, m_eventHandleTable, descriptor, eventTypes); - Debug.Assert(eventHandle != IntPtr.Zero); -#else - IntPtr eventHandle = IntPtr.Zero; -#endif - -#if FEATURE_MANAGED_ETW - int pinCount = eventTypes.pinCount; - byte* scratch = stackalloc byte[eventTypes.scratchSize]; - EventData* descriptors = stackalloc EventData[eventTypes.dataCount + 3]; - for (int i = 0; i < eventTypes.dataCount + 3; i++) - descriptors[i] = default; - - GCHandle* pins = stackalloc GCHandle[pinCount]; - for (int i = 0; i < pinCount; i++) - pins[i] = default; - - fixed (byte* - pMetadata0 = this.providerMetadata, - pMetadata1 = nameInfo.nameMetadata, - pMetadata2 = eventTypes.typeMetadata) - { - descriptors[0].SetMetadata(pMetadata0, this.providerMetadata.Length, 2); - descriptors[1].SetMetadata(pMetadata1, nameInfo.nameMetadata.Length, 1); - descriptors[2].SetMetadata(pMetadata2, eventTypes.typeMetadata.Length, 1); -#endif // FEATURE_MANAGED_ETW - -#if (!ES_BUILD_PCL && !ES_BUILD_PN) - System.Runtime.CompilerServices.RuntimeHelpers.PrepareConstrainedRegions(); -#endif - EventOpcode opcode = (EventOpcode)descriptor.Opcode; - - Guid activityId = Guid.Empty; - Guid relatedActivityId = Guid.Empty; - if (pActivityId == null && pRelatedActivityId == null && - ((options.ActivityOptions & EventActivityOptions.Disable) == 0)) - { - if (opcode == EventOpcode.Start) - { - Debug.Assert(eventName != null, "GetOpcodeWithDefault should not returned Start when eventName is null"); - m_activityTracker.OnStart(m_name, eventName, 0, ref activityId, ref relatedActivityId, options.ActivityOptions); - } - else if (opcode == EventOpcode.Stop) - { - Debug.Assert(eventName != null, "GetOpcodeWithDefault should not returned Stop when eventName is null"); - m_activityTracker.OnStop(m_name, eventName, 0, ref activityId); - } - if (activityId != Guid.Empty) - pActivityId = &activityId; - if (relatedActivityId != Guid.Empty) - pRelatedActivityId = &relatedActivityId; - } - - try - { -#if FEATURE_MANAGED_ETW - DataCollector.ThreadInstance.Enable( - scratch, - eventTypes.scratchSize, - descriptors + 3, - eventTypes.dataCount, - pins, - pinCount); - - TraceLoggingTypeInfo info = eventTypes.typeInfos[0]; - info.WriteData(TraceLoggingDataCollector.Instance, info.PropertyValueFactory(data)); - - this.WriteEventRaw( - eventName, - ref descriptor, - eventHandle, - pActivityId, - pRelatedActivityId, - (int)(DataCollector.ThreadInstance.Finish() - descriptors), - (IntPtr)descriptors); -#endif // FEATURE_MANAGED_ETW - - // TODO enable filtering for listeners. - if (m_Dispatchers != null) - { - var eventData = (EventPayload?)(eventTypes.typeInfos[0].GetData(data)); - WriteToAllListeners(eventName, ref descriptor, nameInfo.tags, pActivityId, pRelatedActivityId, eventData); - } - } - catch (Exception ex) - { - if (ex is EventSourceException) - throw; - else - ThrowEventSourceException(eventName, ex); - } -#if FEATURE_MANAGED_ETW - finally - { - WriteCleanup(pins, pinCount); - } - } -#endif // FEATURE_MANAGED_ETW - } - } - catch (Exception ex) - { - if (ex is EventSourceException) - throw; - else - ThrowEventSourceException(eventName, ex); - } - } - - private unsafe void WriteToAllListeners(string? eventName, ref EventDescriptor eventDescriptor, EventTags tags, Guid* pActivityId, Guid* pChildActivityId, EventPayload? payload) - { - EventWrittenEventArgs eventCallbackArgs = new EventWrittenEventArgs(this); - eventCallbackArgs.EventName = eventName; - eventCallbackArgs.m_level = (EventLevel)eventDescriptor.Level; - eventCallbackArgs.m_keywords = (EventKeywords)eventDescriptor.Keywords; - eventCallbackArgs.m_opcode = (EventOpcode)eventDescriptor.Opcode; - eventCallbackArgs.m_tags = tags; - - // Self described events do not have an id attached. We mark it internally with -1. - eventCallbackArgs.EventId = -1; - if (pActivityId != null) - eventCallbackArgs.ActivityId = *pActivityId; - if (pChildActivityId != null) - eventCallbackArgs.RelatedActivityId = *pChildActivityId; - - if (payload != null) - { - eventCallbackArgs.Payload = new ReadOnlyCollection<object?>((IList<object?>)payload.Values); - eventCallbackArgs.PayloadNames = new ReadOnlyCollection<string>((IList<string>)payload.Keys); - } - - DispatchToAllListeners(-1, eventCallbackArgs); - } - -#if (!ES_BUILD_PCL && !ES_BUILD_PN) - [System.Runtime.ConstrainedExecution.ReliabilityContract( - System.Runtime.ConstrainedExecution.Consistency.WillNotCorruptState, - System.Runtime.ConstrainedExecution.Cer.Success)] -#endif - [NonEvent] - private static unsafe void WriteCleanup(GCHandle* pPins, int cPins) - { - DataCollector.ThreadInstance.Disable(); - - for (int i = 0; i < cPins; i++) - { - if (pPins[i].IsAllocated) - { - pPins[i].Free(); - } - } - } - - private void InitializeProviderMetadata() - { -#if FEATURE_MANAGED_ETW - if (m_traits != null) - { - List<byte> traitMetaData = new List<byte>(100); - for (int i = 0; i < m_traits.Length - 1; i += 2) - { - if (m_traits[i].StartsWith("ETW_", StringComparison.Ordinal)) - { - string etwTrait = m_traits[i].Substring(4); - byte traitNum; - if (!byte.TryParse(etwTrait, out traitNum)) - { - if (etwTrait == "GROUP") - { - traitNum = 1; - } - else - { - throw new ArgumentException(SR.Format(SR.EventSource_UnknownEtwTrait, etwTrait), "traits"); - } - } - string value = m_traits[i + 1]; - int lenPos = traitMetaData.Count; - traitMetaData.Add(0); // Emit size (to be filled in later) - traitMetaData.Add(0); - traitMetaData.Add(traitNum); // Emit Trait number - int valueLen = AddValueToMetaData(traitMetaData, value) + 3; // Emit the value bytes +3 accounts for 3 bytes we emited above. - traitMetaData[lenPos] = unchecked((byte)valueLen); // Fill in size - traitMetaData[lenPos + 1] = unchecked((byte)(valueLen >> 8)); - } - } - providerMetadata = Statics.MetadataForString(this.Name, 0, traitMetaData.Count, 0); - int startPos = providerMetadata.Length - traitMetaData.Count; - foreach (byte b in traitMetaData) - providerMetadata[startPos++] = b; - } - else - providerMetadata = Statics.MetadataForString(this.Name, 0, 0, 0); -#endif //FEATURE_MANAGED_ETW - } - - private static int AddValueToMetaData(List<byte> metaData, string value) - { - if (value.Length == 0) - return 0; - - int startPos = metaData.Count; - char firstChar = value[0]; - - if (firstChar == '@') - metaData.AddRange(Encoding.UTF8.GetBytes(value.Substring(1))); - else if (firstChar == '{') - metaData.AddRange(new Guid(value).ToByteArray()); - else if (firstChar == '#') - { - for (int i = 1; i < value.Length; i++) - { - if (value[i] != ' ') // Skip spaces between bytes. - { - if (!(i + 1 < value.Length)) - { - throw new ArgumentException(SR.EventSource_EvenHexDigits, "traits"); - } - metaData.Add((byte)(HexDigit(value[i]) * 16 + HexDigit(value[i + 1]))); - i++; - } - } - } - else if ('A' <= firstChar || ' ' == firstChar) // Is it alphabetic or space (excludes digits and most punctuation). - { - metaData.AddRange(Encoding.UTF8.GetBytes(value)); - } - else - { - throw new ArgumentException(SR.Format(SR.EventSource_IllegalValue, value), "traits"); - } - - return metaData.Count - startPos; - } - - /// <summary> - /// Returns a value 0-15 if 'c' is a hexadecimal digit. If it throws an argument exception. - /// </summary> - private static int HexDigit(char c) - { - if ('0' <= c && c <= '9') - { - return c - '0'; - } - if ('a' <= c) - { - c = unchecked((char)(c - ('a' - 'A'))); // Convert to lower case - } - if ('A' <= c && c <= 'F') - { - return c - 'A' + 10; - } - - throw new ArgumentException(SR.Format(SR.EventSource_BadHexDigit, c), "traits"); - } - - private NameInfo? UpdateDescriptor( - string? name, - TraceLoggingEventTypes eventInfo, - ref EventSourceOptions options, - out EventDescriptor descriptor) - { - NameInfo? nameInfo = null; - int identity = 0; - byte level = (options.valuesSet & EventSourceOptions.levelSet) != 0 - ? options.level - : eventInfo.level; - byte opcode = (options.valuesSet & EventSourceOptions.opcodeSet) != 0 - ? options.opcode - : eventInfo.opcode; - EventTags tags = (options.valuesSet & EventSourceOptions.tagsSet) != 0 - ? options.tags - : eventInfo.Tags; - EventKeywords keywords = (options.valuesSet & EventSourceOptions.keywordsSet) != 0 - ? options.keywords - : eventInfo.keywords; - - if (this.IsEnabled((EventLevel)level, keywords)) - { - nameInfo = eventInfo.GetNameInfo(name ?? eventInfo.Name, tags); - identity = nameInfo.identity; - } - - descriptor = new EventDescriptor(identity, level, opcode, (long)keywords); - return nameInfo; - } - } -} diff --git a/netcore/System.Private.CoreLib/shared/System/Diagnostics/Tracing/TraceLogging/TraceLoggingEventTraits.cs b/netcore/System.Private.CoreLib/shared/System/Diagnostics/Tracing/TraceLogging/TraceLoggingEventTraits.cs deleted file mode 100644 index e25ae7fdc23..00000000000 --- a/netcore/System.Private.CoreLib/shared/System/Diagnostics/Tracing/TraceLogging/TraceLoggingEventTraits.cs +++ /dev/null @@ -1,30 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -#if ES_BUILD_STANDALONE -using System; -#endif - -#if ES_BUILD_STANDALONE -namespace Microsoft.Diagnostics.Tracing -#else -namespace System.Diagnostics.Tracing -#endif -{ - /// <summary> - /// Tags are flags that are not interpreted by EventSource but are passed along - /// to the EventListener. The EventListener determines the semantics of the flags. - /// </summary> - [Flags] - public enum EventTags - { - /// <summary> - /// No special traits are added to the event. - /// </summary> - None = 0, - - /* Bits below 0x10000 are available for any use by the provider. */ - /* Bits at or above 0x10000 are reserved for definition by Microsoft. */ - } -} diff --git a/netcore/System.Private.CoreLib/shared/System/Diagnostics/Tracing/TraceLogging/TraceLoggingEventTypes.cs b/netcore/System.Private.CoreLib/shared/System/Diagnostics/Tracing/TraceLogging/TraceLoggingEventTypes.cs deleted file mode 100644 index 0bd29d645ce..00000000000 --- a/netcore/System.Private.CoreLib/shared/System/Diagnostics/Tracing/TraceLogging/TraceLoggingEventTypes.cs +++ /dev/null @@ -1,243 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -#if ES_BUILD_STANDALONE -using System; -#endif -using System.Collections.Generic; - -#if ES_BUILD_STANDALONE -namespace Microsoft.Diagnostics.Tracing -#else -namespace System.Diagnostics.Tracing -#endif -{ - /// <summary> - /// TraceLogging: Used when calling EventSource.WriteMultiMerge. - /// Stores the type information to use when writing the event fields. - /// </summary> - public class TraceLoggingEventTypes - { - internal readonly TraceLoggingTypeInfo[] typeInfos; -#if FEATURE_PERFTRACING - internal readonly string[]? paramNames; -#endif - internal readonly string name; - internal readonly EventTags tags; - internal readonly byte level; - internal readonly byte opcode; - internal readonly EventKeywords keywords; - internal readonly byte[] typeMetadata; - internal readonly int scratchSize; - internal readonly int dataCount; - internal readonly int pinCount; - private ConcurrentSet<KeyValuePair<string, EventTags>, NameInfo> nameInfos; - - /// <summary> - /// Initializes a new instance of TraceLoggingEventTypes corresponding - /// to the name, flags, and types provided. Always uses the default - /// TypeInfo for each Type. - /// </summary> - /// <param name="name"> - /// The name to use when the name parameter passed to - /// EventSource.Write is null. This value must not be null. - /// </param> - /// <param name="tags"> - /// Tags to add to the event if the tags are not set via options. - /// </param> - /// <param name="types"> - /// The types of the fields in the event. This value must not be null. - /// </param> - internal TraceLoggingEventTypes( - string name, - EventTags tags, - params Type[] types) - : this(tags, name, MakeArray(types)) - { - } - - /// <summary> - /// Returns a new instance of TraceLoggingEventInfo corresponding to the name, - /// flags, and typeInfos provided. - /// </summary> - /// <param name="name"> - /// The name to use when the name parameter passed to - /// EventSource.Write is null. This value must not be null. - /// </param> - /// <param name="tags"> - /// Tags to add to the event if the tags are not set via options. - /// </param> - /// <param name="typeInfos"> - /// The types of the fields in the event. This value must not be null. - /// </param> - /// <returns> - /// An instance of TraceLoggingEventInfo with DefaultName set to the specified name - /// and with the specified typeInfos. - /// </returns> - internal TraceLoggingEventTypes( - string name, - EventTags tags, - params TraceLoggingTypeInfo[] typeInfos) - : this(tags, name, MakeArray(typeInfos)) - { - } - - internal TraceLoggingEventTypes( - string name, - EventTags tags, - System.Reflection.ParameterInfo[] paramInfos) - { - if (name == null) - { - throw new ArgumentNullException(nameof(name)); - } - - this.typeInfos = MakeArray(paramInfos); -#if FEATURE_PERFTRACING - this.paramNames = MakeParamNameArray(paramInfos); -#endif - this.name = name; - this.tags = tags; - this.level = Statics.DefaultLevel; - - var collector = new TraceLoggingMetadataCollector(); - for (int i = 0; i < typeInfos.Length; ++i) - { - TraceLoggingTypeInfo typeInfo = typeInfos[i]; - this.level = Statics.Combine((int)typeInfo.Level, this.level); - this.opcode = Statics.Combine((int)typeInfo.Opcode, this.opcode); - this.keywords |= typeInfo.Keywords; - string? paramName = paramInfos[i].Name; - if (Statics.ShouldOverrideFieldName(paramName!)) - { - paramName = typeInfo.Name; - } - typeInfo.WriteMetadata(collector, paramName, EventFieldFormat.Default); - } - - this.typeMetadata = collector.GetMetadata(); - this.scratchSize = collector.ScratchSize; - this.dataCount = collector.DataCount; - this.pinCount = collector.PinCount; - } - - private TraceLoggingEventTypes( - EventTags tags, - string defaultName, - TraceLoggingTypeInfo[] typeInfos) - { - if (defaultName == null) - { - throw new ArgumentNullException(nameof(defaultName)); - } - - this.typeInfos = typeInfos; - this.name = defaultName; - this.tags = tags; - this.level = Statics.DefaultLevel; - - var collector = new TraceLoggingMetadataCollector(); - foreach (TraceLoggingTypeInfo typeInfo in typeInfos) - { - this.level = Statics.Combine((int)typeInfo.Level, this.level); - this.opcode = Statics.Combine((int)typeInfo.Opcode, this.opcode); - this.keywords |= typeInfo.Keywords; - typeInfo.WriteMetadata(collector, null, EventFieldFormat.Default); - } - - this.typeMetadata = collector.GetMetadata(); - this.scratchSize = collector.ScratchSize; - this.dataCount = collector.DataCount; - this.pinCount = collector.PinCount; - } - - /// <summary> - /// Gets the default name that will be used for events with this descriptor. - /// </summary> - internal string Name => this.name; - - /// <summary> - /// Gets the default level that will be used for events with this descriptor. - /// </summary> - internal EventLevel Level => (EventLevel)this.level; - - /// <summary> - /// Gets the default opcode that will be used for events with this descriptor. - /// </summary> - internal EventOpcode Opcode => (EventOpcode)this.opcode; - - /// <summary> - /// Gets the default set of keywords that will added to events with this descriptor. - /// </summary> - internal EventKeywords Keywords => (EventKeywords)this.keywords; - - /// <summary> - /// Gets the default tags that will be added events with this descriptor. - /// </summary> - internal EventTags Tags => this.tags; - - internal NameInfo GetNameInfo(string name, EventTags tags) => - this.nameInfos.TryGet(new KeyValuePair<string, EventTags>(name, tags)) ?? - this.nameInfos.GetOrAdd(new NameInfo(name, tags, this.typeMetadata.Length)); - - private TraceLoggingTypeInfo[] MakeArray(System.Reflection.ParameterInfo[] paramInfos) - { - if (paramInfos == null) - { - throw new ArgumentNullException(nameof(paramInfos)); - } - - var recursionCheck = new List<Type>(paramInfos.Length); - var result = new TraceLoggingTypeInfo[paramInfos.Length]; - for (int i = 0; i < paramInfos.Length; ++i) - { - result[i] = TraceLoggingTypeInfo.GetInstance(paramInfos[i].ParameterType, recursionCheck); - } - - return result; - } - - private static TraceLoggingTypeInfo[] MakeArray(Type[] types) - { - if (types == null) - { - throw new ArgumentNullException(nameof(types)); - } - - var recursionCheck = new List<Type>(types.Length); - var result = new TraceLoggingTypeInfo[types.Length]; - for (int i = 0; i < types.Length; i++) - { - result[i] = TraceLoggingTypeInfo.GetInstance(types[i], recursionCheck); - } - - return result; - } - - private static TraceLoggingTypeInfo[] MakeArray( - TraceLoggingTypeInfo[] typeInfos) - { - if (typeInfos == null) - { - throw new ArgumentNullException(nameof(typeInfos)); - } - - return (TraceLoggingTypeInfo[])typeInfos.Clone(); - } - -#if FEATURE_PERFTRACING - private static string[] MakeParamNameArray( - System.Reflection.ParameterInfo[] paramInfos) - { - string[] paramNames = new string[paramInfos.Length]; - for (int i = 0; i < paramNames.Length; i++) - { - paramNames[i] = paramInfos[i].Name!; - } - - return paramNames; - } -#endif - } -} diff --git a/netcore/System.Private.CoreLib/shared/System/Diagnostics/Tracing/TraceLogging/TraceLoggingMetadataCollector.cs b/netcore/System.Private.CoreLib/shared/System/Diagnostics/Tracing/TraceLogging/TraceLoggingMetadataCollector.cs deleted file mode 100644 index 4ab1d054932..00000000000 --- a/netcore/System.Private.CoreLib/shared/System/Diagnostics/Tracing/TraceLogging/TraceLoggingMetadataCollector.cs +++ /dev/null @@ -1,381 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -#if ES_BUILD_STANDALONE -using System; -using Environment = Microsoft.Diagnostics.Tracing.Internal.Environment; -#endif -using System.Collections.Generic; - -#if ES_BUILD_STANDALONE -namespace Microsoft.Diagnostics.Tracing -#else -namespace System.Diagnostics.Tracing -#endif -{ - /// <summary> - /// TraceLogging: used when implementing a custom TraceLoggingTypeInfo. - /// An instance of this type is provided to the TypeInfo.WriteMetadata method. - /// </summary> - internal class TraceLoggingMetadataCollector - { - private readonly Impl impl; - private readonly FieldMetadata? currentGroup; - private int bufferedArrayFieldCount = int.MinValue; - - /// <summary> - /// Creates a root-level collector. - /// </summary> - internal TraceLoggingMetadataCollector() - { - this.impl = new Impl(); - } - - /// <summary> - /// Creates a collector for a group. - /// </summary> - /// <param name="other">Parent collector</param> - /// <param name="group">The field that starts the group</param> - private TraceLoggingMetadataCollector( - TraceLoggingMetadataCollector other, - FieldMetadata group) - { - this.impl = other.impl; - this.currentGroup = group; - } - - /// <summary> - /// The field tags to be used for the next field. - /// This will be reset to None each time a field is written. - /// </summary> - internal EventFieldTags Tags - { - get; - set; - } - - internal int ScratchSize => this.impl.scratchSize; - - internal int DataCount => this.impl.dataCount; - - internal int PinCount => this.impl.pinCount; - - private bool BeginningBufferedArray => this.bufferedArrayFieldCount == 0; - - /// <summary> - /// Call this method to add a group to the event and to return - /// a new metadata collector that can be used to add fields to the - /// group. After all of the fields in the group have been written, - /// switch back to the original metadata collector to add fields - /// outside of the group. - /// Special-case: if name is null, no group is created, and AddGroup - /// returns the original metadata collector. This is useful when - /// adding the top-level group for an event. - /// Note: do not use the original metadata collector while the group's - /// metadata collector is in use, and do not use the group's metadata - /// collector after switching back to the original. - /// </summary> - /// <param name="name"> - /// The name of the group. If name is null, the call to AddGroup is a - /// no-op (collector.AddGroup(null) returns collector). - /// </param> - /// <returns> - /// A new metadata collector that can be used to add fields to the group. - /// </returns> - public TraceLoggingMetadataCollector AddGroup(string? name) - { - TraceLoggingMetadataCollector result = this; - - if (name != null || // Normal. - this.BeginningBufferedArray) // Error, FieldMetadata's constructor will throw the appropriate exception. - { - var newGroup = new FieldMetadata( - name!, - TraceLoggingDataType.Struct, - this.Tags, - this.BeginningBufferedArray); - this.AddField(newGroup); - result = new TraceLoggingMetadataCollector(this, newGroup); - } - - return result; - } - - /// <summary> - /// Adds a scalar field to an event. - /// </summary> - /// <param name="name"> - /// The name to use for the added field. This value must not be null. - /// </param> - /// <param name="type"> - /// The type code for the added field. This must be a fixed-size type - /// (e.g. string types are not supported). - /// </param> - public void AddScalar(string name, TraceLoggingDataType type) - { - int size; - switch ((TraceLoggingDataType)((int)type & Statics.InTypeMask)) - { - case TraceLoggingDataType.Int8: - case TraceLoggingDataType.UInt8: - case TraceLoggingDataType.Char8: - size = 1; - break; - case TraceLoggingDataType.Int16: - case TraceLoggingDataType.UInt16: - case TraceLoggingDataType.Char16: - size = 2; - break; - case TraceLoggingDataType.Int32: - case TraceLoggingDataType.UInt32: - case TraceLoggingDataType.HexInt32: - case TraceLoggingDataType.Float: - case TraceLoggingDataType.Boolean32: - size = 4; - break; - case TraceLoggingDataType.Int64: - case TraceLoggingDataType.UInt64: - case TraceLoggingDataType.HexInt64: - case TraceLoggingDataType.Double: - case TraceLoggingDataType.FileTime: - size = 8; - break; - case TraceLoggingDataType.Guid: - case TraceLoggingDataType.SystemTime: - size = 16; - break; - default: - throw new ArgumentOutOfRangeException(nameof(type)); - } - - this.impl.AddScalar(size); - this.AddField(new FieldMetadata(name, type, this.Tags, this.BeginningBufferedArray)); - } - - /// <summary> - /// Adds a binary-format field to an event. - /// Compatible with core types: Binary, CountedUtf16String, CountedMbcsString. - /// Compatible with dataCollector methods: AddBinary(string), AddArray(Any8bitType[]). - /// </summary> - /// <param name="name"> - /// The name to use for the added field. This value must not be null. - /// </param> - /// <param name="type"> - /// The type code for the added field. This must be a Binary or CountedString type. - /// </param> - public void AddBinary(string name, TraceLoggingDataType type) - { - switch ((TraceLoggingDataType)((int)type & Statics.InTypeMask)) - { - case TraceLoggingDataType.Binary: - case TraceLoggingDataType.CountedMbcsString: - case TraceLoggingDataType.CountedUtf16String: - break; - default: - throw new ArgumentOutOfRangeException(nameof(type)); - } - - this.impl.AddScalar(2); - this.impl.AddNonscalar(); - this.AddField(new FieldMetadata(name, type, this.Tags, this.BeginningBufferedArray)); - } - - /// <summary> - /// Adds a null-terminated string field to an event. - /// Compatible with core types: Utf16String, MbcsString. - /// Compatible with dataCollector method: AddNullTerminatedString(string). - /// </summary> - /// <param name="name"> - /// The name to use for the added field. This value must not be null. - /// </param> - /// <param name="type"> - /// The type code for the added field. This must be a null-terminated string type. - /// </param> - public void AddNullTerminatedString(string name, TraceLoggingDataType type) - { - switch ((TraceLoggingDataType)((int)type & Statics.InTypeMask)) - { - case TraceLoggingDataType.Utf16String: - break; - default: - throw new ArgumentOutOfRangeException(nameof(type)); - } - - this.impl.AddNonscalar(); - this.AddField(new FieldMetadata(name, type, this.Tags, this.BeginningBufferedArray)); - } - - /// <summary> - /// Adds an array field to an event. - /// </summary> - /// <param name="name"> - /// The name to use for the added field. This value must not be null. - /// </param> - /// <param name="type"> - /// The type code for the added field. This must be a fixed-size type. - /// </param> - public void AddArray(string name, TraceLoggingDataType type) - { - switch ((TraceLoggingDataType)((int)type & Statics.InTypeMask)) - { - case TraceLoggingDataType.Int8: - case TraceLoggingDataType.UInt8: - case TraceLoggingDataType.Int16: - case TraceLoggingDataType.UInt16: - case TraceLoggingDataType.Int32: - case TraceLoggingDataType.UInt32: - case TraceLoggingDataType.Int64: - case TraceLoggingDataType.UInt64: - case TraceLoggingDataType.Float: - case TraceLoggingDataType.Double: - case TraceLoggingDataType.Boolean32: - case TraceLoggingDataType.Guid: - case TraceLoggingDataType.FileTime: - case TraceLoggingDataType.HexInt32: - case TraceLoggingDataType.HexInt64: - case TraceLoggingDataType.Char16: - case TraceLoggingDataType.Char8: - break; - default: - throw new ArgumentOutOfRangeException(nameof(type)); - } - - if (this.BeginningBufferedArray) - { - throw new NotSupportedException(SR.EventSource_NotSupportedNestedArraysEnums); - } - - this.impl.AddScalar(2); - this.impl.AddNonscalar(); - this.AddField(new FieldMetadata(name, type, this.Tags, true)); - } - - public void BeginBufferedArray() - { - if (this.bufferedArrayFieldCount >= 0) - { - throw new NotSupportedException(SR.EventSource_NotSupportedNestedArraysEnums); - } - - this.bufferedArrayFieldCount = 0; - this.impl.BeginBuffered(); - } - - public void EndBufferedArray() - { - if (this.bufferedArrayFieldCount != 1) - { - throw new InvalidOperationException(SR.EventSource_IncorrentlyAuthoredTypeInfo); - } - - this.bufferedArrayFieldCount = int.MinValue; - this.impl.EndBuffered(); - } - - /// <summary> - /// Adds a custom-serialized field to an event. - /// </summary> - /// <param name="name"> - /// The name to use for the added field. This value must not be null. - /// </param> - /// <param name="type">The encoding type for the field.</param> - /// <param name="metadata">Additional information needed to decode the field, if any.</param> - public void AddCustom(string name, TraceLoggingDataType type, byte[] metadata) - { - if (this.BeginningBufferedArray) - { - throw new NotSupportedException(SR.EventSource_NotSupportedCustomSerializedData); - } - - this.impl.AddScalar(2); - this.impl.AddNonscalar(); - this.AddField(new FieldMetadata( - name, - type, - this.Tags, - metadata)); - } - - internal byte[] GetMetadata() - { - int size = this.impl.Encode(null); - var metadata = new byte[size]; - this.impl.Encode(metadata); - return metadata; - } - - private void AddField(FieldMetadata fieldMetadata) - { - this.Tags = EventFieldTags.None; - this.bufferedArrayFieldCount++; - this.impl.fields.Add(fieldMetadata); - - if (this.currentGroup != null) - { - this.currentGroup.IncrementStructFieldCount(); - } - } - - private class Impl - { - internal readonly List<FieldMetadata> fields = new List<FieldMetadata>(); - internal short scratchSize; - internal sbyte dataCount; - internal sbyte pinCount; - private int bufferNesting; - private bool scalar; - - public void AddScalar(int size) - { - if (this.bufferNesting == 0) - { - if (!this.scalar) - { - this.dataCount = checked((sbyte)(this.dataCount + 1)); - } - - this.scalar = true; - this.scratchSize = checked((short)(this.scratchSize + size)); - } - } - - public void AddNonscalar() - { - if (this.bufferNesting == 0) - { - this.scalar = false; - this.pinCount = checked((sbyte)(this.pinCount + 1)); - this.dataCount = checked((sbyte)(this.dataCount + 1)); - } - } - - public void BeginBuffered() - { - if (this.bufferNesting == 0) - { - this.AddNonscalar(); - } - - this.bufferNesting++; - } - - public void EndBuffered() - { - this.bufferNesting--; - } - - public int Encode(byte[]? metadata) - { - int size = 0; - - foreach (FieldMetadata field in this.fields) - { - field.Encode(ref size, metadata); - } - - return size; - } - } - } -} diff --git a/netcore/System.Private.CoreLib/shared/System/Diagnostics/Tracing/TraceLogging/TraceLoggingTypeInfo.cs b/netcore/System.Private.CoreLib/shared/System/Diagnostics/Tracing/TraceLogging/TraceLoggingTypeInfo.cs deleted file mode 100644 index a5de12342d1..00000000000 --- a/netcore/System.Private.CoreLib/shared/System/Diagnostics/Tracing/TraceLogging/TraceLoggingTypeInfo.cs +++ /dev/null @@ -1,179 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -#if ES_BUILD_STANDALONE -using System; -#endif -using System.Collections.Generic; - -#if ES_BUILD_STANDALONE -namespace Microsoft.Diagnostics.Tracing -#else -namespace System.Diagnostics.Tracing -#endif -{ - /// <summary> - /// TraceLogging: used when implementing a custom TraceLoggingTypeInfo. - /// Non-generic base class for TraceLoggingTypeInfo<DataType>. Do not derive - /// from this class. Instead, derive from TraceLoggingTypeInfo<DataType>. - /// </summary> - internal abstract class TraceLoggingTypeInfo - { - private readonly string name; - private readonly EventKeywords keywords; - private readonly EventLevel level = (EventLevel)(-1); - private readonly EventOpcode opcode = (EventOpcode)(-1); - private readonly EventTags tags; - private readonly Type dataType; - private readonly Func<object?, PropertyValue> propertyValueFactory; - - internal TraceLoggingTypeInfo(Type dataType) - { - if (dataType == null) - { - throw new ArgumentNullException(nameof(dataType)); - } - - this.name = dataType.Name; - this.dataType = dataType; - this.propertyValueFactory = PropertyValue.GetFactory(dataType); - } - - internal TraceLoggingTypeInfo( - Type dataType, - string name, - EventLevel level, - EventOpcode opcode, - EventKeywords keywords, - EventTags tags) - { - if (dataType == null) - { - throw new ArgumentNullException(nameof(dataType)); - } - - if (name == null) - { - throw new ArgumentNullException(nameof(name)); - } - - Statics.CheckName(name); - - this.name = name; - this.keywords = keywords; - this.level = level; - this.opcode = opcode; - this.tags = tags; - this.dataType = dataType; - this.propertyValueFactory = PropertyValue.GetFactory(dataType); - } - - /// <summary> - /// Gets the name to use for the event if this type is the top-level type, - /// or the name to use for an implicitly-named field. - /// Never null. - /// </summary> - public string Name => this.name; - - /// <summary> - /// Gets the event level associated with this type. Any value in the range 0..255 - /// is an associated event level. Any value outside the range 0..255 is invalid and - /// indicates that this type has no associated event level. - /// </summary> - public EventLevel Level => this.level; - - /// <summary> - /// Gets the event opcode associated with this type. Any value in the range 0..255 - /// is an associated event opcode. Any value outside the range 0..255 is invalid and - /// indicates that this type has no associated event opcode. - /// </summary> - public EventOpcode Opcode => this.opcode; - - /// <summary> - /// Gets the keyword(s) associated with this type. - /// </summary> - public EventKeywords Keywords => this.keywords; - - /// <summary> - /// Gets the event tags associated with this type. - /// </summary> - public EventTags Tags => this.tags; - - internal Type DataType => this.dataType; - - internal Func<object?, PropertyValue> PropertyValueFactory => this.propertyValueFactory; - - /// <summary> - /// When overridden by a derived class, writes the metadata (schema) for - /// this type. Note that the sequence of operations in WriteMetadata should be - /// essentially identical to the sequence of operations in - /// WriteData/WriteObjectData. Otherwise, the metadata and data will not match, - /// which may cause trouble when decoding the event. - /// </summary> - /// <param name="collector"> - /// The object that collects metadata for this object's type. Metadata is written - /// by calling methods on the collector object. Note that if the type contains - /// sub-objects, the implementation of this method may need to call the - /// WriteMetadata method for the type of the sub-object, e.g. by calling - /// TraceLoggingTypeInfo<SubType>.Instance.WriteMetadata(...). - /// </param> - /// <param name="name"> - /// The name of the property that contains an object of this type, or null if this - /// object is being written as a top-level object of an event. Typical usage - /// is to pass this value to collector.AddGroup. - /// </param> - /// <param name="format"> - /// The format attribute for the field that contains an object of this type. - /// </param> - public abstract void WriteMetadata( - TraceLoggingMetadataCollector collector, - string? name, - EventFieldFormat format); - - /// <summary> - /// Refer to TraceLoggingTypeInfo.WriteObjectData for information about this - /// method. - /// </summary> - /// <param name="collector"> - /// Refer to TraceLoggingTypeInfo.WriteObjectData for information about this - /// method. - /// </param> - /// <param name="value"> - /// Refer to TraceLoggingTypeInfo.WriteObjectData for information about this - /// method. - /// </param> - public abstract void WriteData( - TraceLoggingDataCollector collector, - PropertyValue value); - - /// <summary> - /// Fetches the event parameter data for internal serialization. - /// </summary> - /// <param name="value"></param> - /// <returns></returns> - public virtual object? GetData(object? value) - { - return value; - } - - [ThreadStatic] // per-thread cache to avoid synchronization - private static Dictionary<Type, TraceLoggingTypeInfo>? threadCache; - - public static TraceLoggingTypeInfo GetInstance(Type type, List<Type>? recursionCheck) - { - Dictionary<Type, TraceLoggingTypeInfo> cache = threadCache ??= new Dictionary<Type, TraceLoggingTypeInfo>(); - - TraceLoggingTypeInfo? instance; - if (!cache.TryGetValue(type, out instance)) - { - recursionCheck ??= new List<Type>(); - int recursionCheckCount = recursionCheck.Count; - instance = Statics.CreateDefaultTypeInfo(type, recursionCheck); - cache[type] = instance; - recursionCheck.RemoveRange(recursionCheckCount, recursionCheck.Count - recursionCheckCount); - } - return instance; - } - } -} diff --git a/netcore/System.Private.CoreLib/shared/System/Diagnostics/Tracing/TraceLogging/TypeAnalysis.cs b/netcore/System.Private.CoreLib/shared/System/Diagnostics/Tracing/TraceLogging/TypeAnalysis.cs deleted file mode 100644 index 571298623bd..00000000000 --- a/netcore/System.Private.CoreLib/shared/System/Diagnostics/Tracing/TraceLogging/TypeAnalysis.cs +++ /dev/null @@ -1,104 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -#if ES_BUILD_STANDALONE -using System; -#endif -using System.Collections.Generic; -using System.Reflection; - -#if ES_BUILD_STANDALONE -namespace Microsoft.Diagnostics.Tracing -#else -namespace System.Diagnostics.Tracing -#endif -{ - /// <summary> - /// TraceLogging: stores the per-type information obtained by reflecting over a type. - /// </summary> - internal sealed class TypeAnalysis - { - internal readonly PropertyAnalysis[] properties; - internal readonly string? name; - internal readonly EventKeywords keywords; - internal readonly EventLevel level = (EventLevel)(-1); - internal readonly EventOpcode opcode = (EventOpcode)(-1); - internal readonly EventTags tags; - - public TypeAnalysis( - Type dataType, - EventDataAttribute? eventAttrib, - List<Type> recursionCheck) - { - IEnumerable<PropertyInfo> propertyInfos = Statics.GetProperties(dataType); - var propertyList = new List<PropertyAnalysis>(); - - foreach (PropertyInfo propertyInfo in propertyInfos) - { - if (Statics.HasCustomAttribute(propertyInfo, typeof(EventIgnoreAttribute))) - { - continue; - } - - if (!propertyInfo.CanRead || - propertyInfo.GetIndexParameters().Length != 0) - { - continue; - } - - MethodInfo? getterInfo = Statics.GetGetMethod(propertyInfo); - if (getterInfo == null) - { - continue; - } - - if (getterInfo.IsStatic || !getterInfo.IsPublic) - { - continue; - } - - Type propertyType = propertyInfo.PropertyType; - var propertyTypeInfo = TraceLoggingTypeInfo.GetInstance(propertyType, recursionCheck); - EventFieldAttribute? fieldAttribute = Statics.GetCustomAttribute<EventFieldAttribute>(propertyInfo); - - string propertyName = - fieldAttribute != null && fieldAttribute.Name != null - ? fieldAttribute.Name - : Statics.ShouldOverrideFieldName(propertyInfo.Name) - ? propertyTypeInfo.Name - : propertyInfo.Name; - propertyList.Add(new PropertyAnalysis( - propertyName, - propertyInfo, - propertyTypeInfo, - fieldAttribute)); - } - - this.properties = propertyList.ToArray(); - - foreach (PropertyAnalysis property in this.properties) - { - TraceLoggingTypeInfo typeInfo = property.typeInfo; - this.level = (EventLevel)Statics.Combine((int)typeInfo.Level, (int)this.level); - this.opcode = (EventOpcode)Statics.Combine((int)typeInfo.Opcode, (int)this.opcode); - this.keywords |= typeInfo.Keywords; - this.tags |= typeInfo.Tags; - } - - if (eventAttrib != null) - { - this.level = (EventLevel)Statics.Combine((int)eventAttrib.Level, (int)this.level); - this.opcode = (EventOpcode)Statics.Combine((int)eventAttrib.Opcode, (int)this.opcode); - this.keywords |= eventAttrib.Keywords; - this.tags |= eventAttrib.Tags; - this.name = eventAttrib.Name; - } - - if (this.name == null) - { - this.name = dataType.Name; - } - } - } -} diff --git a/netcore/System.Private.CoreLib/shared/System/Diagnostics/Tracing/Winmeta.cs b/netcore/System.Private.CoreLib/shared/System/Diagnostics/Tracing/Winmeta.cs deleted file mode 100644 index 03d86f8efab..00000000000 --- a/netcore/System.Private.CoreLib/shared/System/Diagnostics/Tracing/Winmeta.cs +++ /dev/null @@ -1,185 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -/*============================================================ -** -** -** Purpose: -** Contains eventing constants defined by the Windows -** environment. -** -============================================================*/ - -#if ES_BUILD_STANDALONE -#define FEATURE_MANAGED_ETW_CHANNELS -using System; -#endif - -#if ES_BUILD_STANDALONE -namespace Microsoft.Diagnostics.Tracing -#else -namespace System.Diagnostics.Tracing -#endif -{ - /// <summary> - /// WindowsEventLevel. Custom values must be in the range from 16 through 255 - /// </summary> - public enum EventLevel - { - /// <summary> - /// Log always - /// </summary> - LogAlways = 0, - /// <summary> - /// Only critical errors - /// </summary> - Critical, - /// <summary> - /// All errors, including previous levels - /// </summary> - Error, - /// <summary> - /// All warnings, including previous levels - /// </summary> - Warning, - /// <summary> - /// All informational events, including previous levels - /// </summary> - Informational, - /// <summary> - /// All events, including previous levels - /// </summary> - Verbose - } - /// <summary> - /// WindowsEventTask. Custom values must be in the range from 1 through 65534 - /// </summary> - public enum EventTask - { - /// <summary> - /// Undefined task - /// </summary> - None = 0 - } - /// <summary> - /// EventOpcode. Custom values must be in the range from 11 through 239 - /// </summary> - public enum EventOpcode - { - /// <summary> - /// An informational event - /// </summary> - Info = 0, - /// <summary> - /// An activity start event - /// </summary> - Start, - /// <summary> - /// An activity end event - /// </summary> - Stop, - /// <summary> - /// A trace collection start event - /// </summary> - DataCollectionStart, - /// <summary> - /// A trace collection end event - /// </summary> - DataCollectionStop, - /// <summary> - /// An extensional event - /// </summary> - Extension, - /// <summary> - /// A reply event - /// </summary> - Reply, - /// <summary> - /// An event representing the activity resuming from the suspension - /// </summary> - Resume, - /// <summary> - /// An event representing the activity is suspended, pending another activity's completion - /// </summary> - Suspend, - /// <summary> - /// An event representing the activity is transferred to another component, and can continue to work - /// </summary> - Send, - /// <summary> - /// An event representing receiving an activity transfer from another component - /// </summary> - Receive = 240 - } - - // Added for CLR V4 - /// <summary> - /// EventChannel. Custom values must be in the range from 16 through 255. Currently only predefined values allowed. - /// </summary> - public enum EventChannel : byte - { - /// <summary> - /// No channel - /// </summary> - None = 0, - // Channels 1 - 15 are reserved... - /// <summary>The admin channel</summary> - Admin = 16, - /// <summary>The operational channel</summary> - Operational = 17, - /// <summary>The analytic channel</summary> - Analytic = 18, - /// <summary>The debug channel</summary> - Debug = 19, - } - - /// <summary> - /// EventOpcode - /// </summary> - [Flags] - public enum EventKeywords : long - { - /// <summary> - /// No events. - /// </summary> - None = 0x0, - /// <summary> - /// All Events - /// </summary> - All = ~0, - /// <summary> - /// Telemetry events - /// </summary> - MicrosoftTelemetry = 0x02000000000000, - /// <summary> - /// WDI context events - /// </summary> - WdiContext = 0x02000000000000, - /// <summary> - /// WDI diagnostic events - /// </summary> - WdiDiagnostic = 0x04000000000000, - /// <summary> - /// SQM events - /// </summary> - Sqm = 0x08000000000000, - /// <summary> - /// Failed security audits - /// </summary> - AuditFailure = 0x10000000000000, - /// <summary> - /// Successful security audits - /// </summary> - AuditSuccess = 0x20000000000000, - /// <summary> - /// Transfer events where the related Activity ID is a computed value and not a GUID - /// N.B. The correct value for this field is 0x40000000000000. - /// </summary> - CorrelationHint = 0x10000000000000, - /// <summary> - /// Events raised using classic eventlog API - /// </summary> - EventLogClassic = 0x80000000000000 - } -} |