dotnet-counters: Support EventCounter prefix + remove known data (#4871)
authorNoah Falk <noahfalk@users.noreply.github.com>
Tue, 20 Aug 2024 13:48:00 +0000 (06:48 -0700)
committerGitHub <noreply@github.com>
Tue, 20 Aug 2024 13:48:00 +0000 (09:48 -0400)
- dotnet-counters now supports a prefix 'EventCounters\\' in front of
provider name to force the tool to show EventCounter data when both a
Meter and an EventSource have the same name. Without the prefix the
Meter data would be shown by default. This is intended to help
developers who are migrating between the two and want to see the old
data.

- dotnet-counters list command no longer shows a hard-coded list of
provider and counter names which was already out-of-date. Instead the
command now directs users to the online docs. This should be less work
for us and more accurate information for users.

- dotnet-counters console renderer now sorts all providers
alphabetically when displaying them. Previously providers that were in
the known provider list had special treatment and got sorted first.
Several console exporter tests had to be updated because of the sort
order change.

- JSON and CSV monitoring tests are re-enabled and use the new
EventCounters feature to keep their behavior stable across runtimes
given that 9.0 has a System.Runtime Meter and earlier runtimes do not.
We already have other tests that handle monitoring Meters and there are
tests in the runtime repo for the System.Runtime Meter data.

- CounterSet was deleted and ConfigureCounters() was refactored to use
List\<EventPipeCounterGroup\> instead. CounterSet and
EventPipeCounterGroup were already similar abstractions so there wasn't
much value in maintaining both of them. EventPipeCounterGroup also
supports marking a provider as EventCounter only which was needed for
EventCounter prefix support. The counter list parsing tests were updated
to validate the same results using the EventPipeCounterGroup API
instead.

src/Tools/dotnet-counters/CounterMonitor.cs
src/Tools/dotnet-counters/CounterSet.cs [deleted file]
src/Tools/dotnet-counters/Exporters/ConsoleWriter.cs
src/Tools/dotnet-counters/KnownData.cs [deleted file]
src/Tools/dotnet-counters/Program.cs
src/tests/dotnet-counters/ConsoleExporterTests.cs
src/tests/dotnet-counters/CounterMonitorPayloadTests.cs
src/tests/dotnet-counters/CounterMonitorTests.cs
src/tests/dotnet-counters/KnownProviderTests.cs [deleted file]

index 5f1ff39d68155290e161ae175c3ea8bcd9925124..b57bfec95d58a242f8f54dfaac2260a2170eca55 100644 (file)
@@ -23,8 +23,9 @@ namespace Microsoft.Diagnostics.Tools.Counters
     internal class CounterMonitor : ICountersLogger
     {
         private const int BufferDelaySecs = 1;
+        private const string EventCountersProviderPrefix = "EventCounters\\";
         private int _processId;
-        private CounterSet _counterList;
+        private List<EventPipeCounterGroup> _counterList;
         private IConsole _console;
         private ICounterRenderer _renderer;
         private string _output;
@@ -66,6 +67,13 @@ namespace Microsoft.Diagnostics.Tools.Counters
 
         private void HandleDiagnosticCounter(ICounterPayload payload)
         {
+            // if EventCounters were explicitly requested on the command-line then show them always
+            if (_counterList.Where(group => group.ProviderName == payload.CounterMetadata.ProviderName && group.Type == CounterGroupType.EventCounter).Any())
+            {
+                CounterPayloadReceived((CounterPayload)payload);
+                return;
+            }
+
             // init providerEventState if this is the first time we've seen an event from this provider
             if (!_providerEventStates.TryGetValue(payload.CounterMetadata.ProviderName, out ProviderEventState providerState))
             {
@@ -206,7 +214,7 @@ namespace Microsoft.Diagnostics.Tools.Counters
                         _settings.MaxTimeSeries = maxTimeSeries;
                         _settings.CounterIntervalSeconds = refreshInterval;
                         _settings.ResumeRuntime = resumeRuntime;
-                        _settings.CounterGroups = GetEventPipeProviders();
+                        _settings.CounterGroups = _counterList.ToArray();
                         _settings.UseCounterRateAndValuePayloads = true;
 
                         bool useSharedSession = false;
@@ -290,7 +298,7 @@ namespace Microsoft.Diagnostics.Tools.Counters
                         _settings.MaxTimeSeries = maxTimeSeries;
                         _settings.CounterIntervalSeconds = refreshInterval;
                         _settings.ResumeRuntime = resumeRuntime;
-                        _settings.CounterGroups = GetEventPipeProviders();
+                        _settings.CounterGroups = _counterList.ToArray();
                         _output = output;
                         _diagnosticsClient = holder.Client;
                         if (_output.Length == 0)
@@ -354,9 +362,9 @@ namespace Microsoft.Diagnostics.Tools.Counters
             }
         }
 
-        internal CounterSet ConfigureCounters(string commaSeparatedProviderListText, List<string> providerList)
+        internal List<EventPipeCounterGroup> ConfigureCounters(string commaSeparatedProviderListText, List<string> providerList)
         {
-            CounterSet counters = new();
+            List<EventPipeCounterGroup> counters = new();
             try
             {
                 if (commaSeparatedProviderListText != null)
@@ -388,24 +396,24 @@ namespace Microsoft.Diagnostics.Tools.Counters
                 }
             }
 
-            if (counters.IsEmpty)
+            if (counters.Count == 0)
             {
                 _console.Out.WriteLine($"--counters is unspecified. Monitoring System.Runtime counters by default.");
-                counters.AddAllProviderCounters("System.Runtime");
+                ParseCounterProvider("System.Runtime", counters);
             }
             return counters;
         }
 
         // parses a comma separated list of providers
-        internal static CounterSet ParseProviderList(string providerListText)
+        internal static List<EventPipeCounterGroup> ParseProviderList(string providerListText)
         {
-            CounterSet set = new();
+            List<EventPipeCounterGroup> set = new();
             ParseProviderList(providerListText, set);
             return set;
         }
 
         // parses a comma separated list of providers
-        internal static void ParseProviderList(string providerListText, CounterSet counters)
+        internal static void ParseProviderList(string providerListText, List<EventPipeCounterGroup> counters)
         {
             bool inParen = false;
             int startIdx = -1;
@@ -457,7 +465,7 @@ namespace Microsoft.Diagnostics.Tools.Counters
         //   System.Runtime
         //   System.Runtime[exception-count]
         //   System.Runtime[exception-count,cpu-usage]
-        private static void ParseCounterProvider(string providerText, CounterSet counters)
+        private static void ParseCounterProvider(string providerText, List<EventPipeCounterGroup> counters)
         {
             string[] tokens = providerText.Split('[');
             if (tokens.Length == 0)
@@ -468,12 +476,24 @@ namespace Microsoft.Diagnostics.Tools.Counters
             {
                 throw new FormatException("Expected at most one '[' in counter_provider");
             }
+
             string providerName = tokens[0];
-            if (tokens.Length == 1)
+            CounterGroupType groupType = CounterGroupType.All;
+            // EventCounters\ is a special prefix for a provider that marks it as an EventCounter provider.
+            if (providerName.StartsWith(EventCountersProviderPrefix))
             {
-                counters.AddAllProviderCounters(providerName); // Only a provider name was specified
+                providerName = providerName.Substring(EventCountersProviderPrefix.Length);
+                groupType = CounterGroupType.EventCounter;
             }
-            else
+
+            // An empty set of counters means all counters are enabled.
+            string[] enabledCounters = Array.Empty<string>();
+            EventPipeCounterGroup preExistingGroup = counters.FirstOrDefault(group => group.ProviderName == providerName);
+            if (preExistingGroup != null && preExistingGroup.Type != groupType)
+            {
+                throw new FormatException("Using the same provider name with and without the EventCounters\\ prefix in the counter list is not supported.");
+            }
+            if (tokens.Length == 2)
             {
                 string counterNames = tokens[1];
                 if (!counterNames.EndsWith(']'))
@@ -487,17 +507,34 @@ namespace Microsoft.Diagnostics.Tools.Counters
                         throw new FormatException("Unexpected characters after closing ']' in counter_provider");
                     }
                 }
-                string[] enabledCounters = counterNames.Substring(0, counterNames.Length - 1).Split(',', StringSplitOptions.RemoveEmptyEntries);
-                counters.AddProviderCounters(providerName, enabledCounters);
+                enabledCounters = counterNames.Substring(0, counterNames.Length - 1).Split(',', StringSplitOptions.RemoveEmptyEntries);
             }
-        }
 
