diff --git a/src/Benchmarks.ServerJob/ServerJob.cs b/src/Benchmarks.ServerJob/ServerJob.cs index 81bfca108..62d028463 100644 --- a/src/Benchmarks.ServerJob/ServerJob.cs +++ b/src/Benchmarks.ServerJob/ServerJob.cs @@ -137,6 +137,12 @@ public ServerJob ClearServerCounters() // Other collection options public bool CollectStartup { get; set; } public bool CollectCounters { get; set; } + + /// + /// The list of performance counter providers to be collected. Defaults to System.Runtime. + /// + public List CounterProviders { get; set; } = new List(); + public string BasePath { get; set; } public int ProcessId { get; set; } public Dictionary EnvironmentVariables { get; set; } = new Dictionary(); diff --git a/src/BenchmarksDriver2/README.md b/src/BenchmarksDriver2/README.md index 8c7019f93..79a401eed 100644 --- a/src/BenchmarksDriver2/README.md +++ b/src/BenchmarksDriver2/README.md @@ -69,6 +69,7 @@ Options: --[JOB].dotnetTraceProviders An optional profile name or list of dotnet-trace providers can be passed. Default is 'cpu-sampling'. See https://github.com/dotnet/diagnostics/blob/master/documentation/dotnet-trace-instructions.md for details. e.g., Microsoft-DotNETCore-SampleProfiler, Microsoft-Windows-DotNETRuntime, gc-verbose. Can be used multiple times to set multiple providers. --[JOB].options.traceOutput The name of the trace file. Can be a file prefix (app will add *.DATE*.zip) , or a specific name and no DATE* will be added e.g., c:\traces\mytrace --[JOB].collectCounters Whether to collect dotnet counters. + --[JOB].counterProviders The name of a performance counter provider from which to collect. --[JOB].collectStartup Whether to includes the startup phase in the traces, i.e after the application is launched and before it is marked as ready. For a web application it means before it is ready to accept requests. ## Environment diff --git a/src/BenchmarksServer/Microsoft.Diagnostics.Tools.RuntimeClient/Eventing/SessionConfiguration.cs b/src/BenchmarksServer/Microsoft.Diagnostics.Tools.RuntimeClient/Eventing/SessionConfiguration.cs index da1ccd0bb..2bcfae531 100644 --- a/src/BenchmarksServer/Microsoft.Diagnostics.Tools.RuntimeClient/Eventing/SessionConfiguration.cs +++ b/src/BenchmarksServer/Microsoft.Diagnostics.Tools.RuntimeClient/Eventing/SessionConfiguration.cs @@ -25,7 +25,7 @@ public SessionConfiguration(uint circularBufferSizeMB, EventPipeSerializationFor throw new ArgumentException("Unrecognized format"); if (providers == null) throw new ArgumentNullException(nameof(providers)); - if (providers.Count() <= 0) + if (providers.Count <= 0) throw new ArgumentException($"Specified providers collection is empty."); CircularBufferSizeInMB = circularBufferSizeMB; @@ -37,7 +37,6 @@ public SessionConfiguration(uint circularBufferSizeMB, EventPipeSerializationFor public uint CircularBufferSizeInMB { get; } public EventPipeSerializationFormat Format { get; } - public IReadOnlyCollection Providers => _providers.AsReadOnly(); private readonly List _providers; diff --git a/src/BenchmarksServer/Startup.cs b/src/BenchmarksServer/Startup.cs index c7baad40e..bf7e90a6c 100644 --- a/src/BenchmarksServer/Startup.cs +++ b/src/BenchmarksServer/Startup.cs @@ -3431,41 +3431,78 @@ private static string GetCGroupController(ServerJob job) return $"benchmarks-{Process.GetCurrentProcess().Id}-{job.Id}"; } + private static readonly MeasurementMetadata[] MeasurementMetadatas = new[] + { + // System.Runtime + new MeasurementMetadata { Source = "Counters/System.Runtime", Name = "Counters/System.Runtime/cpu-usage", LongDescription = "Amount of time the process has utilized the CPU (ms)", ShortDescription = "CPU Usage (%)", Format = "n0", Aggregate = Operation.Max, Reduce = Operation.Max }, + new MeasurementMetadata { Source = "Counters/System.Runtime", Name = "Counters/System.Runtime/working-set", LongDescription = "Amount of working set used by the process (MB)", ShortDescription = "Working Set (MB)", Format = "n0", Aggregate = Operation.Max, Reduce = Operation.Max }, + new MeasurementMetadata { Source = "Counters/System.Runtime", Name = "Counters/System.Runtime/gc-heap-size", LongDescription = "Total heap size reported by the GC (MB)", ShortDescription = "GC Heap Size (MB)", Format = "n0", Aggregate = Operation.Median, Reduce = Operation.Max }, + new MeasurementMetadata { Source = "Counters/System.Runtime", Name = "Counters/System.Runtime/gen-0-gc-count", LongDescription = "Number of Gen 0 GCs / sec", ShortDescription = "Gen 0 GC (#/s)", Format = "n0", Aggregate = Operation.Avg, Reduce = Operation.Max }, + new MeasurementMetadata { Source = "Counters/System.Runtime", Name = "Counters/System.Runtime/gen-1-gc-count", LongDescription = "Number of Gen 1 GCs / sec", ShortDescription = "Gen 1 GC (#/s)", Format = "n0", Aggregate = Operation.Avg, Reduce = Operation.Max }, + new MeasurementMetadata { Source = "Counters/System.Runtime", Name = "Counters/System.Runtime/gen-2-gc-count", LongDescription = "Number of Gen 2 GCs / sec", ShortDescription = "Gen 2 GC (#/s)", Format = "n0", Aggregate = Operation.Avg, Reduce = Operation.Max }, + new MeasurementMetadata { Source = "Counters/System.Runtime", Name = "Counters/System.Runtime/time-in-gc", LongDescription = "% time in GC since the last GC", ShortDescription = "Time in GC (%)", Format = "n0", Aggregate = Operation.Avg, Reduce = Operation.Max }, + new MeasurementMetadata { Source = "Counters/System.Runtime", Name = "Counters/System.Runtime/gen-0-size", LongDescription = "Gen 0 Heap Size", ShortDescription = "Gen 0 Size (B)", Format = "n0", Aggregate = Operation.Median, Reduce = Operation.Max }, + new MeasurementMetadata { Source = "Counters/System.Runtime", Name = "Counters/System.Runtime/gen-1-size", LongDescription = "Gen 1 Heap Size", ShortDescription = "Gen 1 Size (B)", Format = "n0", Aggregate = Operation.Median, Reduce = Operation.Max }, + new MeasurementMetadata { Source = "Counters/System.Runtime", Name = "Counters/System.Runtime/gen-2-size", LongDescription = "Gen 2 Heap Size", ShortDescription = "Gen 2 Size (B)", Format = "n0", Aggregate = Operation.Median, Reduce = Operation.Max }, + new MeasurementMetadata { Source = "Counters/System.Runtime", Name = "Counters/System.Runtime/loh-size", LongDescription = "LOH Heap Size", ShortDescription = "LOH Size (B)", Format = "n0", Aggregate = Operation.Median, Reduce = Operation.Max }, + new MeasurementMetadata { Source = "Counters/System.Runtime", Name = "Counters/System.Runtime/alloc-rate", LongDescription = "Allocation Rate", ShortDescription = "Allocation Rate (B/sec)", Format = "n0", Aggregate = Operation.Avg, Reduce = Operation.Max }, + new MeasurementMetadata { Source = "Counters/System.Runtime", Name = "Counters/System.Runtime/assembly-count", LongDescription = "Number of Assemblies Loaded", ShortDescription = "# of Assemblies Loaded", Format = "n0", Aggregate = Operation.Max, Reduce = Operation.Max }, + new MeasurementMetadata { Source = "Counters/System.Runtime", Name = "Counters/System.Runtime/exception-count", LongDescription = "Number of Exceptions / sec", ShortDescription = "Exceptions (#/s)", Format = "n0", Aggregate = Operation.Avg, Reduce = Operation.Max }, + new MeasurementMetadata { Source = "Counters/System.Runtime", Name = "Counters/System.Runtime/threadpool-thread-count", LongDescription = "Number of ThreadPool Threads", ShortDescription = "ThreadPool Threads Count", Format = "n0", Aggregate = Operation.Median, Reduce = Operation.Max }, + new MeasurementMetadata { Source = "Counters/System.Runtime", Name = "Counters/System.Runtime/monitor-lock-contention-count", LongDescription = "Monitor Lock Contention Count", ShortDescription = "Lock Contention (#/s)", Format = "n0", Aggregate = Operation.Avg, Reduce = Operation.Max }, + new MeasurementMetadata { Source = "Counters/System.Runtime", Name = "Counters/System.Runtime/threadpool-queue-length", LongDescription = "ThreadPool Work Items Queue Length", ShortDescription = "ThreadPool Queue Length", Format = "n0", Aggregate = Operation.Median, Reduce = Operation.Max }, + new MeasurementMetadata { Source = "Counters/System.Runtime", Name = "Counters/System.Runtime/threadpool-completed-items-count", LongDescription = "ThreadPool Completed Work Items Count", ShortDescription = "ThreadPool Items (#/s)", Format = "n0", Aggregate = Operation.Avg, Reduce = Operation.Max }, + + // Kestrel + new MeasurementMetadata { Source = "Counters/Microsoft-AspNetCore-Server-Kestrel", Name = "Counters/Microsoft-AspNetCore-Server-Kestrel/connections-per-second", LongDescription = "Connection Rate", ShortDescription = "Connection Rate", Format = "n0", Aggregate = Operation.Avg, Reduce = Operation.Max }, + new MeasurementMetadata { Source = "Counters/Microsoft-AspNetCore-Server-Kestrel", Name = "Counters/Microsoft-AspNetCore-Server-Kestrel/total-connections", LongDescription = "Total Connections", ShortDescription = "Total Connections", Format = "n0", Aggregate = Operation.Max, Reduce = Operation.Max }, + new MeasurementMetadata { Source = "Counters/Microsoft-AspNetCore-Server-Kestrel", Name = "Counters/Microsoft-AspNetCore-Server-Kestrel/tls-handshakes-per-second", LongDescription = "TLS Handshake Rate", ShortDescription = "TLS Handshake Rate", Format = "n0", Aggregate = Operation.Avg, Reduce = Operation.Max }, + new MeasurementMetadata { Source = "Counters/Microsoft-AspNetCore-Server-Kestrel", Name = "Counters/Microsoft-AspNetCore-Server-Kestrel/total-tls-handshakes", LongDescription = "Total TLS Handshakes", ShortDescription = "Total TLS Handshakes", Format = "n0", Aggregate = Operation.Max, Reduce = Operation.Max }, + new MeasurementMetadata { Source = "Counters/Microsoft-AspNetCore-Server-Kestrel", Name = "Counters/Microsoft-AspNetCore-Server-Kestrel/current-tls-handshakes", LongDescription = "Current TLS Handshakes", ShortDescription = "Current TLS Handshakes", Format = "n0", Aggregate = Operation.Avg, Reduce = Operation.Max }, + new MeasurementMetadata { Source = "Counters/Microsoft-AspNetCore-Server-Kestrel", Name = "Counters/Microsoft-AspNetCore-Server-Kestrel/failed-tls-handshakes", LongDescription = "Failed TLS Handshakes", ShortDescription = "Failed TLS Handshakes", Format = "n0", Aggregate = Operation.Max, Reduce = Operation.Max }, + new MeasurementMetadata { Source = "Counters/Microsoft-AspNetCore-Server-Kestrel", Name = "Counters/Microsoft-AspNetCore-Server-Kestrel/current-connections", LongDescription = "Current Connections", ShortDescription = "Current Connections", Format = "n0", Aggregate = Operation.Avg, Reduce = Operation.Max }, + new MeasurementMetadata { Source = "Counters/Microsoft-AspNetCore-Server-Kestrel", Name = "Counters/Microsoft-AspNetCore-Server-Kestrel/connection-queue-length", LongDescription = "Connection Queue Length", ShortDescription = "Connection Queue Length", Format = "n0", Aggregate = Operation.Avg, Reduce = Operation.Max }, + new MeasurementMetadata { Source = "Counters/Microsoft-AspNetCore-Server-Kestrel", Name = "Counters/Microsoft-AspNetCore-Server-Kestrel/request-queue-length", LongDescription = "Request Queue Length", ShortDescription = "Request Queue Length", Format = "n0", Aggregate = Operation.Avg, Reduce = Operation.Max }, + new MeasurementMetadata { Source = "Counters/Microsoft-AspNetCore-Server-Kestrel", Name = "Counters/Microsoft-AspNetCore-Server-Kestrel/current-upgraded-requests", LongDescription = "Current Upgraded Requests (WebSockets)", ShortDescription = "Current Upgraded Requests (WebSockets)", Format = "n0", Aggregate = Operation.Avg, Reduce = Operation.Max }, + + // Npgsql + new MeasurementMetadata { Source = "Counters/Npgsql", Name = "Counters/Npgsql/bytes-written-per-second", LongDescription = "Bytes Written", ShortDescription = "Bytes Written", Format = "n0", Aggregate = Operation.Avg, Reduce = Operation.Max }, + new MeasurementMetadata { Source = "Counters/Npgsql", Name = "Counters/Npgsql/bytes-read-per-second", LongDescription = "Bytes Read", ShortDescription = "Bytes Read", Format = "n0", Aggregate = Operation.Avg, Reduce = Operation.Max }, + new MeasurementMetadata { Source = "Counters/Npgsql", Name = "Counters/Npgsql/commands-per-second", LongDescription = "Command Rate", ShortDescription = "Command Rate", Format = "n0", Aggregate = Operation.Avg, Reduce = Operation.Max }, + new MeasurementMetadata { Source = "Counters/Npgsql", Name = "Counters/Npgsql/total-commands", LongDescription = "Total Commands", ShortDescription = "Total Commands", Format = "n0", Aggregate = Operation.Max, Reduce = Operation.Max }, + new MeasurementMetadata { Source = "Counters/Npgsql", Name = "Counters/Npgsql/current-commands", LongDescription = "Current Commands", ShortDescription = "Current Commands", Format = "n0", Aggregate = Operation.Avg, Reduce = Operation.Max }, + new MeasurementMetadata { Source = "Counters/Npgsql", Name = "Counters/Npgsql/failed-commands", LongDescription = "Failed Commands", ShortDescription = "Failed Commands", Format = "n0", Aggregate = Operation.Max, Reduce = Operation.Max }, + new MeasurementMetadata { Source = "Counters/Npgsql", Name = "Counters/Npgsql/prepared-commands-ratio", LongDescription = "Prepared Commands Ratio", ShortDescription = "Prepared Commands Ratio", Format = "n0", Aggregate = Operation.Avg, Reduce = Operation.Max }, + new MeasurementMetadata { Source = "Counters/Npgsql", Name = "Counters/Npgsql/connection-pools", LongDescription = "Connection Pools", ShortDescription = "Connection Pools", Format = "n0", Aggregate = Operation.Max, Reduce = Operation.Max }, + new MeasurementMetadata { Source = "Counters/Npgsql", Name = "Counters/Npgsql/idle-connections", LongDescription = "Idle Connections", ShortDescription = "Idle Connections", Format = "n0", Aggregate = Operation.Avg, Reduce = Operation.Max }, + new MeasurementMetadata { Source = "Counters/Npgsql", Name = "Counters/Npgsql/busy-connections", LongDescription = "Busy Connections", ShortDescription = "Busy Connections", Format = "n0", Aggregate = Operation.Avg, Reduce = Operation.Max }, + new MeasurementMetadata { Source = "Counters/Npgsql", Name = "Counters/Npgsql/multiplexing-average-commands-per-batch", LongDescription = "Average commands per multiplexing batch", ShortDescription = "Commands per multiplexing batch", Format = "n0", Aggregate = Operation.Avg, Reduce = Operation.Max }, + new MeasurementMetadata { Source = "Counters/Npgsql", Name = "Counters/Npgsql/multiplexing-average-waits-per-batch", LongDescription = "Average waits per multiplexing batch", ShortDescription = "Waits per multiplexing batch", Format = "n0", Aggregate = Operation.Avg, Reduce = Operation.Max }, + new MeasurementMetadata { Source = "Counters/Npgsql", Name = "Counters/Npgsql/multiplexing-average-write-time-per-batch", LongDescription = "Average write time per multiplexing batch (us)", ShortDescription = "Time per multiplexing batch (us)", Format = "n0", Aggregate = Operation.Avg, Reduce = Operation.Max }, + }; + private static void StartCounters(ServerJob job) { - job.Metadata.Enqueue(new MeasurementMetadata { Source = "Counters", Name = "runtime-counter/cpu-usage", LongDescription = "Amount of time the process has utilized the CPU (ms)", ShortDescription = "CPU Usage (%)", Format = "n0", Aggregate = Operation.Max, Reduce = Operation.Max }); - job.Metadata.Enqueue(new MeasurementMetadata { Source = "Counters", Name = "runtime-counter/working-set", LongDescription = "Amount of working set used by the process (MB)", ShortDescription = "Working Set (MB)", Format = "n0", Aggregate = Operation.Max, Reduce = Operation.Max }); - job.Metadata.Enqueue(new MeasurementMetadata { Source = "Counters", Name = "runtime-counter/gc-heap-size", LongDescription = "Total heap size reported by the GC (MB)", ShortDescription = "GC Heap Size (MB)", Format = "n0", Aggregate = Operation.Median, Reduce = Operation.Max }); - job.Metadata.Enqueue(new MeasurementMetadata { Source = "Counters", Name = "runtime-counter/gen-0-gc-count", LongDescription = "Number of Gen 0 GCs / sec", ShortDescription = "Gen 0 GC (#/s)", Format = "n0", Aggregate = Operation.Avg, Reduce = Operation.Max }); - job.Metadata.Enqueue(new MeasurementMetadata { Source = "Counters", Name = "runtime-counter/gen-1-gc-count", LongDescription = "Number of Gen 1 GCs / sec", ShortDescription = "Gen 1 GC (#/s)", Format = "n0", Aggregate = Operation.Avg, Reduce = Operation.Max }); - job.Metadata.Enqueue(new MeasurementMetadata { Source = "Counters", Name = "runtime-counter/gen-2-gc-count", LongDescription = "Number of Gen 2 GCs / sec", ShortDescription = "Gen 2 GC (#/s)", Format = "n0", Aggregate = Operation.Avg, Reduce = Operation.Max }); - job.Metadata.Enqueue(new MeasurementMetadata { Source = "Counters", Name = "runtime-counter/time-in-gc", LongDescription = "% time in GC since the last GC", ShortDescription = "Time in GC (%)", Format = "n0", Aggregate = Operation.Avg, Reduce = Operation.Max }); - job.Metadata.Enqueue(new MeasurementMetadata { Source = "Counters", Name = "runtime-counter/gen-0-size", LongDescription = "Gen 0 Heap Size", ShortDescription = "Gen 0 Size (B)", Format = "n0", Aggregate = Operation.Median, Reduce = Operation.Max }); - job.Metadata.Enqueue(new MeasurementMetadata { Source = "Counters", Name = "runtime-counter/gen-1-size", LongDescription = "Gen 1 Heap Size", ShortDescription = "Gen 1 Size (B)", Format = "n0", Aggregate = Operation.Median, Reduce = Operation.Max }); - job.Metadata.Enqueue(new MeasurementMetadata { Source = "Counters", Name = "runtime-counter/gen-2-size", LongDescription = "Gen 2 Heap Size", ShortDescription = "Gen 2 Size (B)", Format = "n0", Aggregate = Operation.Median, Reduce = Operation.Max }); - job.Metadata.Enqueue(new MeasurementMetadata { Source = "Counters", Name = "runtime-counter/loh-size", LongDescription = "LOH Heap Size", ShortDescription = "LOH Size (B)", Format = "n0", Aggregate = Operation.Median, Reduce = Operation.Max }); - job.Metadata.Enqueue(new MeasurementMetadata { Source = "Counters", Name = "runtime-counter/alloc-rate", LongDescription = "Allocation Rate", ShortDescription = "Allocation Rate (B/sec)", Format = "n0", Aggregate = Operation.Avg, Reduce = Operation.Max }); - job.Metadata.Enqueue(new MeasurementMetadata { Source = "Counters", Name = "runtime-counter/assembly-count", LongDescription = "Number of Assemblies Loaded", ShortDescription = "# of Assemblies Loaded", Format = "n0", Aggregate = Operation.Max, Reduce = Operation.Max }); - job.Metadata.Enqueue(new MeasurementMetadata { Source = "Counters", Name = "runtime-counter/exception-count", LongDescription = "Number of Exceptions / sec", ShortDescription = "Exceptions (#/s)", Format = "n0", Aggregate = Operation.Avg, Reduce = Operation.Max }); - job.Metadata.Enqueue(new MeasurementMetadata { Source = "Counters", Name = "runtime-counter/threadpool-thread-count", LongDescription = "Number of ThreadPool Threads", ShortDescription = "ThreadPool Threads Count", Format = "n0", Aggregate = Operation.Median, Reduce = Operation.Max }); - job.Metadata.Enqueue(new MeasurementMetadata { Source = "Counters", Name = "runtime-counter/monitor-lock-contention-count", LongDescription = "Monitor Lock Contention Count", ShortDescription = "Lock Contention (#/s)", Format = "n0", Aggregate = Operation.Avg, Reduce = Operation.Max }); - job.Metadata.Enqueue(new MeasurementMetadata { Source = "Counters", Name = "runtime-counter/threadpool-queue-length", LongDescription = "ThreadPool Work Items Queue Length", ShortDescription = "ThreadPool Queue Length", Format = "n0", Aggregate = Operation.Median, Reduce = Operation.Max }); - job.Metadata.Enqueue(new MeasurementMetadata { Source = "Counters", Name = "runtime-counter/threadpool-completed-items-count", LongDescription = "ThreadPool Completed Work Items Count", ShortDescription = "ThreadPool Items (#/s)", Format = "n0", Aggregate = Operation.Avg, Reduce = Operation.Max }); + foreach (var measurementMetadata in MeasurementMetadatas) + job.Metadata.Enqueue(measurementMetadata); eventPipeTerminated = false; eventPipeTask = new Task(() => { - Log.WriteLine("Listening to event pipes"); + var providerNames = job.CounterProviders.Count > 0 + ? job.CounterProviders + : new List { "System.Runtime" }; + + Log.WriteLine($"Listening to counter event pipes (providers: {string.Join(", ", providerNames)})"); try { - var providerList = new List() - { - new Provider( - name: "System.Runtime", - eventLevel: EventLevel.Informational, - filterData: "EventCounterIntervalSec=1"), - }; + var providerList = providerNames + .Select(p => new Provider( + name: p, + eventLevel: EventLevel.Informational, + filterData: "EventCounterIntervalSec=1")) + .ToList(); var configuration = new SessionConfiguration( circularBufferSizeMB: 1000, @@ -3499,7 +3536,7 @@ private static void StartCounters(ServerJob job) } } - measurement.Name = "runtime-counter/" + counterName; + measurement.Name = $"Counters/{eventData.ProviderName}/{counterName}"; switch (payloadFields["CounterType"]) {