Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,7 @@
#nullable enable

using System;
using System.Linq;
using System.Runtime.InteropServices;
using System.Threading;
using Datadog.Trace.Debugger.RateLimiting;
using Datadog.Trace.Logging;
using Datadog.Trace.VendoredMicrosoftCode.System;

Expand All @@ -29,15 +27,10 @@ private DefaultMemoryChecker()

public bool IsLowResourceEnvironment { get; }

[return: MarshalAs(UnmanagedType.Bool)]
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern bool GlobalMemoryStatusEx([In, Out] MEMORYSTATUSEX lpBuffer);

private bool CheckLowResourceEnvironment()
{
try
{
Logger.Debug("Checking if environment is low on resources");
// Check if we're using more than 75% of available memory or there is less than 1GB of RAM available.
return IsLowResourceEnvironmentGc() || IsLowResourceEnvironmentSystem();
}
Expand Down Expand Up @@ -76,7 +69,7 @@ internal bool CheckWindowsMemory()
{
try
{
if (MEMORYSTATUSEX.GetAvailablePhysicalMemory(out var availableMemory))
if (WindowsMemoryInfo.TryGetAvailablePhysicalMemory(out var availableMemory))
{
// If less than 1GB of RAM is available, consider it a low-resource environment
return availableMemory < 1_073_741_824; // 1 GB in bytes
Expand Down Expand Up @@ -151,43 +144,4 @@ protected virtual Datadog.Trace.VendoredMicrosoftCode.System.ReadOnlySpan<char>

return value.Slice(0, spaceIndex);
}

// Windows API for memory information
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
private class MEMORYSTATUSEX
{
#pragma warning disable IDE0044 // Add readonly modifier
#pragma warning disable CS0169 // Field is never used
private uint dwLength;
private uint dwMemoryLoad;
private ulong ullTotalPhys;
#pragma warning disable CS0649 // Field is never assigned to, and will always have its default value
private ulong ullAvailPhys;
#pragma warning restore CS0649 // Field is never assigned to, and will always have its default value
private ulong ullTotalPageFile;
private ulong ullAvailPageFile;
private ulong ullTotalVirtual;
private ulong ullAvailVirtual;
private ulong ullAvailExtendedVirtual;
#pragma warning restore CS0169 // Field is never used
#pragma warning restore IDE0044 // Add readonly modifier

private MEMORYSTATUSEX()
{
dwLength = (uint)Marshal.SizeOf(typeof(MEMORYSTATUSEX));
}

internal static bool GetAvailablePhysicalMemory(out ulong availableMemory)
{
availableMemory = 0;
MEMORYSTATUSEX memStatus = new MEMORYSTATUSEX();
if (GlobalMemoryStatusEx(memStatus))
{
availableMemory = memStatus.ullAvailPhys;
return true;
}

return false;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
// <copyright file="IGCInfoProvider.cs" company="Datadog">
// Unless explicitly stated otherwise all files in this repository are licensed under the Apache 2 License.
// This product includes software developed at Datadog (https://www.datadoghq.com/). Copyright 2017 Datadog, Inc.
// </copyright>

#nullable enable

using System;

namespace Datadog.Trace.Debugger.RateLimiting
{
/// <summary>
/// Abstraction for GC information
/// </summary>
internal interface IGCInfoProvider
{
/// <summary>
/// Gets the number of times garbage collection has occurred for gen 2 objects
/// </summary>
int GetGen2CollectionCount();

#if NETCOREAPP3_1_OR_GREATER
/// <summary>
/// Gets memory info from the garbage collector
/// </summary>
GCMemoryInfo GetGCMemoryInfo();
#endif

/// <summary>
/// Gets the memory usage ratio (0.0 to 1.0+)
/// </summary>
double GetMemoryUsageRatio();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
// <copyright file="IHighResolutionClock.cs" company="Datadog">
// Unless explicitly stated otherwise all files in this repository are licensed under the Apache 2 License.
// This product includes software developed at Datadog (https://www.datadoghq.com/). Copyright 2017 Datadog, Inc.
// </copyright>

#nullable enable

namespace Datadog.Trace.Debugger.RateLimiting
{
internal interface IHighResolutionClock
{
/// <summary>Gets the frequency of the timestamp (ticks per second).</summary>
double Frequency { get; }

/// <summary>Gets the current timestamp in ticks.</summary>
long GetTimestamp();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
// <copyright file="IMemoryPressureMonitor.cs" company="Datadog">
// Unless explicitly stated otherwise all files in this repository are licensed under the Apache 2 License.
// This product includes software developed at Datadog (https://www.datadoghq.com/). Copyright 2017 Datadog, Inc.
// </copyright>

#nullable enable
using System;

namespace Datadog.Trace.Debugger.RateLimiting
{
/// <summary>
/// Monitors runtime memory pressure to protect against OOM scenarios.
/// </summary>
internal interface IMemoryPressureMonitor : IDisposable
{
/// <summary>
/// Gets a value indicating whether memory pressure is currently high
/// </summary>
bool IsHighMemoryPressure { get; }

/// <summary>
/// Gets current memory usage as percentage (0-100)
/// </summary>
double MemoryUsagePercent { get; }

/// <summary>
/// Gets Gen2 collections per second (indicator of pressure)
/// </summary>
double Gen2CollectionsPerSecond { get; }

/// <summary>
/// Gets the high pressure memory threshold as a ratio (0-1)
/// </summary>
double HighPressureThreshold { get; }

/// <summary>
/// Gets the maximum Gen2 collections per second threshold
/// </summary>
int MaxGen2PerSecond { get; }

/// <summary>
/// Records memory pressure event
/// </summary>
void RecordMemoryPressureEvent();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
// <copyright file="ISamplerScheduler.cs" company="Datadog">
// Unless explicitly stated otherwise all files in this repository are licensed under the Apache 2 License.
// This product includes software developed at Datadog (https://www.datadoghq.com/). Copyright 2017 Datadog, Inc.
// </copyright>

#nullable enable
using System;

namespace Datadog.Trace.Debugger.RateLimiting
{
/// <summary>
/// Shared scheduler for sampler window rolls to reduce timer overhead
/// </summary>
internal interface ISamplerScheduler
{
/// <summary>
/// Schedules a callback to be invoked at regular intervals
/// </summary>
/// <param name="callback">The callback to invoke</param>
/// <param name="interval">The interval at which to invoke the callback</param>
/// <returns>A token that can be used to unschedule the callback</returns>
IDisposable Schedule(Action callback, TimeSpan interval);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
// <copyright file="MemoryPressureConfig.cs" company="Datadog">
// Unless explicitly stated otherwise all files in this repository are licensed under the Apache 2 License.
// This product includes software developed at Datadog (https://www.datadoghq.com/). Copyright 2017 Datadog, Inc.
// </copyright>

using System;
using System.Globalization;
using Datadog.Trace.Util;

namespace Datadog.Trace.Debugger.RateLimiting
{
internal readonly struct MemoryPressureConfig
{
public static MemoryPressureConfig Default => new()
{
HighPressureThresholdRatio = 0.85,
MaxGen2PerSecond = 2,
MemoryExitMargin = 0.05,
Gen2ExitMargin = 1,
ConsecutiveHighToEnter = 1,
ConsecutiveLowToExit = 1,
RefreshInterval = TimeSpan.FromSeconds(1)
};

public double HighPressureThresholdRatio { get; init; } // 0.0–1.0

public int MaxGen2PerSecond { get; init; }

public double MemoryExitMargin { get; init; }

public int Gen2ExitMargin { get; init; }

public int ConsecutiveHighToEnter { get; init; }

public int ConsecutiveLowToExit { get; init; }

public TimeSpan RefreshInterval { get; init; }

public override string ToString()
{
var culture = CultureInfo.InvariantCulture;
var sb = StringBuilderCache.Acquire();

sb.Append("Threshold=");
sb.Append(HighPressureThresholdRatio.ToString("F2", culture));
sb.Append(" (");
sb.Append((HighPressureThresholdRatio * 100).ToString("F1", culture));
sb.Append("%), MaxGen2=");
sb.Append(MaxGen2PerSecond.ToString(culture));
sb.Append("/s, ExitMargin=");
sb.Append(MemoryExitMargin.ToString("F2", culture));
sb.Append(", Gen2ExitMargin=");
sb.Append(Gen2ExitMargin.ToString(culture));
sb.Append(", HighToEnter=");
sb.Append(ConsecutiveHighToEnter.ToString(culture));
sb.Append(", LowToExit=");
sb.Append(ConsecutiveLowToExit.ToString(culture));

return StringBuilderCache.GetStringAndRelease(sb);
}
}
}
Loading
Loading