-        private EventPipeCounterGroup[] GetEventPipeProviders() =>
-            _counterList.Providers.Select(provider => new EventPipeCounterGroup
+            // haven't seen this provider yet so add it
+            if (preExistingGroup == null)
             {
-                ProviderName = provider,
-                CounterNames = _counterList.GetCounters(provider).ToArray()
-            }).ToArray();
+                counters.Add(new EventPipeCounterGroup
+                {
+                    ProviderName = providerName,
+                    CounterNames = enabledCounters,
+                    Type = groupType
+                });
+            }
+            // we've already seen the provider so merge the configurations
+            else
+            {
+                // If the previous config had some specific counters and the new one also has specific counters then merge them.
+                // Otherwise one of the two requested all counters so the union is also all counters.
+                if (preExistingGroup.CounterNames.Length == 0 || enabledCounters.Length == 0)
+                {
+                    preExistingGroup.CounterNames = Array.Empty<string>();
+                }
+                else
+                {
+                    preExistingGroup.CounterNames = preExistingGroup.CounterNames.Union(enabledCounters).ToArray();
+                }
+            }
+        }
 
         private async Task<ReturnCode> Start(MetricsPipeline pipeline, CancellationToken token)
         {
diff --git a/src/Tools/dotnet-counters/CounterSet.cs b/src/Tools/dotnet-counters/CounterSet.cs
deleted file mode 100644 (file)
index 26483af..0000000
+++ /dev/null
@@ -1,70 +0,0 @@
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-
-using System;
-using System.Collections.Generic;
-using System.Linq;
-
-namespace Microsoft.Diagnostics.Tools.Counters
-{
-    public class CounterSet
-    {
-        // a mapping from provider to a list of counters that should be enabled
-        // an empty List means all counters are enabled
-        private readonly Dictionary<string, List<string>> _providerCounters;
-
-        public CounterSet()
-        {
-            _providerCounters = new Dictionary<string, List<string>>();
-        }
-
-        public bool IsEmpty => _providerCounters.Count == 0;
-
-        public IEnumerable<string> Providers => _providerCounters.Keys;
-
-        public bool IncludesAllCounters(string providerName)
-        {
-            return _providerCounters.TryGetValue(providerName, out List<string> enabledCounters) && enabledCounters.Count == 0;
-        }
-
-        public IEnumerable<string> GetCounters(string providerName)
-        {
-            if (!_providerCounters.TryGetValue(providerName, out List<string> enabledCounters))
-            {
-                return Array.Empty<string>();
-            }
-            return enabledCounters;
-        }
-
-        // Called when we want to enable all counters under a provider name.
-        public void AddAllProviderCounters(string providerName)
-        {
-            _providerCounters[providerName] = new List<string>();
-        }
-
-        public void AddProviderCounters(string providerName, string[] counters)
-        {
-            if (!_providerCounters.TryGetValue(providerName, out List<string> enabledCounters))
-            {
-                enabledCounters = new List<string>(counters.Distinct());
-                _providerCounters.Add(providerName, enabledCounters);
-            }
-            else if (enabledCounters.Count != 0) // empty list means all counters are enabled already
-            {
-                foreach (string counter in counters)
-                {
-                    if (!enabledCounters.Contains(counter))
-                    {
-                        enabledCounters.Add(counter);
-                    }
-                }
-            }
-        }
-
-        public bool Contains(string providerName, string counterName)
-        {
-            return _providerCounters.TryGetValue(providerName, out List<string> counters) &&
-                (counters.Count == 0 || counters.Contains(counterName));
-        }
-    }
-}
index 94df5e4dc44945616f45c2229297318f44d3687e..0acab088697d2866915ebefffbcc06ec42e3b0ee 100644 (file)
@@ -21,12 +21,10 @@ namespace Microsoft.Diagnostics.Tools.Counters.Exporters
             public ObservedProvider(string name)
             {
                 Name = name;
-                KnownData.TryGetProvider(name, out KnownProvider);
             }
 
             public string Name { get; } // Name of the category.
             public Dictionary<string, ObservedCounter> Counters { get; } = new Dictionary<string, ObservedCounter>(); // Counters in this category.
-            public readonly CounterProvider KnownProvider;
         }
 
         /// <summary>Information about an observed counter.</summary>
