move EventCounter to shared CoreLib (dotnet/corefx#35183)
authorSung Yoon Whang <suwhang@microsoft.com>
Sat, 9 Feb 2019 03:58:24 +0000 (19:58 -0800)
committerJan Kotas <jkotas@microsoft.com>
Sat, 9 Feb 2019 03:58:24 +0000 (19:58 -0800)
Commit migrated from https://github.com/dotnet/corefx/commit/acb7df13e9fe7c91117a7ae5a85044b9caee03b8

src/libraries/Microsoft.Diagnostics.Tracing.EventSource.Redist/src/Microsoft.Diagnostics.Tracing.EventSource.Redist.csproj
src/libraries/System.Diagnostics.Tracing/src/System.Diagnostics.Tracing.csproj
src/libraries/System.Diagnostics.Tracing/src/System/Diagnostics/Tracing/EventCounter.cs [deleted file]

index 1754e71..9843324 100644 (file)
@@ -12,7 +12,6 @@
   </PropertyGroup>
   <!-- Default configurations to help VS understand the options -->
   <ItemGroup>
-    <Compile Include="..\..\System.Diagnostics.Tracing\src\System\Diagnostics\Tracing\EventCounter.cs" />
     <Compile Include="*.cs" />
     <Compile Include="$(CommonPath)\CoreLib\System\Diagnostics\Tracing\*.cs" />
     <Compile Include="$(CommonPath)\CoreLib\System\Diagnostics\Tracing\TraceLogging\*.cs" />
index 48c606d..c8777a8 100644 (file)
@@ -8,7 +8,7 @@
   <!-- Default configurations to help VS understand the options -->
   <ItemGroup>
     <Compile Include="FxCopBaseline.cs" />
-    <Compile Include="System\Diagnostics\Tracing\EventCounter.cs" />
+    <Compile Include="$(CommonPath)\CoreLib\System\Diagnostics\Tracing\EventCounter.cs" />
   </ItemGroup>
   <ItemGroup>
     <ReferenceFromRuntime Include="System.Private.CoreLib" />
diff --git a/src/libraries/System.Diagnostics.Tracing/src/System/Diagnostics/Tracing/EventCounter.cs b/src/libraries/System.Diagnostics.Tracing/src/System/Diagnostics/Tracing/EventCounter.cs
deleted file mode 100644 (file)
index 5d568a5..0000000
+++ /dev/null
@@ -1,491 +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;
-using System.Diagnostics;
-using System.Collections;
-using System.Collections.Generic;
-using System.Threading;
-#if ES_BUILD_PCL
-    using System.Threading.Tasks;
-#endif
-
-#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 class EventCounter : IDisposable
-    {
-        /// <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)
-        {
-            if (name == null)
-            {
-                throw new ArgumentNullException(nameof(name));
-            }
-
-            if (eventSource == null)
-            {
-                throw new ArgumentNullException(nameof(eventSource));
-            }
-
-            InitializeBuffer();
-            _name = name;
-            _group = EventCounterGroup.GetEventCounterGroup(eventSource);
-            _group.Add(this);
-            _min = float.PositiveInfinity;
-            _max = float.NegativeInfinity;
-        }
-
-        /// <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(value);
-        }
-
-        /// <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()
-        {
-            var group = _group;
-            if (group != null)
-            {
-                group.Remove(this);
-                _group = null;
-            }
-        }
-
-        public override string ToString()
-        {
-            return "EventCounter '" + _name + "' Count " + _count + " Mean " + (((double)_sum) / _count).ToString("n3");
-        }
-        #region private implementation
-
-        private readonly string _name;
-        private EventCounterGroup _group;
-
-        #region Buffer Management
-
-        // Values buffering
-        private const int BufferedSize = 10;
-        private const float UnusedBufferSlotValue = float.NegativeInfinity;
-        private const int UnsetIndex = -1;
-        private volatile float[] _bufferedValues;
-        private volatile int _bufferedValuesIndex;
-
-        // arbitrarily we use _bufferfValues as the lock object.  
-        private object MyLock { get { return _bufferedValues; } }
-
-        private void InitializeBuffer()
-        {
-            _bufferedValues = new float[BufferedSize];
-            for (int i = 0; i < _bufferedValues.Length; i++)
-            {
-                _bufferedValues[i] = UnusedBufferSlotValue;
-            }
-        }
-
-        private void Enqueue(float 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)
-            {
-                float 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 (MyLock) // Lock the counter
-                        Flush();
-                    i = 0;
-                }
-
-                if (result == UnusedBufferSlotValue)
-                {
-                    // CompareExchange succeeded 
-                    _bufferedValuesIndex = i;
-                    return;
-                }
-            }
-        }
-
-        private void Flush()
-        {
-            Debug.Assert(Monitor.IsEntered(MyLock));
-            for (int i = 0; i < _bufferedValues.Length; i++)
-            {
-                var value = Interlocked.Exchange(ref _bufferedValues[i], UnusedBufferSlotValue);
-                if (value != UnusedBufferSlotValue)
-                {
-                    OnMetricWritten(value);
-                }
-            }
-
-            _bufferedValuesIndex = 0;
-        }
-
-        #endregion // Buffer Management
-
-        #region Statistics Calculation
-
-        // Statistics
-        private int _count;
-        private float _sum;
-        private float _sumSquared;
-        private float _min;
-        private float _max;
-
-        private void OnMetricWritten(float value)
-        {
-            Debug.Assert(Monitor.IsEntered(MyLock));
-            _sum += value;
-            _sumSquared += value * value;
-            if (value > _max)
-                _max = value;
-
-            if (value < _min)
-                _min = value;
-
-            _count++;
-        }
-
-        internal EventCounterPayload GetEventCounterPayload()
-        {
-            lock (MyLock)     // Lock the counter
-            {
-                Flush();
-                EventCounterPayload result = new EventCounterPayload();
-                result.Name = _name;
-                result.Count = _count;
-                if (0 < _count)
-                {
-                    result.Mean = _sum / _count;
-                    result.StandardDeviation = (float)Math.Sqrt(_sumSquared / _count - _sum * _sum / _count / _count);
-                }
-                else
-                {
-                    result.Mean = 0;
-                    result.StandardDeviation = 0;
-                }
-                result.Min = _min;
-                result.Max = _max;
-                ResetStatistics();
-                return result;
-            }
-        }
-
-        private void ResetStatistics()
-        {
-            Debug.Assert(Monitor.IsEntered(MyLock));
-            _count = 0;
-            _sum = 0;
-            _sumSquared = 0;
-            _min = float.PositiveInfinity;
-            _max = float.NegativeInfinity;
-        }
-
-        #endregion // Statistics Calculation
-
-        #endregion // private implementation
-    }
-
-    #region internal supporting classes
-
-    [EventData]
-    internal class EventCounterPayload : IEnumerable<KeyValuePair<string, object>>
-    {
-        public string Name { get; set; }
-
-        public float Mean { get; set; }
-
-        public float StandardDeviation { get; set; }
-
-        public int Count { get; set; }
-
-        public float Min { get; set; }
-
-        public float Max { get; set; }
-
-        public float IntervalSec { get; internal 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>("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);
-            }
-        }
-
-        #endregion // Implementation of the IEnumerable interface
-    }
-
-    internal class EventCounterGroup
-    {
-        private readonly EventSource _eventSource;
-        private readonly List<EventCounter> _eventCounters;
-
-        internal EventCounterGroup(EventSource eventSource)
-        {
-            _eventSource = eventSource;
-            _eventCounters = new List<EventCounter>();
-            RegisterCommandCallback();
-        }
-
-        internal void Add(EventCounter eventCounter)
-        {
-            lock (this) // Lock the EventCounterGroup
-                _eventCounters.Add(eventCounter);
-        }
-
-        internal void Remove(EventCounter eventCounter)
-        {
-            lock (this) // Lock the EventCounterGroup
-                _eventCounters.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)
-            {
-                string valueStr;
-                float value;
-                if (e.Arguments.TryGetValue("EventCounterIntervalSec", out valueStr) && float.TryParse(valueStr, out value))
-                {
-                    // Recursion through EventSource callbacks possible.  When we enable the timer
-                    // we synchonously issue a EventSource.Write event, which in turn can call back
-                    // to user code (in an EventListener) while holding this lock.   This is dangerous
-                    // because it mean this code might inadvertantly participate in a lock loop. 
-                    // The scenario seems very unlikely so we ignore that problem for now.  
-                    lock (this)      // Lock the EventCounterGroup
-                    {
-                        EnableTimer(value);
-                    }
-                }
-            }
-        }
-
-        #endregion // EventSource Command Processing
-
-        #region Global EventCounterGroup Array management
-
-        // We need eventCounters to 'attach' themselves to a particular EventSource.   
-        // this table provides the mapping from EventSource -> EventCounterGroup 
-        // which represents this 'attached' information.   
-        private static WeakReference<EventCounterGroup>[] s_eventCounterGroups;
-        private static readonly object s_eventCounterGroupsLock = new object();
-
-        private static void EnsureEventSourceIndexAvailable(int eventSourceIndex)
-        {
-            Debug.Assert(Monitor.IsEntered(s_eventCounterGroupsLock));
-            if (EventCounterGroup.s_eventCounterGroups == null)
-            {
-                EventCounterGroup.s_eventCounterGroups = new WeakReference<EventCounterGroup>[eventSourceIndex + 1];
-            }
-            else if (eventSourceIndex >= EventCounterGroup.s_eventCounterGroups.Length)
-            {
-                WeakReference<EventCounterGroup>[] newEventCounterGroups = new WeakReference<EventCounterGroup>[eventSourceIndex + 1];
-                Array.Copy(EventCounterGroup.s_eventCounterGroups, 0, newEventCounterGroups, 0, EventCounterGroup.s_eventCounterGroups.Length);
-                EventCounterGroup.s_eventCounterGroups = newEventCounterGroups;
-            }
-        }
-
-        internal static EventCounterGroup GetEventCounterGroup(EventSource eventSource)
-        {
-            lock (s_eventCounterGroupsLock)
-            {
-                int eventSourceIndex = EventListener.EventSourceIndex(eventSource);
-                EnsureEventSourceIndexAvailable(eventSourceIndex);
-                WeakReference<EventCounterGroup> weakRef = EventCounterGroup.s_eventCounterGroups[eventSourceIndex];
-                EventCounterGroup ret = null;
-                if (weakRef == null || !weakRef.TryGetTarget(out ret))
-                {
-                    ret = new EventCounterGroup(eventSource);
-                    EventCounterGroup.s_eventCounterGroups[eventSourceIndex] = new WeakReference<EventCounterGroup>(ret);
-                }
-                return ret;
-            }
-        }
-
-        #endregion // Global EventCounterGroup Array management
-
-        #region Timer Processing
-
-        private DateTime _timeStampSinceCollectionStarted;
-        private int _pollingIntervalInMilliseconds;
-        private Timer _pollingTimer;
-
-        private void DisposeTimer()
-        {
-            Debug.Assert(Monitor.IsEntered(this));
-            if (_pollingTimer != null)
-            {
-                _pollingTimer.Dispose();
-                _pollingTimer = null;
-            }
-        }
-
-        private void EnableTimer(float pollingIntervalInSeconds)
-        {
-            Debug.Assert(Monitor.IsEntered(this));
-            if (pollingIntervalInSeconds <= 0)
-            {
-                DisposeTimer();
-                _pollingIntervalInMilliseconds = 0;
-            }
-            else if (_pollingIntervalInMilliseconds == 0 || pollingIntervalInSeconds * 1000 < _pollingIntervalInMilliseconds)
-            {
-                Debug.WriteLine("Polling interval changed at " + DateTime.UtcNow.ToString("mm.ss.ffffff"));
-                _pollingIntervalInMilliseconds = (int)(pollingIntervalInSeconds * 1000);
-                DisposeTimer();
-                _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;
-                    }
-
-                    _pollingTimer = new Timer(s => ((EventCounterGroup)s).OnTimer(null), this, _pollingIntervalInMilliseconds, _pollingIntervalInMilliseconds);
-                }
-                finally
-                {
-                    // Restore the current ExecutionContext
-                    if (restoreFlow)
-                        ExecutionContext.RestoreFlow();
-                }
-            }
-            // Always fire the timer event (so you see everything up to this time).  
-            OnTimer(null);
-        }
-
-        private void OnTimer(object state)
-        {
-            Debug.WriteLine("Timer fired at " + DateTime.UtcNow.ToString("mm.ss.ffffff"));
-            lock (this) // Lock the EventCounterGroup
-            {
-                if (_eventSource.IsEnabled())
-                {
-                    DateTime now = DateTime.UtcNow;
-                    TimeSpan elapsed = now - _timeStampSinceCollectionStarted;
-
-                    foreach (var eventCounter in _eventCounters)
-                    {
-                        EventCounterPayload payload = eventCounter.GetEventCounterPayload();
-                        payload.IntervalSec = (float)elapsed.TotalSeconds;
-                        _eventSource.Write("EventCounters", new EventSourceOptions() { Level = EventLevel.LogAlways }, new PayloadType(payload));
-                    }
-                    _timeStampSinceCollectionStarted = now;
-                }
-                else
-                {
-                    DisposeTimer();
-                }
-            }
-        }
-
-        /// <summary>
-        /// This is the payload that is sent in the with EventSource.Write
-        /// </summary>
-        [EventData]
-        class PayloadType
-        {
-            public PayloadType(EventCounterPayload payload) { Payload = payload; }
-            public EventCounterPayload Payload { get; set; }
-        }
-
-        #region PCL timer hack
-
-#if ES_BUILD_PCL
-    internal delegate void TimerCallback(object state);
-
-        internal sealed class Timer : CancellationTokenSource, IDisposable
-        {
-            private int _period;
-            private TimerCallback _callback;
-            private object _state;
-
-            internal Timer(TimerCallback callback, object state, int dueTime, int period)
-            {
-                _callback = callback;
-                _state = state;
-                _period = period;
-                Schedule(dueTime);
-            }
-
-            private void Schedule(int dueTime)
-            {
-                Task.Delay(dueTime, Token).ContinueWith(OnTimer, null, CancellationToken.None, TaskContinuationOptions.ExecuteSynchronously | TaskContinuationOptions.OnlyOnRanToCompletion, TaskScheduler.Default);
-            }
-
-            private void OnTimer(Task t, object s)
-            {
-                Schedule(_period);
-                _callback(_state);
-            }
-
-            public new void Dispose() { base.Cancel(); }
-        }
-#endif
-        #endregion // PCL timer hack
-
-        #endregion // Timer Processing
-
-    }
-
-    #endregion // internal supporting classes
-}