Skip to content

Commit

Permalink
Use sum of memory consumptions of all processes of a container (#5301)
Browse files Browse the repository at this point in the history
  • Loading branch information
evgenyfedorov2 authored Jul 23, 2024
1 parent 74f689e commit b5e5452
Show file tree
Hide file tree
Showing 5 changed files with 44 additions and 114 deletions.
Original file line number Diff line number Diff line change
@@ -1,18 +1,16 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using static Microsoft.Extensions.Diagnostics.ResourceMonitoring.Windows.Interop.ProcessInfo;

namespace Microsoft.Extensions.Diagnostics.ResourceMonitoring.Windows.Interop;

/// <summary>
/// An interface to enable the mocking of process information retrieval.
/// An interface to enable the mocking of memory usage information retrieval.
/// </summary>
internal interface IProcessInfo
{
/// <summary>
/// Retrieve the current application memory information.
/// Retrieve the memory usage of a system.
/// </summary>
/// <returns>An appropriate memory data structure.</returns>
APP_MEMORY_INFORMATION GetCurrentAppMemoryInfo();
/// <returns>Memory usage amount in bytes.</returns>
ulong GetMemoryUsage();
}
Original file line number Diff line number Diff line change
@@ -1,88 +1,41 @@
// 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.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Runtime.InteropServices;

namespace Microsoft.Extensions.Diagnostics.ResourceMonitoring.Windows.Interop;

/// <summary>
/// Process native methods class.
/// </summary>
/// <remarks>This will not be covered by UTs, as those
/// classes have insufficient and inconsistent privileges,
/// depending on runtime environment.</remarks>
[ExcludeFromCodeCoverage]
internal static class ProcessInfo
internal sealed class ProcessInfo : IProcessInfo
{
private enum PROCESS_INFORMATION_CLASS
public ulong GetMemoryUsage()
{
ProcessAppMemoryInfo = 2
}

/// <summary>
/// The APP_MEMORY_INFORMATION structure.
/// </summary>
[StructLayout(LayoutKind.Sequential)]
internal struct APP_MEMORY_INFORMATION
{
public ulong AvailableCommit;
public ulong PrivateCommitUsage;
public ulong PeakPrivateCommitUsage;
public ulong TotalCommitUsage;
}

/// <summary>
/// Retrieve the current application memory information.
/// </summary>
/// <returns>An appropriate memory data structure.</returns>
public static APP_MEMORY_INFORMATION GetCurrentAppMemoryInfo()
{
unsafe
{
APP_MEMORY_INFORMATION info = default;
void* buffer = &info;
using var currentProcess = Process.GetCurrentProcess();
NtGetProcessInformation(
currentProcess.Handle,
PROCESS_INFORMATION_CLASS.ProcessAppMemoryInfo,
buffer,
sizeof(APP_MEMORY_INFORMATION));

return info;
}
}

/// <summary>
/// Get process information.
/// </summary>
/// <param name="handle">The handle of the object to query.</param>
/// <param name="infoClass">Process info class.</param>
/// <param name="buffer">Buffer containing the limit.</param>
/// <param name="size">Buffer size.</param>
private static unsafe void NtGetProcessInformation(IntPtr handle, PROCESS_INFORMATION_CLASS infoClass, void* buffer, int size)
{
if (!UnsafeNativeMethods.GetProcessInformation(
handle,
infoClass,
buffer,
size))
ulong memoryUsage = 0;
var processes = Process.GetProcesses();
foreach (var process in processes)
{
Marshal.ThrowExceptionForHR(Marshal.GetHRForLastWin32Error());
try
{
memoryUsage += (ulong)process.WorkingSet64;
}
#pragma warning disable CA1031 // Do not catch general exception types
catch
#pragma warning restore CA1031 // Do not catch general exception types
{
// Ignore various exceptions including, but not limited:
// AccessDenied (from kernel processes),
// InvalidOperation (process does not exist anymore)
// and silently continue to the next process.
}
finally
{
#pragma warning disable EA0011 // Consider removing unnecessary conditional access operator (?)
process?.Dispose();
#pragma warning restore EA0011 // Consider removing unnecessary conditional access operator (?)
}
}
}

private static class UnsafeNativeMethods
{
[DllImport("kernel32.dll", ExactSpelling = true, SetLastError = true)]
[DefaultDllImportSearchPaths(DllImportSearchPath.System32)]
[return: MarshalAs(UnmanagedType.Bool)]
public static unsafe extern bool GetProcessInformation(
IntPtr processHandle,
PROCESS_INFORMATION_CLASS processInformationClass,
void* processInformation,
int processInformationSize);
return memoryUsage;
}
}

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ public WindowsContainerSnapshotProvider(
ILogger<WindowsContainerSnapshotProvider> logger,
IMeterFactory meterFactory,
IOptions<ResourceMonitoringOptions> options)
: this(new MemoryInfo(), new SystemInfo(), new ProcessInfoWrapper(), logger, meterFactory,
: this(new MemoryInfo(), new SystemInfo(), new ProcessInfo(), logger, meterFactory,
static () => new JobHandleWrapper(), TimeProvider.System, options.Value)
{
}
Expand Down Expand Up @@ -169,12 +169,7 @@ private ulong GetMemoryLimits(IJobHandle jobHandle)
/// Gets memory usage within the system.
/// </summary>
/// <returns>Memory usage within the system in bytes.</returns>
private ulong GetMemoryUsage()
{
var memoryInfo = _processInfo.GetCurrentAppMemoryInfo();

return memoryInfo.TotalCommitUsage;
}
private ulong GetMemoryUsage() => _processInfo.GetMemoryUsage();

private double MemoryPercentage()
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ public sealed class WindowsContainerSnapshotProviderTests
private SYSTEM_INFO _sysInfo;
private JOBOBJECT_BASIC_ACCOUNTING_INFORMATION _accountingInfo;
private JOBOBJECT_CPU_RATE_CONTROL_INFORMATION _cpuLimit;
private ProcessInfo.APP_MEMORY_INFORMATION _appMemoryInfo;
private ulong _appMemoryUsage;
private JOBOBJECT_EXTENDED_LIMIT_INFORMATION _limitInfo;

public WindowsContainerSnapshotProviderTests()
Expand Down Expand Up @@ -60,9 +60,9 @@ public WindowsContainerSnapshotProviderTests()
_jobHandleMock.Setup(j => j.GetExtendedLimitInfo())
.Returns(() => _limitInfo);

_appMemoryInfo.TotalCommitUsage = 1000UL;
_processInfoMock.Setup(p => p.GetCurrentAppMemoryInfo())
.Returns(() => _appMemoryInfo);
_appMemoryUsage = 1000UL;
_processInfoMock.Setup(p => p.GetMemoryUsage())
.Returns(() => _appMemoryUsage);
}

[Theory]
Expand Down Expand Up @@ -120,7 +120,7 @@ public void GetSnapshot_ProducesCorrectSnapshot()
Assert.Equal(_accountingInfo.TotalUserTime, data.UserTimeSinceStart.Ticks);
Assert.Equal(_limitInfo.JobMemoryLimit.ToUInt64(), source.Resources.GuaranteedMemoryInBytes);
Assert.Equal(_limitInfo.JobMemoryLimit.ToUInt64(), source.Resources.MaximumMemoryInBytes);
Assert.Equal(_appMemoryInfo.TotalCommitUsage, data.MemoryUsageInBytes);
Assert.Equal(_appMemoryUsage, data.MemoryUsageInBytes);
Assert.True(data.MemoryUsageInBytes > 0);
}