@@ -146,7 +144,7 @@ namespace Microsoft.Diagnostics.Tools.Counters.Exporters
             if (RenderRow(ref row) &&                                            // Blank line.
                 RenderTableRow(ref row, "Name", "Current Value", "Last Delta"))  // Table header
             {
-                foreach (ObservedProvider provider in _providers.Values.OrderBy(p => p.KnownProvider == null).ThenBy(p => p.Name)) // Known providers first.
+                foreach (ObservedProvider provider in _providers.Values.OrderBy(p => p.Name))
                 {
                     if (!RenderTableRow(ref row, $"[{provider.Name}]"))
                     {
diff --git a/src/Tools/dotnet-counters/KnownData.cs b/src/Tools/dotnet-counters/KnownData.cs
deleted file mode 100644 (file)
index 4658d6f..0000000
+++ /dev/null
@@ -1,175 +0,0 @@
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-
-using System;
-using System.Collections.Generic;
-using System.Linq;
-
-namespace Microsoft.Diagnostics.Tools.Counters
-{
-    internal static class KnownData
-    {
-        private const string maxVersion = "8.0";
-
-        internal static readonly string[] s_AllVersions = new[] { net30, net31, net50, net60, net70, net80 };
-        private static readonly string[] s_StartingNet5 = new[] { net50, net60, net70, net80 };
-        private static readonly string[] s_StartingNet6 = new[] { net60, net70, net80 };
-        private static readonly string[] s_StartingNet7 = new[] { net70, net80 };
-
-        private static readonly IReadOnlyDictionary<string, CounterProvider> _knownProviders =
-            CreateKnownProviders(maxVersion).ToDictionary(p => p.Name, StringComparer.OrdinalIgnoreCase);
-
-        private const string net80 = "8.0";
-        private const string net70 = "7.0";
-        private const string net60 = "6.0";
-        private const string net50 = "5.0";
-        private const string net31 = "3.1";
-        private const string net30 = "3.0";
-
-        private static IEnumerable<CounterProvider> CreateKnownProviders(string runtimeVersion)
-        {
-            yield return new CounterProvider(
-                "System.Runtime", // Name
-                "A default set of performance counters provided by the .NET runtime.", // Description
-                "0xffffffff", // Keywords
-                "5", // Level
-                new[] { // Counters
-                    new CounterProfile{ Name="cpu-usage", Description="The percent of process' CPU usage relative to all of the system CPU resources [0-100]", SupportedVersions=s_AllVersions },
-                    new CounterProfile{ Name="working-set", Description="Amount of working set used by the process (MB)", SupportedVersions=s_AllVersions },
-                    new CounterProfile{ Name="gc-heap-size", Description="Total heap size reported by the GC (MB)", SupportedVersions=s_AllVersions },
-                    new CounterProfile{ Name="gen-0-gc-count", Description="Number of Gen 0 GCs between update intervals", SupportedVersions=s_AllVersions },
-                    new CounterProfile{ Name="gen-1-gc-count", Description="Number of Gen 1 GCs between update intervals", SupportedVersions=s_AllVersions },
-                    new CounterProfile{ Name="gen-2-gc-count", Description="Number of Gen 2 GCs between update intervals", SupportedVersions=s_AllVersions },
-                    new CounterProfile{ Name="time-in-gc", Description="% time in GC since the last GC", SupportedVersions=s_AllVersions },
-                    new CounterProfile{ Name="gen-0-size", Description="Gen 0 Heap Size", SupportedVersions=s_AllVersions },
-                    new CounterProfile{ Name="gen-1-size", Description="Gen 1 Heap Size", SupportedVersions=s_AllVersions },
-                    new CounterProfile{ Name="gen-2-size", Description="Gen 2 Heap Size", SupportedVersions=s_AllVersions },
-                    new CounterProfile{ Name="loh-size", Description="LOH Size", SupportedVersions=s_AllVersions },
-                    new CounterProfile{ Name="poh-size", Description="POH (Pinned Object Heap) Size", SupportedVersions=s_StartingNet5 },
-                    new CounterProfile{ Name="alloc-rate", Description="Number of bytes allocated in the managed heap between update intervals", SupportedVersions=s_AllVersions },
-                    new CounterProfile{ Name="gc-fragmentation", Description="GC Heap Fragmentation", SupportedVersions=s_StartingNet5 },
-                    new CounterProfile{ Name="assembly-count", Description="Number of Assemblies Loaded", SupportedVersions=s_AllVersions },
-                    new CounterProfile{ Name="exception-count", Description="Number of Exceptions / sec", SupportedVersions=s_AllVersions },
-                    new CounterProfile{ Name="threadpool-thread-count", Description="Number of ThreadPool Threads", SupportedVersions=s_AllVersions },
-                    new CounterProfile{ Name="monitor-lock-contention-count", Description="Number of times there were contention when trying to take the monitor lock between update intervals", SupportedVersions=s_AllVersions },
-                    new CounterProfile{ Name="threadpool-queue-length", Description="ThreadPool Work Items Queue Length", SupportedVersions=s_AllVersions },
-                    new CounterProfile{ Name="threadpool-completed-items-count", Description="ThreadPool Completed Work Items Count", SupportedVersions=s_AllVersions },
-                    new CounterProfile{ Name="active-timer-count", Description="Number of timers that are currently active", SupportedVersions=s_AllVersions },
-                    new CounterProfile{ Name="il-bytes-jitted", Description="Total IL bytes jitted", SupportedVersions=s_StartingNet5 },
-                    new CounterProfile{ Name="methods-jitted-count", Description="Number of methods jitted", SupportedVersions=s_StartingNet5 },
-                    new CounterProfile{ Name="gc-committed", Description="Size of committed memory by the GC (MB)", SupportedVersions=s_StartingNet6 }
-                },
-                runtimeVersion // RuntimeVersion
-            );
-            yield return new CounterProvider(
-                "Microsoft.AspNetCore.Hosting", // Name
-                "A set of performance counters provided by ASP.NET Core.", // Description
-                "0x0", // Keywords
-                "4", // Level
-                new[] { // Counters
-                    new CounterProfile{ Name="requests-per-second", Description="Number of requests between update intervals", SupportedVersions=s_AllVersions },
-                    new CounterProfile{ Name="total-requests", Description="Total number of requests", SupportedVersions=s_AllVersions },
-                    new CounterProfile{ Name="current-requests", Description="Current number of requests", SupportedVersions=s_AllVersions },
-                    new CounterProfile{ Name="failed-requests", Description="Failed number of requests", SupportedVersions=s_AllVersions },
-                },
-                runtimeVersion
-            );
-            yield return new CounterProvider(
-                "Microsoft-AspNetCore-Server-Kestrel", // Name
-                "A set of performance counters provided by Kestrel.", // Description
-                "0x0", // Keywords
-                "4", // Level
-                new[] {
-                    new CounterProfile{ Name="connections-per-second", Description="Number of connections between update intervals", SupportedVersions=s_StartingNet5 },
-                    new CounterProfile{ Name="total-connections", Description="Total Connections", SupportedVersions=s_StartingNet5 },
-                    new CounterProfile{ Name="tls-handshakes-per-second", Description="Number of TLS Handshakes made between update intervals", SupportedVersions=s_StartingNet5 },
-                    new CounterProfile{ Name="total-tls-handshakes", Description="Total number of TLS handshakes made", SupportedVersions=s_StartingNet5 },
-                    new CounterProfile{ Name="current-tls-handshakes", Description="Number of currently active TLS handshakes", SupportedVersions=s_StartingNet5 },
-                    new CounterProfile{ Name="failed-tls-handshakes", Description="Total number of failed TLS handshakes", SupportedVersions=s_StartingNet5 },
-                    new CounterProfile{ Name="current-connections", Description="Number of current connections", SupportedVersions=s_StartingNet5 },
-                    new CounterProfile{ Name="connection-queue-length", Description="Length of Kestrel Connection Queue", SupportedVersions=s_StartingNet5 },
-                    new CounterProfile{ Name="request-queue-length", Description="Length total HTTP request queue", SupportedVersions=s_StartingNet5 },
-                },
-                runtimeVersion
-            );
-            yield return new CounterProvider(
-                "System.Net.Http",
-                "A set of performance counters for System.Net.Http",
-                "0x0", // Keywords
-                "1", // Level
-                new[] {
-                    new CounterProfile{ Name="requests-started", Description="Total Requests Started", SupportedVersions=s_StartingNet5 },
-                    new CounterProfile{ Name="requests-started-rate", Description="Number of Requests Started between update intervals", SupportedVersions=s_StartingNet5 },
-                    new CounterProfile{ Name="requests-aborted", Description="Total Requests Aborted", SupportedVersions=s_StartingNet5 },
-                    new CounterProfile{ Name="requests-aborted-rate", Description="Number of Requests Aborted between update intervals", SupportedVersions=s_StartingNet5 },
-                    new CounterProfile{ Name="current-requests", Description="Current Requests", SupportedVersions=s_StartingNet5 },
-                    new CounterProfile{ Name="http11-connections-current-total", Description="Current number of HTTP 1.1 connections", SupportedVersions=s_StartingNet5 },
-                    new CounterProfile{ Name="http20-connections-current-total", Description="Current number of HTTP 2.0 connections", SupportedVersions=s_StartingNet5 },
-                    new CounterProfile{ Name="http30-connections-current-total", Description="Current number of HTTP 3.0 connections", SupportedVersions=s_StartingNet7 },
-                    new CounterProfile{ Name="http11-requests-queue-duration", Description="Average duration of the time HTTP 1.1 requests spent in the request queue", SupportedVersions=s_StartingNet5 },
-                    new CounterProfile{ Name="http20-requests-queue-duration", Description="Average duration of the time HTTP 2.0 requests spent in the request queue", SupportedVersions=s_StartingNet5 },
-                    new CounterProfile{ Name="http30-requests-queue-duration", Description="Average duration of the time HTTP 3.0 requests spent in the request queue", SupportedVersions=s_StartingNet7 },
-                },
-                runtimeVersion
-            );
-            yield return new CounterProvider(
-                "System.Net.NameResolution",
-                "A set of performance counters for DNS lookups",
-                "0x0",
-                "1",
-                new[] {
-                    new CounterProfile{ Name="dns-lookups-requested", Description="The number of DNS lookups requested since the process started", SupportedVersions=s_StartingNet5 },
-                    new CounterProfile{ Name="dns-lookups-duration", Description="Average DNS Lookup Duration", SupportedVersions=s_StartingNet5 },
-                    new CounterProfile{ Name="current-dns-lookups", Description="The current number of DNS lookups that have started but not yet completed", SupportedVersions=s_StartingNet6 },
-                },
-                runtimeVersion
-            );
-            yield return new CounterProvider(
-                "System.Net.Security",
-                "A set of performance counters for TLS",
-                "0x0",
-                "1",
-                new[] {
-                    new CounterProfile{ Name="tls-handshake-rate", Description="The number of TLS handshakes completed per update interval", SupportedVersions=s_StartingNet5 },
-                    new CounterProfile{ Name="total-tls-handshakes", Description="The total number of TLS handshakes completed since the process started", SupportedVersions=s_StartingNet5 },
-                    new CounterProfile{ Name="current-tls-handshakes", Description="The current number of TLS handshakes that have started but not yet completed", SupportedVersions=s_StartingNet5 },
-                    new CounterProfile{ Name="failed-tls-handshakes", Description="The total number of TLS handshakes failed since the process started", SupportedVersions=s_StartingNet5 },
-                    new CounterProfile{ Name="all-tls-sessions-open", Description="The number of active all TLS sessions", SupportedVersions=s_StartingNet5 },
-                    new CounterProfile{ Name="tls10-sessions-open", Description="The number of active TLS 1.0 sessions", SupportedVersions=s_StartingNet5 },
-                    new CounterProfile{ Name="tls11-sessions-open", Description="The number of active TLS 1.1 sessions", SupportedVersions=s_StartingNet5 },
-                    new CounterProfile{ Name="tls12-sessions-open", Description="The number of active TLS 1.2 sessions", SupportedVersions=s_StartingNet5 },
-                    new CounterProfile{ Name="tls13-sessions-open", Description="The number of active TLS 1.3 sessions", SupportedVersions=s_StartingNet5 },
-                    new CounterProfile{ Name="all-tls-handshake-duration", Description="The average duration of all TLS handshakes", SupportedVersions=s_StartingNet5 },
-                    new CounterProfile{ Name="tls10-handshake-duration", Description="The average duration of TLS 1.0 handshakes", SupportedVersions=s_StartingNet5 },
-                    new CounterProfile{ Name="tls11-handshake-duration", Description="The average duration of TLS 1.1 handshakes", SupportedVersions=s_StartingNet5 },
-                    new CounterProfile{ Name="tls12-handshake-duration", Description="The average duration of TLS 1.2 handshakes", SupportedVersions=s_StartingNet5 },
-                    new CounterProfile{ Name="tls13-handshake-duration", Description="The average duration of TLS 1.3 handshakes", SupportedVersions=s_StartingNet5 },
-                },
-                runtimeVersion
-            );
-            yield return new CounterProvider(
-                "System.Net.Sockets",
-                "A set of performance counters for System.Net.Sockets",
-                "0x0",
-                "1",
-                new[] {
-                    new CounterProfile{ Name="outgoing-connections-established", Description="The total number of outgoing connections established since the process started", SupportedVersions=s_StartingNet5 },
-                    new CounterProfile{ Name="incoming-connections-established", Description="The total number of incoming connections established since the process started", SupportedVersions=s_StartingNet5 },
-                    new CounterProfile{ Name="current-outgoing-connect-attempts", Description="The current number of outgoing connect attempts that have started but not yet completed", SupportedVersions=s_StartingNet7 },
-                    new CounterProfile{ Name="bytes-received", Description="The total number of bytes received since the process started", SupportedVersions=s_StartingNet5 },
-                    new CounterProfile{ Name="bytes-sent", Description="The total number of bytes sent since the process started", SupportedVersions=s_StartingNet5 },
-                    new CounterProfile{ Name="datagrams-received", Description="The total number of datagrams received since the process started", SupportedVersions=s_StartingNet5 },
-                    new CounterProfile{ Name="datagrams-sent", Description="The total number of datagrams sent since the process started", SupportedVersions=s_StartingNet5 },
-                },
-                runtimeVersion
-            );
-        }
-
-        public static IReadOnlyList<CounterProvider> GetAllProviders(string version)
-        {
-            return CreateKnownProviders(version).Where(p => p.Counters.Count > 0).ToDictionary(p => p.Name, StringComparer.OrdinalIgnoreCase).Values.ToList();
-        }
-
-        public static bool TryGetProvider(string providerName, out CounterProvider provider) => _knownProviders.TryGetValue(providerName, out provider);
-    }
-}
index 4870e3faf8c3cbed3c9906f88cfc0df1075cf84d..b9079cefbb00a4f09a55f0c7a769d3e0234b7701 100644 (file)
@@ -138,7 +138,12 @@ namespace Microsoft.Diagnostics.Tools.Counters
         private static Option CounterOption() =>
             new(
                 alias: "--counters",
-                description: "A comma-separated list of counter providers. Counter providers can be specified as <provider_name> or <provider_name>[comma_separated_counter_names]. If the provider_name is used without qualifying counter_names then all counters will be shown. For example \"System.Runtime[cpu-usage,working-set],Microsoft.AspNetCore.Hosting\" includes the cpu-usage and working-set counters from the System.Runtime provider and all the counters from the Microsoft.AspNetCore.Hosting provider. To discover provider and counter names, use the list command.")
+                description: "A comma-separated list of counter providers. Counter providers can be specified as <provider_name> or <provider_name>[comma_separated_counter_names]. If the provider_name" +
+                " is used without qualifying counter_names then all counters will be shown. For example \"System.Runtime[dotnet.assembly.count,dotnet.gc.pause.time],Microsoft.AspNetCore.Hosting\"" +
+                " includes the dotnet.assembly.count and dotnet.gc.pause.time counters from the System.Runtime provider and all the counters from the Microsoft.AspNetCore.Hosting provider. Provider" +
+                " names can either refer to the name of a Meter for the System.Diagnostics.Metrics API or the name of an EventSource for the EventCounters API. If the monitored application has both" +
+                " a Meter and an EventSource with the same name, the Meter is automatically preferred. Use the prefix \'EventCounters\\\' in front of a provider name to only show the EventCounters." +
+                " To discover well-known provider and counter names, please visit https://learn.microsoft.com/dotnet/core/diagnostics/built-in-metrics.")
             {
                 Argument = new Argument<string>(name: "counters")
             };
@@ -216,30 +221,10 @@ namespace Microsoft.Diagnostics.Tools.Counters
                " This is useful to monitor the rate of change for a metric.")
             { };
 
-        private static readonly string[] s_SupportedRuntimeVersions = KnownData.s_AllVersions;
-
         public static int List(IConsole console, string runtimeVersion)
         {
-            if (!s_SupportedRuntimeVersions.Contains(runtimeVersion))
-            {
-                Console.WriteLine($"{runtimeVersion} is not a supported version string or a supported runtime version.");
-                Console.WriteLine("Supported version strings: 3.0, 3.1, 5.0, 6.0, 7.0, 8.0");
-                return 0;
-            }
-            IReadOnlyList<CounterProvider> profiles = KnownData.GetAllProviders(runtimeVersion);
-            int maxNameLength = profiles.Max(p => p.Name.Length);
-            Console.WriteLine($"Showing well-known counters for .NET (Core) version {runtimeVersion} only. Specific processes may support additional counters.");
-            foreach (CounterProvider profile in profiles)
-            {
-                IReadOnlyList<CounterProfile> counters = profile.GetAllCounters();
-                int maxCounterNameLength = counters.Max(c => c.Name.Length);
-                Console.WriteLine($"{profile.Name.PadRight(maxNameLength)}");
-                foreach (CounterProfile counter in profile.Counters.Values)
-                {
-                    Console.WriteLine($"    {counter.Name.PadRight(maxCounterNameLength)} \t\t {counter.Description}");
-                }
-                Console.WriteLine("");
-            }
+            Console.WriteLine("Counter information has been moved to the online .NET documentation.");
+            Console.WriteLine("Please visit https://learn.microsoft.com/dotnet/core/diagnostics/built-in-metrics.");
             return 1;
         }
 
index 1f671c39b6ffbe34299d0e5f9dfbaaf67005478b..4fdee4bef1966dc617c00a09a4c59d5fc40d9206 100644 (file)
@@ -70,10 +70,11 @@ namespace DotnetCounters.UnitTests
                                      "    Status: Running",
                                      "",
                                      "Name                                           Current Value",
-                                     "[System.Runtime]",
-                                     "    Allocation Rate (B / 1 sec)                    1,731",
                                      "[Provider2]",
-                                     "    CounterXyz (Doodads)                               0.076");
+                                     "    CounterXyz (Doodads)                               0.076",
+                                     "[System.Runtime]",
+                                     "    Allocation Rate (B / 1 sec)                    1,731");
+
         }
 
         [Fact]
