From 4e16fae986e85efc23b6e9ad42cee0b610949fa8 Mon Sep 17 00:00:00 2001 From: evgenyfedorov2 Date: Mon, 30 Jun 2025 10:01:55 +0200 Subject: [PATCH 1/6] Fix tests Update CompatibilitySuppressions Remove unnecessary logging and metrics Rename CalculateCpuUsageWithoutHostDelta to UseLinuxCpuUsageV2 Remove UseDeltaNrPeriosForCpuCalculaton --- .../CompatibilitySuppressions.xml | 86 ++++++++++ .../Linux/LinuxUtilizationProvider.cs | 153 ++++++------------ .../Linux/Log.cs | 14 +- .../ResourceMonitoringOptions.cs | 18 +-- .../ResourceUtilizationInstruments.cs | 16 -- .../Linux/AcceptanceTest.cs | 79 +-------- .../Linux/LinuxUtilizationProviderTests.cs | 2 +- .../ResourceMonitoringOptionsTests.cs | 6 +- 8 files changed, 145 insertions(+), 229 deletions(-) diff --git a/src/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring/CompatibilitySuppressions.xml b/src/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring/CompatibilitySuppressions.xml index 6526176c304..1b26cb7a9d6 100644 --- a/src/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring/CompatibilitySuppressions.xml +++ b/src/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring/CompatibilitySuppressions.xml @@ -43,4 +43,90 @@ lib/net9.0/Microsoft.Extensions.Diagnostics.ResourceMonitoring.dll true + + + CP0002 + M:Microsoft.Extensions.Diagnostics.ResourceMonitoring.ResourceMonitoringOptions.get_UseDeltaNrPeriodsForCpuCalculation + lib/net462/Microsoft.Extensions.Diagnostics.ResourceMonitoring.dll + lib/net462/Microsoft.Extensions.Diagnostics.ResourceMonitoring.dll + true + + + CP0002 + M:Microsoft.Extensions.Diagnostics.ResourceMonitoring.ResourceMonitoringOptions.set_UseDeltaNrPeriodsForCpuCalculation(System.Boolean) + lib/net462/Microsoft.Extensions.Diagnostics.ResourceMonitoring.dll + lib/net462/Microsoft.Extensions.Diagnostics.ResourceMonitoring.dll + true + + + CP0002 + M:Microsoft.Extensions.Diagnostics.ResourceMonitoring.ResourceMonitoringOptions.get_UseDeltaNrPeriodsForCpuCalculation + lib/net8.0/Microsoft.Extensions.Diagnostics.ResourceMonitoring.dll + lib/net8.0/Microsoft.Extensions.Diagnostics.ResourceMonitoring.dll + true + + + CP0002 + M:Microsoft.Extensions.Diagnostics.ResourceMonitoring.ResourceMonitoringOptions.set_UseDeltaNrPeriodsForCpuCalculation(System.Boolean) + lib/net8.0/Microsoft.Extensions.Diagnostics.ResourceMonitoring.dll + lib/net8.0/Microsoft.Extensions.Diagnostics.ResourceMonitoring.dll + true + + + CP0002 + M:Microsoft.Extensions.Diagnostics.ResourceMonitoring.ResourceMonitoringOptions.get_UseDeltaNrPeriodsForCpuCalculation + lib/net9.0/Microsoft.Extensions.Diagnostics.ResourceMonitoring.dll + lib/net9.0/Microsoft.Extensions.Diagnostics.ResourceMonitoring.dll + true + + + CP0002 + M:Microsoft.Extensions.Diagnostics.ResourceMonitoring.ResourceMonitoringOptions.set_UseDeltaNrPeriodsForCpuCalculation(System.Boolean) + lib/net9.0/Microsoft.Extensions.Diagnostics.ResourceMonitoring.dll + lib/net9.0/Microsoft.Extensions.Diagnostics.ResourceMonitoring.dll + true + + + + CP0002 + M:Microsoft.Extensions.Diagnostics.ResourceMonitoring.ResourceMonitoringOptions.get_CalculateCpuUsageWithoutHostDelta + lib/net462/Microsoft.Extensions.Diagnostics.ResourceMonitoring.dll + lib/net462/Microsoft.Extensions.Diagnostics.ResourceMonitoring.dll + true + + + CP0002 + M:Microsoft.Extensions.Diagnostics.ResourceMonitoring.ResourceMonitoringOptions.set_CalculateCpuUsageWithoutHostDelta(System.Boolean) + lib/net462/Microsoft.Extensions.Diagnostics.ResourceMonitoring.dll + lib/net462/Microsoft.Extensions.Diagnostics.ResourceMonitoring.dll + true + + + CP0002 + M:Microsoft.Extensions.Diagnostics.ResourceMonitoring.ResourceMonitoringOptions.get_CalculateCpuUsageWithoutHostDelta + lib/net8.0/Microsoft.Extensions.Diagnostics.ResourceMonitoring.dll + lib/net8.0/Microsoft.Extensions.Diagnostics.ResourceMonitoring.dll + true + + + CP0002 + M:Microsoft.Extensions.Diagnostics.ResourceMonitoring.ResourceMonitoringOptions.set_CalculateCpuUsageWithoutHostDelta(System.Boolean) + lib/net8.0/Microsoft.Extensions.Diagnostics.ResourceMonitoring.dll + lib/net8.0/Microsoft.Extensions.Diagnostics.ResourceMonitoring.dll + true + + + CP0002 + M:Microsoft.Extensions.Diagnostics.ResourceMonitoring.ResourceMonitoringOptions.get_CalculateCpuUsageWithoutHostDelta + lib/net9.0/Microsoft.Extensions.Diagnostics.ResourceMonitoring.dll + lib/net9.0/Microsoft.Extensions.Diagnostics.ResourceMonitoring.dll + true + + + CP0002 + M:Microsoft.Extensions.Diagnostics.ResourceMonitoring.ResourceMonitoringOptions.set_CalculateCpuUsageWithoutHostDelta(System.Boolean) + lib/net9.0/Microsoft.Extensions.Diagnostics.ResourceMonitoring.dll + lib/net9.0/Microsoft.Extensions.Diagnostics.ResourceMonitoring.dll + true + \ No newline at end of file diff --git a/src/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring/Linux/LinuxUtilizationProvider.cs b/src/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring/Linux/LinuxUtilizationProvider.cs index 4090bbb5619..db2981c3a33 100644 --- a/src/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring/Linux/LinuxUtilizationProvider.cs +++ b/src/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring/Linux/LinuxUtilizationProvider.cs @@ -14,39 +14,25 @@ internal sealed class LinuxUtilizationProvider : ISnapshotProvider { private const double One = 1.0; private const long Hundred = 100L; - private const double CpuLimitThreshold110Percent = 1.1; - // Meters to track CPU utilization threshold exceedances - private readonly Counter? _cpuUtilizationLimit100PercentExceededCounter; - private readonly Counter? _cpuUtilizationLimit110PercentExceededCounter; - - private readonly bool _useDeltaNrPeriods; private readonly object _cpuLocker = new(); private readonly object _memoryLocker = new(); private readonly ILogger _logger; private readonly ILinuxUtilizationParser _parser; private readonly ulong _memoryLimit; + private readonly long _cpuPeriodsInterval; private readonly TimeSpan _cpuRefreshInterval; private readonly TimeSpan _memoryRefreshInterval; private readonly TimeProvider _timeProvider; - private readonly double _scaleRelativeToCpuLimit; - private readonly double _scaleRelativeToCpuRequest; private readonly double _scaleRelativeToCpuRequestForTrackerApi; private DateTimeOffset _refreshAfterCpu; private DateTimeOffset _refreshAfterMemory; - - // Track the actual timestamp when we read CPU values - private DateTimeOffset _lastCpuMeasurementTime; - private double _cpuPercentage = double.NaN; private double _lastCpuCoresUsed = double.NaN; private double _memoryPercentage; private long _previousCgroupCpuTime; private long _previousHostCpuTime; - private long _cpuUtilizationLimit100PercentExceeded; - private long _cpuUtilizationLimit110PercentExceeded; - private long _cpuPeriodsInterval; private long _previousCgroupCpuPeriodCounter; public SystemResources Resources { get; } @@ -59,7 +45,6 @@ public LinuxUtilizationProvider(IOptions options, ILi DateTimeOffset now = _timeProvider.GetUtcNow(); _cpuRefreshInterval = options.Value.CpuConsumptionRefreshInterval; _memoryRefreshInterval = options.Value.MemoryConsumptionRefreshInterval; - _useDeltaNrPeriods = options.Value.UseDeltaNrPeriodsForCpuCalculation; _refreshAfterCpu = now; _refreshAfterMemory = now; _memoryLimit = _parser.GetAvailableMemoryInBytes(); @@ -69,8 +54,8 @@ public LinuxUtilizationProvider(IOptions options, ILi float hostCpus = _parser.GetHostCpuCount(); float cpuLimit = _parser.GetCgroupLimitedCpus(); float cpuRequest = _parser.GetCgroupRequestCpu(); - _scaleRelativeToCpuLimit = hostCpus / cpuLimit; - _scaleRelativeToCpuRequest = hostCpus / cpuRequest; + float scaleRelativeToCpuLimit = hostCpus / cpuLimit; + float scaleRelativeToCpuRequest = hostCpus / cpuRequest; _scaleRelativeToCpuRequestForTrackerApi = hostCpus; // the division by cpuRequest is performed later on in the ResourceUtilization class #pragma warning disable CA2000 // Dispose objects before losing scope @@ -80,28 +65,23 @@ public LinuxUtilizationProvider(IOptions options, ILi var meter = meterFactory.Create(ResourceUtilizationInstruments.MeterName); #pragma warning restore CA2000 // Dispose objects before losing scope - if (options.Value.CalculateCpuUsageWithoutHostDelta) + if (options.Value.UseLinuxCpuUsageV2) { cpuLimit = _parser.GetCgroupLimitV2(); - - // Try to get the CPU request from cgroup cpuRequest = _parser.GetCgroupRequestCpuV2(); // Get Cpu periods interval from cgroup _cpuPeriodsInterval = _parser.GetCgroupPeriodsIntervalInMicroSecondsV2(); (_previousCgroupCpuTime, _previousCgroupCpuPeriodCounter) = _parser.GetCgroupCpuUsageInNanosecondsAndCpuPeriodsV2(); - // Initialize the counters - _cpuUtilizationLimit100PercentExceededCounter = meter.CreateCounter("cpu_utilization_limit_100_percent_exceeded"); - _cpuUtilizationLimit110PercentExceededCounter = meter.CreateCounter("cpu_utilization_limit_110_percent_exceeded"); _ = meter.CreateObservableGauge(name: ResourceUtilizationInstruments.ContainerCpuLimitUtilization, observeValue: () => CpuUtilizationLimit(cpuLimit), unit: "1"); - _ = meter.CreateObservableGauge(name: ResourceUtilizationInstruments.ContainerCpuRequestUtilization, observeValue: () => CpuUtilizationWithoutHostDelta() / cpuRequest, unit: "1"); + _ = meter.CreateObservableGauge(name: ResourceUtilizationInstruments.ContainerCpuRequestUtilization, observeValue: () => CpuUtilizationRequest(cpuRequest), unit: "1"); } else { - _ = meter.CreateObservableGauge(name: ResourceUtilizationInstruments.ContainerCpuLimitUtilization, observeValue: () => CpuUtilization() * _scaleRelativeToCpuLimit, unit: "1"); - _ = meter.CreateObservableGauge(name: ResourceUtilizationInstruments.ContainerCpuRequestUtilization, observeValue: () => CpuUtilization() * _scaleRelativeToCpuRequest, unit: "1"); - _ = meter.CreateObservableGauge(name: ResourceUtilizationInstruments.ProcessCpuUtilization, observeValue: () => CpuUtilization() * _scaleRelativeToCpuRequest, unit: "1"); + _ = meter.CreateObservableGauge(name: ResourceUtilizationInstruments.ContainerCpuLimitUtilization, observeValue: () => CpuUtilization() * scaleRelativeToCpuLimit, unit: "1"); + _ = meter.CreateObservableGauge(name: ResourceUtilizationInstruments.ContainerCpuRequestUtilization, observeValue: () => CpuUtilization() * scaleRelativeToCpuRequest, unit: "1"); + _ = meter.CreateObservableGauge(name: ResourceUtilizationInstruments.ProcessCpuUtilization, observeValue: () => CpuUtilization() * scaleRelativeToCpuRequest, unit: "1"); } _ = meter.CreateObservableGauge(name: ResourceUtilizationInstruments.ContainerMemoryLimitUtilization, observeValue: MemoryUtilization, unit: "1"); @@ -115,10 +95,9 @@ public LinuxUtilizationProvider(IOptions options, ILi _logger.SystemResourcesInfo(cpuLimit, cpuRequest, _memoryLimit, _memoryLimit); } - public double CpuUtilizationWithoutHostDelta() + public double CpuUtilizationV2() { DateTimeOffset now = _timeProvider.GetUtcNow(); - double actualElapsedNanoseconds = (now - _lastCpuMeasurementTime).TotalNanoseconds; lock (_cpuLocker) { if (now < _refreshAfterCpu) @@ -127,79 +106,34 @@ public double CpuUtilizationWithoutHostDelta() } } - var (cpuUsageTime, cpuPeriodCounter) = _parser.GetCgroupCpuUsageInNanosecondsAndCpuPeriodsV2(); + (long cpuUsageTime, long cpuPeriodCounter) = _parser.GetCgroupCpuUsageInNanosecondsAndCpuPeriodsV2(); lock (_cpuLocker) { - if (now >= _refreshAfterCpu) + if (now < _refreshAfterCpu) { - long deltaCgroup = cpuUsageTime - _previousCgroupCpuTime; - double coresUsed; - - if (_useDeltaNrPeriods) - { - long deltaPeriodCount = cpuPeriodCounter - _previousCgroupCpuPeriodCounter; - long deltaCpuPeriodInNanoseconds = deltaPeriodCount * _cpuPeriodsInterval * 1000; - - if (deltaCgroup > 0 && deltaPeriodCount > 0) - { - coresUsed = deltaCgroup / (double)deltaCpuPeriodInNanoseconds; - - _logger.CpuUsageDataV2(cpuUsageTime, _previousCgroupCpuTime, deltaCpuPeriodInNanoseconds, coresUsed); - - _lastCpuCoresUsed = coresUsed; - _refreshAfterCpu = now.Add(_cpuRefreshInterval); - _previousCgroupCpuTime = cpuUsageTime; - _previousCgroupCpuPeriodCounter = cpuPeriodCounter; - } - } - else - { - if (deltaCgroup > 0) - { - coresUsed = deltaCgroup / actualElapsedNanoseconds; - - _logger.CpuUsageDataV2(cpuUsageTime, _previousCgroupCpuTime, actualElapsedNanoseconds, coresUsed); - - _lastCpuCoresUsed = coresUsed; - _refreshAfterCpu = now.Add(_cpuRefreshInterval); - _previousCgroupCpuTime = cpuUsageTime; - - // Update the timestamp for next calculation - _lastCpuMeasurementTime = now; - } - } + return _lastCpuCoresUsed; } - } - return _lastCpuCoresUsed; - } + long deltaCgroup = cpuUsageTime - _previousCgroupCpuTime; + long deltaPeriodCount = cpuPeriodCounter - _previousCgroupCpuPeriodCounter; - /// - /// Calculates CPU utilization relative to the CPU limit. - /// - /// The CPU limit to use for the calculation. - /// CPU usage as a ratio of the limit. - public double CpuUtilizationLimit(float cpuLimit) - { - double utilization = CpuUtilizationWithoutHostDelta() / cpuLimit; + if (deltaCgroup <= 0 || deltaPeriodCount <= 0) + { + return _lastCpuCoresUsed; + } - // Increment counter if utilization exceeds 1 (100%) - if (utilization > 1.0) - { - _cpuUtilizationLimit100PercentExceededCounter?.Add(1); - _cpuUtilizationLimit100PercentExceeded++; - _logger.CounterMessage100(_cpuUtilizationLimit100PercentExceeded); - } + long deltaCpuPeriodInNanoseconds = deltaPeriodCount * _cpuPeriodsInterval * 1000; + double coresUsed = deltaCgroup / (double)deltaCpuPeriodInNanoseconds; - // Increment counter if utilization exceeds 110% - if (utilization > CpuLimitThreshold110Percent) - { - _cpuUtilizationLimit110PercentExceededCounter?.Add(1); - _cpuUtilizationLimit110PercentExceeded++; - _logger.CounterMessage110(_cpuUtilizationLimit110PercentExceeded); + _logger.CpuUsageDataV2(cpuUsageTime, _previousCgroupCpuTime, deltaCpuPeriodInNanoseconds, coresUsed); + + _lastCpuCoresUsed = coresUsed; + _refreshAfterCpu = now.Add(_cpuRefreshInterval); + _previousCgroupCpuTime = cpuUsageTime; + _previousCgroupCpuPeriodCounter = cpuPeriodCounter; } - return utilization; + return _lastCpuCoresUsed; } public double CpuUtilization() @@ -219,23 +153,27 @@ public double CpuUtilization() lock (_cpuLocker) { - if (now >= _refreshAfterCpu) + if (now < _refreshAfterCpu) { - long deltaHost = hostCpuTime - _previousHostCpuTime; - long deltaCgroup = cgroupCpuTime - _previousCgroupCpuTime; - - if (deltaHost > 0 && deltaCgroup > 0) - { - double percentage = Math.Min(One, (double)deltaCgroup / deltaHost); + return _cpuPercentage; + } - _logger.CpuUsageData(cgroupCpuTime, hostCpuTime, _previousCgroupCpuTime, _previousHostCpuTime, percentage); + long deltaHost = hostCpuTime - _previousHostCpuTime; + long deltaCgroup = cgroupCpuTime - _previousCgroupCpuTime; - _cpuPercentage = percentage; - _refreshAfterCpu = now.Add(_cpuRefreshInterval); - _previousCgroupCpuTime = cgroupCpuTime; - _previousHostCpuTime = hostCpuTime; - } + if (deltaHost <= 0 || deltaCgroup <= 0) + { + return _cpuPercentage; } + + double percentage = Math.Min(One, (double)deltaCgroup / deltaHost); + + _logger.CpuUsageData(cgroupCpuTime, hostCpuTime, _previousCgroupCpuTime, _previousHostCpuTime, percentage); + + _cpuPercentage = percentage; + _refreshAfterCpu = now.Add(_cpuRefreshInterval); + _previousCgroupCpuTime = cgroupCpuTime; + _previousHostCpuTime = hostCpuTime; } return _cpuPercentage; @@ -288,4 +226,7 @@ public Snapshot GetSnapshot() userTimeSinceStart: TimeSpan.FromTicks((long)(cgroupTime / Hundred * _scaleRelativeToCpuRequestForTrackerApi)), memoryUsageInBytes: memoryUsed); } + + private double CpuUtilizationRequest(double cpuRequest) => Math.Min(One, CpuUtilizationV2() / cpuRequest); + private double CpuUtilizationLimit(double cpuLimit) => Math.Min(One, CpuUtilizationV2() / cpuLimit); } diff --git a/src/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring/Linux/Log.cs b/src/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring/Linux/Log.cs index b78f64ddfe0..209a495e844 100644 --- a/src/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring/Linux/Log.cs +++ b/src/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring/Linux/Log.cs @@ -50,19 +50,7 @@ public static partial void CpuUsageDataV2( double actualElapsedNanoseconds, double cpuCores); - [LoggerMessage(5, LogLevel.Debug, - "CPU utilization exceeded 100%: Counter = {counterValue}")] - public static partial void CounterMessage100( - this ILogger logger, - long counterValue); - - [LoggerMessage(6, LogLevel.Debug, - "CPU utilization exceeded 110%: Counter = {counterValue}")] - public static partial void CounterMessage110( - this ILogger logger, - long counterValue); - - [LoggerMessage(7, LogLevel.Warning, + [LoggerMessage(5, LogLevel.Warning, "Error while getting disk stats: Error={errorMessage}")] public static partial void HandleDiskStatsException( this ILogger logger, diff --git a/src/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring/ResourceMonitoringOptions.cs b/src/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring/ResourceMonitoringOptions.cs index 420d6001f57..2fdca9ac6af 100644 --- a/src/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring/ResourceMonitoringOptions.cs +++ b/src/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring/ResourceMonitoringOptions.cs @@ -100,23 +100,17 @@ public partial class ResourceMonitoringOptions public TimeSpan MemoryConsumptionRefreshInterval { get; set; } = DefaultRefreshInterval; /// - /// Gets or sets a value indicating whether CPU metrics are calculated via cgroup CPU limits instead of Host CPU delta. + /// Gets or sets a value indicating whether CPU metrics are calculated using V2 method - via cgroup CPU limits instead of Host CPU delta. /// /// /// The default value is . /// + /// + /// This is a more accurate way to calculate CPU utilization on Linux systems, please enable if possible. + /// It will be the default in the future. + /// [Experimental(diagnosticId: DiagnosticIds.Experiments.ResourceMonitoring, UrlFormat = DiagnosticIds.UrlFormat)] - public bool CalculateCpuUsageWithoutHostDelta { get; set; } - - /// - /// Gets or sets a value indicating whether to use the number of periods in cpu.stat for cgroup CPU usage. - /// We use delta time for CPU usage calculation when this flag is not set. - /// - /// The default value is . - /// - /// - [Experimental(diagnosticId: DiagnosticIds.Experiments.ResourceMonitoring, UrlFormat = DiagnosticIds.UrlFormat)] - public bool UseDeltaNrPeriodsForCpuCalculation { get; set; } + public bool UseLinuxCpuUsageV2 { get; set; } /// /// Gets or sets a value indicating whether disk I/O metrics should be enabled. diff --git a/src/Shared/Instruments/ResourceUtilizationInstruments.cs b/src/Shared/Instruments/ResourceUtilizationInstruments.cs index 3b3e4f80ea2..c0a230c84ea 100644 --- a/src/Shared/Instruments/ResourceUtilizationInstruments.cs +++ b/src/Shared/Instruments/ResourceUtilizationInstruments.cs @@ -89,22 +89,6 @@ internal static class ResourceUtilizationInstruments /// The type of an instrument is . /// public const string SystemNetworkConnections = "system.network.connections"; - - /// - /// The name of an instrument to count occurrences when CPU utilization exceeds 100% of the limit. - /// - /// - /// The type of an instrument is . - /// - public const string CpuUtilizationLimit100PercentExceeded = "cpu.utilization.limit.100percent.exceeded"; - - /// - /// The name of an instrument to count occurrences when CPU utilization exceeds 110% of the limit. - /// - /// - /// The type of an instrument is . - /// - public const string CpuUtilizationLimit110PercentExceeded = "cpu.utilization.limit.110percent.exceeded"; } #pragma warning disable CS1574 diff --git a/test/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring.Tests/Linux/AcceptanceTest.cs b/test/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring.Tests/Linux/AcceptanceTest.cs index efaaea1a51a..f8a22d99385 100644 --- a/test/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring.Tests/Linux/AcceptanceTest.cs +++ b/test/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring.Tests/Linux/AcceptanceTest.cs @@ -359,82 +359,6 @@ public Task ResourceUtilizationTracker_And_Metrics_Report_Same_Values_With_Cgrou return Task.CompletedTask; } - [ConditionalFact] - [CombinatorialData] - [OSSkipCondition(OperatingSystems.Windows | OperatingSystems.MacOSX, SkipReason = "Linux specific tests")] - public Task ResourceUtilizationTracker_And_Metrics_Report_Same_Values_With_Cgroupsv2_v2() - { - var cpuRefresh = TimeSpan.FromMinutes(13); - var memoryRefresh = TimeSpan.FromMinutes(14); - var fileSystem = new HardcodedValueFileSystem(new Dictionary - { - { new FileInfo("/proc/self/cgroup"), "0::/fakeslice"}, - { new FileInfo("/proc/stat"), "cpu 10 10 10 10 10 10 10 10 10 10"}, - { new FileInfo("/sys/fs/cgroup/fakeslice/cpu.stat"), "usage_usec 1020000\nnr_periods 50"}, - { new FileInfo("/sys/fs/cgroup/memory.max"), "1048576" }, - { new FileInfo("/proc/meminfo"), "MemTotal: 1024 kB"}, - { new FileInfo("/sys/fs/cgroup/cpuset.cpus.effective"), "0-19"}, - { new FileInfo("/sys/fs/cgroup/fakeslice/cpu.max"), "40000 10000"}, - { new FileInfo("/sys/fs/cgroup/fakeslice/cpu.weight"), "79"}, - }); - - using var listener = new MeterListener(); - var clock = new FakeTimeProvider(DateTimeOffset.UtcNow); - var cpuFromGauge = 0.0d; - var cpuLimitFromGauge = 0.0d; - var cpuRequestFromGauge = 0.0d; - var memoryFromGauge = 0.0d; - var memoryLimitFromGauge = 0.0d; - using var e = new ManualResetEventSlim(); - - object? meterScope = null; - listener.InstrumentPublished = (Instrument instrument, MeterListener meterListener) - => OnInstrumentPublished(instrument, meterListener, meterScope); - listener.SetMeasurementEventCallback((m, f, _, _) - => OnMeasurementReceived(m, f, ref cpuFromGauge, ref cpuLimitFromGauge, ref cpuRequestFromGauge, ref memoryFromGauge, ref memoryLimitFromGauge)); - listener.Start(); - - using var host = FakeHost.CreateBuilder() - .ConfigureServices(x => - x.AddLogging() - .AddSingleton(clock) - .AddSingleton(new FakeUserHz(100)) - .AddSingleton(fileSystem) - .AddSingleton(new GenericPublisher(_ => e.Set())) - .AddResourceMonitoring(x => x.ConfigureMonitor(options => options.CalculateCpuUsageWithoutHostDelta = true)) - .Replace(ServiceDescriptor.Singleton())) - .Build(); - - meterScope = host.Services.GetRequiredService(); - var tracker = host.Services.GetService(); - Assert.NotNull(tracker); - - _ = host.RunAsync(); - - listener.RecordObservableInstruments(); - - var utilization = tracker.GetUtilization(TimeSpan.FromSeconds(5)); - - fileSystem.ReplaceFileContent(new FileInfo("/proc/stat"), "cpu 11 10 10 10 10 10 10 10 10 10"); - fileSystem.ReplaceFileContent(new FileInfo("/sys/fs/cgroup/fakeslice/cpu.stat"), "usage_usec 1120000\nnr_periods 56"); - fileSystem.ReplaceFileContent(new FileInfo("/sys/fs/cgroup/memory.current"), "524298"); - fileSystem.ReplaceFileContent(new FileInfo("/sys/fs/cgroup/memory.stat"), "inactive_file 10"); - - clock.Advance(TimeSpan.FromSeconds(1)); - listener.RecordObservableInstruments(); - - e.Wait(); - - utilization = tracker.GetUtilization(TimeSpan.FromSeconds(1)); - - var roundedCpuUsedPercentage = Math.Round(utilization.CpuUsedPercentage, 1); - - Assert.Equal(0, Math.Round(cpuLimitFromGauge * 100)); - Assert.Equal(0, Math.Round(cpuRequestFromGauge * 100)); - - return Task.CompletedTask; - } - [ConditionalFact] [CombinatorialData] [OSSkipCondition(OperatingSystems.Windows | OperatingSystems.MacOSX, SkipReason = "Linux specific tests")] @@ -479,8 +403,7 @@ public Task ResourceUtilizationTracker_And_Metrics_Report_Same_Values_With_Cgrou .AddSingleton(new GenericPublisher(_ => e.Set())) .AddResourceMonitoring(x => x.ConfigureMonitor(options => { - options.CalculateCpuUsageWithoutHostDelta = true; - options.UseDeltaNrPeriodsForCpuCalculation = true; + options.UseLinuxCpuUsageV2 = true; })) .Replace(ServiceDescriptor.Singleton())) .Build(); diff --git a/test/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring.Tests/Linux/LinuxUtilizationProviderTests.cs b/test/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring.Tests/Linux/LinuxUtilizationProviderTests.cs index e6e9a282eca..07a66d8f38b 100644 --- a/test/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring.Tests/Linux/LinuxUtilizationProviderTests.cs +++ b/test/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring.Tests/Linux/LinuxUtilizationProviderTests.cs @@ -212,7 +212,7 @@ public void Provider_Registers_Instruments_CgroupV2_WithoutHostCpu() { var meterName = Guid.NewGuid().ToString(); var logger = new FakeLogger(); - var options = Options.Options.Create(new ResourceMonitoringOptions { CalculateCpuUsageWithoutHostDelta = true }); + var options = Options.Options.Create(new ResourceMonitoringOptions { UseLinuxCpuUsageV2 = true }); using var meter = new Meter(nameof(Provider_Registers_Instruments_CgroupV2_WithoutHostCpu)); var meterFactoryMock = new Mock(); meterFactoryMock.Setup(x => x.Create(It.IsAny())) diff --git a/test/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring.Tests/ResourceMonitoringOptionsTests.cs b/test/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring.Tests/ResourceMonitoringOptionsTests.cs index 1a40889fd72..176055c1fe9 100644 --- a/test/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring.Tests/ResourceMonitoringOptionsTests.cs +++ b/test/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring.Tests/ResourceMonitoringOptionsTests.cs @@ -19,7 +19,7 @@ public void Basic() }; Assert.NotNull(options); - Assert.False(options.CalculateCpuUsageWithoutHostDelta); + Assert.False(options.UseLinuxCpuUsageV2); } [Fact] @@ -27,9 +27,9 @@ public void CalculateCpuUsageWithoutHostDelta_WhenSet_ReturnsExpectedValue() { var options = new ResourceMonitoringOptions { - CalculateCpuUsageWithoutHostDelta = true + UseLinuxCpuUsageV2 = true }; - Assert.True(options.CalculateCpuUsageWithoutHostDelta); + Assert.True(options.UseLinuxCpuUsageV2); } } From 35587e3c05b2d8b5e854727b00830906797bbbda Mon Sep 17 00:00:00 2001 From: evgenyfedorov2 Date: Tue, 1 Jul 2025 09:45:23 +0200 Subject: [PATCH 2/6] Rename to UseLinuxCalculationV2 --- .../Linux/LinuxUtilizationProvider.cs | 2 +- .../ResourceMonitoringOptions.cs | 5 +++-- .../Linux/AcceptanceTest.cs | 2 +- .../Linux/LinuxUtilizationProviderTests.cs | 2 +- .../ResourceMonitoringOptionsTests.cs | 6 +++--- 5 files changed, 9 insertions(+), 8 deletions(-) diff --git a/src/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring/Linux/LinuxUtilizationProvider.cs b/src/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring/Linux/LinuxUtilizationProvider.cs index db2981c3a33..2d0931c9b7f 100644 --- a/src/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring/Linux/LinuxUtilizationProvider.cs +++ b/src/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring/Linux/LinuxUtilizationProvider.cs @@ -65,7 +65,7 @@ public LinuxUtilizationProvider(IOptions options, ILi var meter = meterFactory.Create(ResourceUtilizationInstruments.MeterName); #pragma warning restore CA2000 // Dispose objects before losing scope - if (options.Value.UseLinuxCpuUsageV2) + if (options.Value.UseLinuxCalculationV2) { cpuLimit = _parser.GetCgroupLimitV2(); cpuRequest = _parser.GetCgroupRequestCpuV2(); diff --git a/src/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring/ResourceMonitoringOptions.cs b/src/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring/ResourceMonitoringOptions.cs index 2fdca9ac6af..eb890da291e 100644 --- a/src/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring/ResourceMonitoringOptions.cs +++ b/src/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring/ResourceMonitoringOptions.cs @@ -100,17 +100,18 @@ public partial class ResourceMonitoringOptions public TimeSpan MemoryConsumptionRefreshInterval { get; set; } = DefaultRefreshInterval; /// - /// Gets or sets a value indicating whether CPU metrics are calculated using V2 method - via cgroup CPU limits instead of Host CPU delta. + /// Gets or sets a value indicating whether CPU metrics for Linux are calculated using V2 method - via cgroup CPU limits instead of Host CPU delta. /// /// /// The default value is . /// /// + /// This applies to cgroups v2 only and not supported on cgroups v1. /// This is a more accurate way to calculate CPU utilization on Linux systems, please enable if possible. /// It will be the default in the future. /// [Experimental(diagnosticId: DiagnosticIds.Experiments.ResourceMonitoring, UrlFormat = DiagnosticIds.UrlFormat)] - public bool UseLinuxCpuUsageV2 { get; set; } + public bool UseLinuxCalculationV2 { get; set; } /// /// Gets or sets a value indicating whether disk I/O metrics should be enabled. diff --git a/test/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring.Tests/Linux/AcceptanceTest.cs b/test/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring.Tests/Linux/AcceptanceTest.cs index f8a22d99385..b71ea1c47d2 100644 --- a/test/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring.Tests/Linux/AcceptanceTest.cs +++ b/test/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring.Tests/Linux/AcceptanceTest.cs @@ -403,7 +403,7 @@ public Task ResourceUtilizationTracker_And_Metrics_Report_Same_Values_With_Cgrou .AddSingleton(new GenericPublisher(_ => e.Set())) .AddResourceMonitoring(x => x.ConfigureMonitor(options => { - options.UseLinuxCpuUsageV2 = true; + options.UseLinuxCalculationV2 = true; })) .Replace(ServiceDescriptor.Singleton())) .Build(); diff --git a/test/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring.Tests/Linux/LinuxUtilizationProviderTests.cs b/test/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring.Tests/Linux/LinuxUtilizationProviderTests.cs index 07a66d8f38b..775133c31ad 100644 --- a/test/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring.Tests/Linux/LinuxUtilizationProviderTests.cs +++ b/test/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring.Tests/Linux/LinuxUtilizationProviderTests.cs @@ -212,7 +212,7 @@ public void Provider_Registers_Instruments_CgroupV2_WithoutHostCpu() { var meterName = Guid.NewGuid().ToString(); var logger = new FakeLogger(); - var options = Options.Options.Create(new ResourceMonitoringOptions { UseLinuxCpuUsageV2 = true }); + var options = Options.Options.Create(new ResourceMonitoringOptions { UseLinuxCalculationV2 = true }); using var meter = new Meter(nameof(Provider_Registers_Instruments_CgroupV2_WithoutHostCpu)); var meterFactoryMock = new Mock(); meterFactoryMock.Setup(x => x.Create(It.IsAny())) diff --git a/test/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring.Tests/ResourceMonitoringOptionsTests.cs b/test/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring.Tests/ResourceMonitoringOptionsTests.cs index 176055c1fe9..ca3126e8b97 100644 --- a/test/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring.Tests/ResourceMonitoringOptionsTests.cs +++ b/test/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring.Tests/ResourceMonitoringOptionsTests.cs @@ -19,7 +19,7 @@ public void Basic() }; Assert.NotNull(options); - Assert.False(options.UseLinuxCpuUsageV2); + Assert.False(options.UseLinuxCalculationV2); } [Fact] @@ -27,9 +27,9 @@ public void CalculateCpuUsageWithoutHostDelta_WhenSet_ReturnsExpectedValue() { var options = new ResourceMonitoringOptions { - UseLinuxCpuUsageV2 = true + UseLinuxCalculationV2 = true }; - Assert.True(options.UseLinuxCpuUsageV2); + Assert.True(options.UseLinuxCalculationV2); } } From fc46ea3dba57c83e2201e1a871d91935735d93e1 Mon Sep 17 00:00:00 2001 From: evgenyfedorov2 <25526458+evgenyfedorov2@users.noreply.github.com> Date: Tue, 1 Jul 2025 12:38:14 +0200 Subject: [PATCH 3/6] PR Comments --- .../Linux/LinuxUtilizationProvider.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring/Linux/LinuxUtilizationProvider.cs b/src/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring/Linux/LinuxUtilizationProvider.cs index 2d0931c9b7f..ae6270f303b 100644 --- a/src/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring/Linux/LinuxUtilizationProvider.cs +++ b/src/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring/Linux/LinuxUtilizationProvider.cs @@ -227,6 +227,8 @@ public Snapshot GetSnapshot() memoryUsageInBytes: memoryUsed); } + // Math.Min() is used below to mitigate margin errors and various kinds of precisions losses + // due to the fact that the calculation itself is not an atomic operation: private double CpuUtilizationRequest(double cpuRequest) => Math.Min(One, CpuUtilizationV2() / cpuRequest); private double CpuUtilizationLimit(double cpuLimit) => Math.Min(One, CpuUtilizationV2() / cpuLimit); } From 3694e5d88ec8212d563d71d221816946c62f6cb8 Mon Sep 17 00:00:00 2001 From: evgenyfedorov2 <25526458+evgenyfedorov2@users.noreply.github.com> Date: Wed, 2 Jul 2025 15:06:22 +0200 Subject: [PATCH 4/6] Fix --- .../Linux/LinuxUtilizationProvider.cs | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring/Linux/LinuxUtilizationProvider.cs b/src/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring/Linux/LinuxUtilizationProvider.cs index ee0b0f9d694..5fb2f0a189e 100644 --- a/src/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring/Linux/LinuxUtilizationProvider.cs +++ b/src/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring/Linux/LinuxUtilizationProvider.cs @@ -81,9 +81,6 @@ public LinuxUtilizationProvider(IOptions options, ILi _cpuPeriodsInterval = _parser.GetCgroupPeriodsIntervalInMicroSecondsV2(); (_previousCgroupCpuTime, _previousCgroupCpuPeriodCounter) = _parser.GetCgroupCpuUsageInNanosecondsAndCpuPeriodsV2(); - _ = meter.CreateObservableGauge(name: ResourceUtilizationInstruments.ContainerCpuLimitUtilization, observeValue: () => CpuUtilizationLimit(cpuLimit), unit: "1"); - _ = meter.CreateObservableGauge(name: ResourceUtilizationInstruments.ContainerCpuRequestUtilization, observeValue: () => CpuUtilizationRequest(cpuRequest), unit: "1"); - _ = meter.CreateObservableGauge( ResourceUtilizationInstruments.ContainerCpuLimitUtilization, () => GetMeasurementWithRetry(() => CpuUtilizationLimit(cpuLimit)), From 6a57a63b4e35e598d4fc746dfb6333b9dea548ac Mon Sep 17 00:00:00 2001 From: evgenyfedorov2 Date: Thu, 3 Jul 2025 07:06:14 +0200 Subject: [PATCH 5/6] Fix suppressions --- .../CompatibilitySuppressions.xml | 43 ------------------- 1 file changed, 43 deletions(-) diff --git a/src/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring/CompatibilitySuppressions.xml b/src/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring/CompatibilitySuppressions.xml index 32bf7744fbd..29001fe96f0 100644 --- a/src/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring/CompatibilitySuppressions.xml +++ b/src/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring/CompatibilitySuppressions.xml @@ -1,49 +1,6 @@  - - CP0002 - M:Microsoft.Extensions.Diagnostics.ResourceMonitoring.ResourceMonitoringOptions.get_UseDeltaNrPeriodsForCpuCalculation - lib/net462/Microsoft.Extensions.Diagnostics.ResourceMonitoring.dll - lib/net462/Microsoft.Extensions.Diagnostics.ResourceMonitoring.dll - true - - - CP0002 - M:Microsoft.Extensions.Diagnostics.ResourceMonitoring.ResourceMonitoringOptions.set_UseDeltaNrPeriodsForCpuCalculation(System.Boolean) - lib/net462/Microsoft.Extensions.Diagnostics.ResourceMonitoring.dll - lib/net462/Microsoft.Extensions.Diagnostics.ResourceMonitoring.dll - true - - - CP0002 - M:Microsoft.Extensions.Diagnostics.ResourceMonitoring.ResourceMonitoringOptions.get_UseDeltaNrPeriodsForCpuCalculation - lib/net8.0/Microsoft.Extensions.Diagnostics.ResourceMonitoring.dll - lib/net8.0/Microsoft.Extensions.Diagnostics.ResourceMonitoring.dll - true - - - CP0002 - M:Microsoft.Extensions.Diagnostics.ResourceMonitoring.ResourceMonitoringOptions.set_UseDeltaNrPeriodsForCpuCalculation(System.Boolean) - lib/net8.0/Microsoft.Extensions.Diagnostics.ResourceMonitoring.dll - lib/net8.0/Microsoft.Extensions.Diagnostics.ResourceMonitoring.dll - true - - - CP0002 - M:Microsoft.Extensions.Diagnostics.ResourceMonitoring.ResourceMonitoringOptions.get_UseDeltaNrPeriodsForCpuCalculation - lib/net9.0/Microsoft.Extensions.Diagnostics.ResourceMonitoring.dll - lib/net9.0/Microsoft.Extensions.Diagnostics.ResourceMonitoring.dll - true - - - CP0002 - M:Microsoft.Extensions.Diagnostics.ResourceMonitoring.ResourceMonitoringOptions.set_UseDeltaNrPeriodsForCpuCalculation(System.Boolean) - lib/net9.0/Microsoft.Extensions.Diagnostics.ResourceMonitoring.dll - lib/net9.0/Microsoft.Extensions.Diagnostics.ResourceMonitoring.dll - true - - CP0002 M:Microsoft.Extensions.Diagnostics.ResourceMonitoring.ResourceMonitoringOptions.get_CalculateCpuUsageWithoutHostDelta From 9b1b7eb9fdf6c90c127d75d11e49d2a50af1a3d9 Mon Sep 17 00:00:00 2001 From: evgenyfedorov2 Date: Thu, 3 Jul 2025 07:36:40 +0200 Subject: [PATCH 6/6] Revert "Fix suppressions" This reverts commit 6a57a63b4e35e598d4fc746dfb6333b9dea548ac. --- .../CompatibilitySuppressions.xml | 43 +++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/src/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring/CompatibilitySuppressions.xml b/src/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring/CompatibilitySuppressions.xml index 29001fe96f0..32bf7744fbd 100644 --- a/src/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring/CompatibilitySuppressions.xml +++ b/src/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring/CompatibilitySuppressions.xml @@ -1,6 +1,49 @@  + + CP0002 + M:Microsoft.Extensions.Diagnostics.ResourceMonitoring.ResourceMonitoringOptions.get_UseDeltaNrPeriodsForCpuCalculation + lib/net462/Microsoft.Extensions.Diagnostics.ResourceMonitoring.dll + lib/net462/Microsoft.Extensions.Diagnostics.ResourceMonitoring.dll + true + + + CP0002 + M:Microsoft.Extensions.Diagnostics.ResourceMonitoring.ResourceMonitoringOptions.set_UseDeltaNrPeriodsForCpuCalculation(System.Boolean) + lib/net462/Microsoft.Extensions.Diagnostics.ResourceMonitoring.dll + lib/net462/Microsoft.Extensions.Diagnostics.ResourceMonitoring.dll + true + + + CP0002 + M:Microsoft.Extensions.Diagnostics.ResourceMonitoring.ResourceMonitoringOptions.get_UseDeltaNrPeriodsForCpuCalculation + lib/net8.0/Microsoft.Extensions.Diagnostics.ResourceMonitoring.dll + lib/net8.0/Microsoft.Extensions.Diagnostics.ResourceMonitoring.dll + true + + + CP0002 + M:Microsoft.Extensions.Diagnostics.ResourceMonitoring.ResourceMonitoringOptions.set_UseDeltaNrPeriodsForCpuCalculation(System.Boolean) + lib/net8.0/Microsoft.Extensions.Diagnostics.ResourceMonitoring.dll + lib/net8.0/Microsoft.Extensions.Diagnostics.ResourceMonitoring.dll + true + + + CP0002 + M:Microsoft.Extensions.Diagnostics.ResourceMonitoring.ResourceMonitoringOptions.get_UseDeltaNrPeriodsForCpuCalculation + lib/net9.0/Microsoft.Extensions.Diagnostics.ResourceMonitoring.dll + lib/net9.0/Microsoft.Extensions.Diagnostics.ResourceMonitoring.dll + true + + + CP0002 + M:Microsoft.Extensions.Diagnostics.ResourceMonitoring.ResourceMonitoringOptions.set_UseDeltaNrPeriodsForCpuCalculation(System.Boolean) + lib/net9.0/Microsoft.Extensions.Diagnostics.ResourceMonitoring.dll + lib/net9.0/Microsoft.Extensions.Diagnostics.ResourceMonitoring.dll + true + + CP0002 M:Microsoft.Extensions.Diagnostics.ResourceMonitoring.ResourceMonitoringOptions.get_CalculateCpuUsageWithoutHostDelta