Expand All @@ -147,7 +147,7 @@ public void GetSnapshot_ProducesCorrectSnapshotForDifferentCpuRate()
Assert.Equal(0.7, source.Resources.MaximumCpuUnits);
Assert.Equal(_limitInfo.JobMemoryLimit.ToUInt64(), source.Resources.GuaranteedMemoryInBytes);
Assert.Equal(_limitInfo.JobMemoryLimit.ToUInt64(), source.Resources.MaximumMemoryInBytes);
Assert.Equal(_appMemoryInfo.TotalCommitUsage, data.MemoryUsageInBytes);
Assert.Equal(_appMemoryUsage, data.MemoryUsageInBytes);
Assert.True(data.MemoryUsageInBytes > 0);
}

Expand All @@ -160,7 +160,7 @@ public void GetSnapshot_With_JobMemoryLimit_Set_To_Zero_ProducesCorrectSnapshot(

_limitInfo.JobMemoryLimit = new UIntPtr(0);

_appMemoryInfo.TotalCommitUsage = 3000UL;
_appMemoryUsage = 3000UL;

var source = new WindowsContainerSnapshotProvider(
_memoryInfoMock.Object,
Expand All @@ -179,7 +179,7 @@ public void GetSnapshot_With_JobMemoryLimit_Set_To_Zero_ProducesCorrectSnapshot(
Assert.Equal(1.0, source.Resources.MaximumCpuUnits);
Assert.Equal(_memStatus.TotalPhys, source.Resources.GuaranteedMemoryInBytes);
Assert.Equal(_memStatus.TotalPhys, source.Resources.MaximumMemoryInBytes);
Assert.Equal(_appMemoryInfo.TotalCommitUsage, data.MemoryUsageInBytes);
Assert.Equal(_appMemoryUsage, data.MemoryUsageInBytes);
Assert.True(data.MemoryUsageInBytes > 0);
}

Expand Down Expand Up @@ -248,13 +248,12 @@ public void SnapshotProvider_EmitsCpuMetrics()
[Fact]
public void SnapshotProvider_EmitsMemoryMetrics()
{
_appMemoryInfo.TotalCommitUsage = 200UL;
_appMemoryUsage = 200UL;

ProcessInfo.APP_MEMORY_INFORMATION updatedAppMemoryInfo = default;
updatedAppMemoryInfo.TotalCommitUsage = 600UL;
_processInfoMock.SetupSequence(p => p.GetCurrentAppMemoryInfo())
.Returns(() => _appMemoryInfo)
.Returns(updatedAppMemoryInfo)
ulong updatedAppMemoryUsage = 600UL;
_processInfoMock.SetupSequence(p => p.GetMemoryUsage())
.Returns(() => _appMemoryUsage)
.Returns(updatedAppMemoryUsage)
.Throws(new InvalidOperationException("We shouldn't hit here..."));

var fakeClock = new FakeTimeProvider();
Expand Down

0 comments on commit b5e5452

Please sign in to comment.