@@ -339,8 +340,6 @@ namespace DotnetCounters.UnitTests
                                      "    Status: Running",
                                      "",
                                      "Name                               Current Value      Last Delta",
-                                     "[System.Runtime]",
-                                     "    Allocation Rate (B / 1 sec)        1,731",
                                      "[Provider1]",
                                      "    Counter1 ({widget} / 1 sec)",
                                      "        color",
@@ -349,7 +348,9 @@ namespace DotnetCounters.UnitTests
                                      "    Counter2 ({widget} / 1 sec)",
                                      "        size temp",
                                      "        1                                 14",
-                                     "             hot                         160");
+                                     "             hot                         160",
+                                     "[System.Runtime]",
+                                     "    Allocation Rate (B / 1 sec)        1,731");
         }
 
         [Fact]
@@ -368,8 +369,6 @@ namespace DotnetCounters.UnitTests
                                      "    Status: Running",
                                      "",
                                      "Name                               Current Value      Last Delta",
-                                     "[System.Runtime]",
-                                     "    Allocation Rate (B / 1 sec)        1,731",
                                      "[Provider1]",
                                      "    Counter1 ({widget} / 1 sec)",
                                      "        color",
@@ -378,7 +377,9 @@ namespace DotnetCounters.UnitTests
                                      "    Counter2 ({widget} / 1 sec)",
                                      "        size temp",
                                      "        1                                 14",
-                                     "             hot                         160");
+                                     "             hot                         160",
+                                     "[System.Runtime]",
+                                     "    Allocation Rate (B / 1 sec)        1,731");
 
             exporter.CounterPayloadReceived(CreateIncrementingEventCounter("System.Runtime", "Allocation Rate", "B", 1732), false);
             exporter.CounterPayloadReceived(CreateMeterCounterPreNet8("Provider1", "Counter1", "{widget}", "color=red", 0.2), false);
@@ -388,8 +389,6 @@ namespace DotnetCounters.UnitTests
                                      "    Status: Running",
                                      "",
                                      "Name                               Current Value      Last Delta",
-                                     "[System.Runtime]",
-                                     "    Allocation Rate (B / 1 sec)        1,732               1",
                                      "[Provider1]",
                                      "    Counter1 ({widget} / 1 sec)",
                                      "        color",
@@ -398,7 +397,9 @@ namespace DotnetCounters.UnitTests
                                      "    Counter2 ({widget} / 1 sec)",
                                      "        size temp",
                                      "        1                                 10              -4",
-                                     "             hot                         160");
+                                     "             hot                         160",
+                                     "[System.Runtime]",
+                                     "    Allocation Rate (B / 1 sec)        1,732               1");
         }
 
         // Starting in .NET 8 MetricsEventSource, Meter counter instruments report both rate of change and
index 5194152c8fec5945717181af1820a2c546d592f8..93ba9c0b162c289b5e50cdff71c4bc9de2181a65 100644 (file)
@@ -34,7 +34,7 @@ namespace DotnetCounters.UnitTests
 
         private ITestOutputHelper _outputHelper;
         private static readonly TimeSpan DefaultTimeout = TimeSpan.FromMinutes(2);
-        private static readonly string SystemRuntimeName = "System.Runtime";
+        private static readonly string EventCounterSystemRuntimeName = "EventCounters\\System.Runtime";
         private static readonly string TagStart = "[";
 
         private static HashSet<CounterTypes> ExpectedCounterTypes = new() { CounterTypes.Metric, CounterTypes.Rate };
@@ -66,27 +66,27 @@ namespace DotnetCounters.UnitTests
             ValidateCustomMetrics(metricComponents, CountersExportFormat.csv);
         }
 
-        [SkippableTheory(Skip = "https://github.com/dotnet/diagnostics/issues/4806"), MemberData(nameof(Configurations))]
-        public async Task TestCounterMonitorSystemRuntimeMetricsJSON(TestConfiguration configuration)
+        [SkippableTheory, MemberData(nameof(Configurations))]
+        public async Task TestCounterMonitorEventCounterSystemRuntimeMetricsJSON(TestConfiguration configuration)
         {
             CheckRuntimeOS();
 
-            List<MetricComponents> metricComponents = await GetCounterTraceJSON(configuration, new List<string> { SystemRuntimeName });
+            List<MetricComponents> metricComponents = await GetCounterTraceJSON(configuration, new List<string> { EventCounterSystemRuntimeName });
 
-            ValidateSystemRuntimeMetrics(metricComponents);
+            ValidateEventCounterSystemRuntimeMetrics(metricComponents);
         }
 
-        [SkippableTheory(Skip = "https://github.com/dotnet/diagnostics/issues/4806"), MemberData(nameof(Configurations))]
-        public async Task TestCounterMonitorSystemRuntimeMetricsCSV(TestConfiguration configuration)
+        [SkippableTheory, MemberData(nameof(Configurations))]
+        public async Task TestCounterMonitorEventCounterSystemRuntimeMetricsCSV(TestConfiguration configuration)
         {
             CheckRuntimeOS();
 
-            List<MetricComponents> metricComponents = await GetCounterTraceCSV(configuration, new List<string> { SystemRuntimeName });
+            List<MetricComponents> metricComponents = await GetCounterTraceCSV(configuration, new List<string> { EventCounterSystemRuntimeName });
 
-            ValidateSystemRuntimeMetrics(metricComponents);
+            ValidateEventCounterSystemRuntimeMetrics(metricComponents);
         }
 
-        private void ValidateSystemRuntimeMetrics(List<MetricComponents> metricComponents)
+        private void ValidateEventCounterSystemRuntimeMetrics(List<MetricComponents> metricComponents)
         {
             string[] ExpectedProviders = { "System.Runtime" };
             Assert.Equal(ExpectedProviders, metricComponents.Select(c => c.ProviderName).ToHashSet());
index 7c5f8f7ed149580879cf3e0b3cc059db8f6ccff1..0f6829db737822514ef671d9a67afa3025e4e8b2 100644 (file)
@@ -3,6 +3,7 @@
 
 using System.Collections.Generic;
 using System.Linq;
+using Microsoft.Diagnostics.Monitoring.EventPipe;
 using Microsoft.Diagnostics.Tools;
 using Microsoft.Diagnostics.Tools.Counters;
 using Xunit;
@@ -18,52 +19,65 @@ namespace DotnetCounters.UnitTests
         public void GenerateCounterListTestSingleProvider()
         {
             CounterMonitor monitor = new();
-            CounterSet counters = CounterMonitor.ParseProviderList("MySource");
-            Assert.Single(counters.Providers);
-            Assert.Equal("MySource", counters.Providers.First());
-            Assert.True(counters.IncludesAllCounters("MySource"));
+            List<EventPipeCounterGroup> counters = CounterMonitor.ParseProviderList("MySource");
+            Assert.Single(counters);
+            EventPipeCounterGroup mySourceGroup = counters.First();
+            Assert.Equal("MySource", mySourceGroup.ProviderName);
+            Assert.False(mySourceGroup.CounterNames.Any());
         }
 
         [Fact]
         public void GenerateCounterListTestSingleProviderWithFilter()
         {
             CounterMonitor monitor = new();
-            CounterSet counters = CounterMonitor.ParseProviderList("MySource[counter1,counter2,counter3]");
-            Assert.Single(counters.Providers);
-            Assert.Equal("MySource", counters.Providers.First());
-            Assert.False(counters.IncludesAllCounters("MySource"));
-            Assert.True(Enumerable.SequenceEqual(counters.GetCounters("MySource"), new string[] { "counter1", "counter2", "counter3" }));
+            List<EventPipeCounterGroup> counters = CounterMonitor.ParseProviderList("MySource[counter1,counter2,counter3]");
+            Assert.Single(counters);
+            EventPipeCounterGroup mySourceGroup = counters.First();
+            Assert.Equal("MySource", mySourceGroup.ProviderName);
+            Assert.True(Enumerable.SequenceEqual(mySourceGroup.CounterNames, new string[] { "counter1", "counter2", "counter3" }));
         }
 
         [Fact]
         public void GenerateCounterListTestManyProviders()
         {
             CounterMonitor monitor = new();
-            CounterSet counters = CounterMonitor.ParseProviderList("MySource1,MySource2,System.Runtime");
-            Assert.Equal(3, counters.Providers.Count());
-            Assert.Equal("MySource1", counters.Providers.ElementAt(0));
-            Assert.Equal("MySource2", counters.Providers.ElementAt(1));
-            Assert.Equal("System.Runtime", counters.Providers.ElementAt(2));
+            List<EventPipeCounterGroup> counters = CounterMonitor.ParseProviderList("MySource1,MySource2,System.Runtime");
+            Assert.Equal(3, counters.Count());
+            Assert.Equal("MySource1", counters.ElementAt(0).ProviderName);
+            Assert.Equal("MySource2", counters.ElementAt(1).ProviderName);
+            Assert.Equal("System.Runtime", counters.ElementAt(2).ProviderName);
+        }
+
+        [Fact]
+        public void GenerateCounterListTestEventCountersPrefix()
+        {
+            CounterMonitor monitor = new();
+            List<EventPipeCounterGroup> counters = CounterMonitor.ParseProviderList("MySource1,EventCounters\\MySource2");
+            Assert.Equal(2, counters.Count());
+            Assert.Equal("MySource1", counters.ElementAt(0).ProviderName);
+            Assert.Equal(CounterGroupType.All, counters.ElementAt(0).Type);
+            Assert.Equal("MySource2", counters.ElementAt(1).ProviderName);
+            Assert.Equal(CounterGroupType.EventCounter, counters.ElementAt(1).Type);
         }
 
         [Fact]
         public void GenerateCounterListTestManyProvidersWithFilter()
         {
             CounterMonitor monitor = new();
-            CounterSet counters = CounterMonitor.ParseProviderList("MySource1[mycounter1,mycounter2], MySource2[mycounter1], System.Runtime[cpu-usage,working-set]");
-            Assert.Equal(3, counters.Providers.Count());
+            List<EventPipeCounterGroup> counters = CounterMonitor.ParseProviderList("MySource1[mycounter1,mycounter2], MySource2[mycounter1], System.Runtime[cpu-usage,working-set]");
+            Assert.Equal(3, counters.Count());
 
-            Assert.Equal("MySource1", counters.Providers.ElementAt(0));
-            Assert.False(counters.IncludesAllCounters("MySource1"));
-            Assert.True(Enumerable.SequenceEqual(counters.GetCounters("MySource1"), new string[] { "mycounter1", "mycounter2" }));
+            EventPipeCounterGroup mySource1Group = counters.ElementAt(0);
+            Assert.Equal("MySource1", mySource1Group.ProviderName);
+            Assert.True(Enumerable.SequenceEqual(mySource1Group.CounterNames, new string[] { "mycounter1", "mycounter2" }));
 
-            Assert.Equal("MySource2", counters.Providers.ElementAt(1));
-            Assert.False(counters.IncludesAllCounters("MySource2"));
-            Assert.True(Enumerable.SequenceEqual(counters.GetCounters("MySource2"), new string[] { "mycounter1" }));
+            EventPipeCounterGroup mySource2Group = counters.ElementAt(1);
+            Assert.Equal("MySource2", mySource2Group.ProviderName);
+            Assert.True(Enumerable.SequenceEqual(mySource2Group.CounterNames, new string[] { "mycounter1" }));
 
-            Assert.Equal("System.Runtime", counters.Providers.ElementAt(2));
-            Assert.False(counters.IncludesAllCounters("System.Runtime"));
-            Assert.True(Enumerable.SequenceEqual(counters.GetCounters("System.Runtime"), new string[] { "cpu-usage", "working-set" }));
+            EventPipeCounterGroup runtimeGroup = counters.ElementAt(2);
+            Assert.Equal("System.Runtime", runtimeGroup.ProviderName);
+            Assert.True(Enumerable.SequenceEqual(runtimeGroup.CounterNames, new string[] { "cpu-usage", "working-set" }));
         }
 
         [Fact]
@@ -72,11 +86,11 @@ namespace DotnetCounters.UnitTests
             CounterMonitor monitor = new();
             List<string> commandLineProviderArgs = new() { "System.Runtime", "MyEventSource" };
             string countersOptionText = "MyEventSource1,MyEventSource2";
-            CounterSet counters = monitor.ConfigureCounters(countersOptionText, commandLineProviderArgs);
-            Assert.Contains("MyEventSource", counters.Providers);
-            Assert.Contains("MyEventSource1", counters.Providers);
-            Assert.Contains("MyEventSource2", counters.Providers);
-            Assert.Contains("System.Runtime", counters.Providers);
+            List<EventPipeCounterGroup> counters = monitor.ConfigureCounters(countersOptionText, commandLineProviderArgs);
+            Assert.Contains("MyEventSource", counters.Select(g => g.ProviderName));
+            Assert.Contains("MyEventSource1", counters.Select(g => g.ProviderName));
+            Assert.Contains("MyEventSource2", counters.Select(g => g.ProviderName));
+            Assert.Contains("System.Runtime", counters.Select(g => g.ProviderName));
         }
 
         [Fact]
@@ -85,10 +99,10 @@ namespace DotnetCounters.UnitTests
             CounterMonitor monitor = new();
             List<string> commandLineProviderArgs = new() { "System.Runtime", "MyEventSource" };
             string countersOptionText = "System.Runtime,MyEventSource";
-            CounterSet counters = monitor.ConfigureCounters(countersOptionText, commandLineProviderArgs);
-            Assert.Equal(2, counters.Providers.Count());
-            Assert.Contains("MyEventSource", counters.Providers);
-            Assert.Contains("System.Runtime", counters.Providers);
+            List<EventPipeCounterGroup> counters = monitor.ConfigureCounters(countersOptionText, commandLineProviderArgs);
+            Assert.Equal(2, counters.Count());
+            Assert.Contains("MyEventSource", counters.Select(g => g.ProviderName));
+            Assert.Contains("System.Runtime", counters.Select(g => g.ProviderName));
         }
 
         [Fact]
@@ -136,5 +150,14 @@ namespace DotnetCounters.UnitTests
             CommandLineErrorException e = Assert.Throws<CommandLineErrorException>(() => monitor.ConfigureCounters(countersOptionText, null));
             Assert.Equal("Error parsing --counters argument: Expected at most one '[' in counter_provider", e.Message);
         }
+
+        [Fact]
+        public void ParseErrorMultiplePrefixesOnSameProvider()
+        {
+            CounterMonitor monitor = new();
+            string countersOptionText = "System.Runtime,MyEventSource,EventCounters\\System.Runtime";
+            CommandLineErrorException e = Assert.Throws<CommandLineErrorException>(() => monitor.ConfigureCounters(countersOptionText, null));
+            Assert.Equal("Error parsing --counters argument: Using the same provider name with and without the EventCounters\\ prefix in the counter list is not supported.", e.Message);
+        }
     }
 }
diff --git a/src/tests/dotnet-counters/KnownProviderTests.cs b/src/tests/dotnet-counters/KnownProviderTests.cs
deleted file mode 100644 (file)
index 51a1753..0000000
+++ /dev/null
@@ -1,45 +0,0 @@
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-
-using Xunit;
-
-namespace Microsoft.Diagnostics.Tools.Counters
-{
-    /// <summary>
-    /// These test the some of the known providers that we provide as a default configuration for customers to use.
-    /// </summary>
-    public class KnownProviderTests
-    {
-        [Fact]
-        public void TestRuntimeProvider()
-        {
-            KnownData.TryGetProvider("System.Runtime", out CounterProvider runtimeProvider);
-
-            Assert.Equal("System.Runtime", runtimeProvider.Name);
-            Assert.Equal("0xffffffff", runtimeProvider.Keywords);
-            Assert.Equal("5", runtimeProvider.Level);
-            Assert.Equal("System.Runtime:0xffffffff:5:EventCounterIntervalSec=1", runtimeProvider.ToProviderString(1));
-        }
-
-        [Fact]
-        public void TestASPNETProvider()
-        {
-            KnownData.TryGetProvider("Microsoft.AspNetCore.Hosting", out CounterProvider aspnetProvider);
-
-            Assert.Equal("Microsoft.AspNetCore.Hosting", aspnetProvider.Name);
-            Assert.Equal("0x0", aspnetProvider.Keywords);
-            Assert.Equal("4", aspnetProvider.Level);
-            Assert.Equal("Microsoft.AspNetCore.Hosting:0x0:4:EventCounterIntervalSec=5", aspnetProvider.ToProviderString(5));
-        }
-
-        [Fact]
-        public void UnknownProvider()
-        {
-            KnownData.TryGetProvider("SomeRandomProvider", out CounterProvider randomProvider);
-
-            Assert.Null(randomProvider);
-        }
-
-        // TODO: Add more as we add more providers as known providers to the tool...
-    }
-}