diff --git a/tracer/src/Datadog.Trace/Debugger/Caching/DefaultMemoryChecker.cs b/tracer/src/Datadog.Trace/Debugger/Caching/DefaultMemoryChecker.cs index 7d8091ebc782..b108994bba1b 100644 --- a/tracer/src/Datadog.Trace/Debugger/Caching/DefaultMemoryChecker.cs +++ b/tracer/src/Datadog.Trace/Debugger/Caching/DefaultMemoryChecker.cs @@ -7,6 +7,7 @@ using System; using System.Runtime.InteropServices; +using Datadog.Trace.Debugger.RateLimiting; using Datadog.Trace.Logging; namespace Datadog.Trace.Debugger.Caching; @@ -26,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(); } @@ -73,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 @@ -148,43 +144,4 @@ private ReadOnlySpan ReadMemInfo() return value.Slice(0, spaceIndex); } - - // Windows API for memory information - [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)] - private sealed 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; - } - } } diff --git a/tracer/src/Datadog.Trace/Debugger/DebuggerFactory.cs b/tracer/src/Datadog.Trace/Debugger/DebuggerFactory.cs index a87047b957ad..01d42e96b70a 100644 --- a/tracer/src/Datadog.Trace/Debugger/DebuggerFactory.cs +++ b/tracer/src/Datadog.Trace/Debugger/DebuggerFactory.cs @@ -42,6 +42,7 @@ internal static DynamicInstrumentation CreateDynamicInstrumentation(IDiscoverySe var lineProbeResolver = LineProbeResolver.Create(debuggerSettings.ThirdPartyDetectionExcludes, debuggerSettings.ThirdPartyDetectionIncludes); var probeStatusPoller = ProbeStatusPoller.Create(diagnosticsSink, debuggerSettings); var configurationUpdater = ConfigurationUpdater.Create(tracerSettings.Manager.InitialMutableSettings.Environment, tracerSettings.Manager.InitialMutableSettings.ServiceVersion, debuggerSettings.MaxProbesPerType, globalRateLimiter); + var memoryPressureMonitor = new MemoryPressureMonitor(MemoryPressureConfig.Default); var statsd = GetDogStatsd(tracerSettings); @@ -56,7 +57,8 @@ internal static DynamicInstrumentation CreateDynamicInstrumentation(IDiscoverySe probeStatusPoller: probeStatusPoller, configurationUpdater: configurationUpdater, dogStats: statsd, - globalRateLimiter: globalRateLimiter); + globalRateLimiter: globalRateLimiter, + memoryPressureMonitor: memoryPressureMonitor); } private static IDogStatsd GetDogStatsd(TracerSettings tracerSettings) diff --git a/tracer/src/Datadog.Trace/Debugger/DynamicInstrumentation.cs b/tracer/src/Datadog.Trace/Debugger/DynamicInstrumentation.cs index 7d03b6cab8ca..9a016b3fab4f 100644 --- a/tracer/src/Datadog.Trace/Debugger/DynamicInstrumentation.cs +++ b/tracer/src/Datadog.Trace/Debugger/DynamicInstrumentation.cs @@ -40,7 +40,9 @@ internal sealed class DynamicInstrumentation : IDisposable { private static readonly IDatadogLogger Log = DatadogLogging.GetLoggerFor(typeof(DynamicInstrumentation)); - private readonly TaskCompletionSource _processExit; + // Completed when this DI instance is being disposed (runtime disable via remote config, or process shutdown). + // Used to abort in-flight initialization waits promptly. + private readonly TaskCompletionSource _disposalSignal; private readonly IDiscoveryService _discoveryService; private readonly IRcmSubscriptionManager _subscriptionManager; private readonly ISubscription _subscription; @@ -53,6 +55,7 @@ internal sealed class DynamicInstrumentation : IDisposable private readonly IProbeStatusPoller _probeStatusPoller; private readonly ConfigurationUpdater _configurationUpdater; private readonly IDogStatsd _dogStats; + private readonly MemoryPressureMonitor _memoryPressureMonitor; private readonly DebuggerSettings _settings; private readonly NativeProbeInstrumentationRequester _instrumentProbes; private readonly object _instanceLock = new(); @@ -72,11 +75,12 @@ internal DynamicInstrumentation( ConfigurationUpdater configurationUpdater, IDogStatsd dogStats, IDebuggerGlobalRateLimiter? globalRateLimiter = null, + MemoryPressureMonitor? memoryPressureMonitor = null, NativeProbeInstrumentationRequester? instrumentProbes = null) { Log.Information("Initializing Dynamic Instrumentation"); _settings = settings; - _processExit = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); + _disposalSignal = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); _discoveryService = discoveryService; _lineProbeResolver = lineProbeResolver; _snapshotUploader = snapshotUploader; @@ -87,6 +91,7 @@ internal DynamicInstrumentation( _configurationUpdater = configurationUpdater; _configurationUpdater.SetProbeInstrumentationHandlers(UpdateAddedProbeInstrumentations, UpdateRemovedProbeInstrumentations); _dogStats = dogStats; + _memoryPressureMonitor = memoryPressureMonitor ?? new MemoryPressureMonitor(MemoryPressureConfig.Default); _instrumentProbes = instrumentProbes ?? DebuggerNativeMethods.InstrumentProbes; _unboundProbes = new List(); _lastReportedUnboundProbeErrors = new Dictionary(); @@ -145,7 +150,7 @@ private async Task InitializeAsync() hasFileProbes = _configurationUpdater.HasAnyEffectiveProbeForFile(probeConfiguration); if (hasFileProbes) { - StartRuntimeIfNeeded(); + StartRuntimeIfNeeded(subscribeToRcm: false); } _configurationUpdater.AcceptFile(probeConfiguration); @@ -154,8 +159,7 @@ private async Task InitializeAsync() var isRcmAvailable = await rcmAvailabilityTask.ConfigureAwait(false); if (isRcmAvailable) { - StartRuntimeIfNeeded(); - _subscriptionManager.SubscribeToChanges(_subscription); + StartRuntimeIfNeeded(subscribeToRcm: true); } // Start background processing and register the assembly load callback if either: @@ -184,16 +188,55 @@ private async Task InitializeAsync() } } - private void StartRuntimeIfNeeded() + /// + /// Starts the runtime (background processing + assembly-load callback) exactly once, and optionally + /// subscribes to RCM. Idempotent and safe to call from both the file-probe path ( + /// false) and the RCM-available path ( true). + /// + private void StartRuntimeIfNeeded(bool subscribeToRcm) { - if (IsInitialized || IsDisposed) + if (IsDisposed || (IsInitialized && !subscribeToRcm)) { return; } - AppDomain.CurrentDomain.AssemblyLoad += CheckUnboundProbes; - StartBackgroundProcess(); - Volatile.Write(ref _initializationState, 2); + lock (_instanceLock) + { + if (IsDisposed) + { + return; + } + + // Start the runtime exactly once. + if (!IsInitialized) + { + var assemblyLoadSubscribed = false; + try + { + AppDomain.CurrentDomain.AssemblyLoad += CheckUnboundProbes; + assemblyLoadSubscribed = true; + StartBackgroundProcess(); + Volatile.Write(ref _initializationState, 2); + } + catch + { + if (assemblyLoadSubscribed) + { + AppDomain.CurrentDomain.AssemblyLoad -= CheckUnboundProbes; + } + + throw; + } + } + + // Subscribe only on the RCM path, and only if a disable hasn't landed in the meantime. + // Best-effort: correctness against a leaked subscription is guaranteed by Dispose's Unsubscribe + // running under this same lock. + if (subscribeToRcm && !IsDisposed) + { + _subscriptionManager.SubscribeToChanges(_subscription); + } + } } private void StartBackgroundProcess() @@ -836,6 +879,16 @@ internal void AddLog(ProbeInfo probe, string log) SetProbeStatusToEmitting(probe); } + internal void RefreshMemoryPressureIfStale() + { + if (IsDisposed) + { + return; + } + + _memoryPressureMonitor.RefreshIfStale(); + } + internal void SetProbeStatusToEmitting(ProbeInfo probe) { if (IsDisposed) @@ -899,7 +952,7 @@ private async Task WaitForRcmAvailabilityAsync() var rcmTimeout = TimeSpan.FromMinutes(5); var timeoutTask = Task.Delay(rcmTimeout); - var completedTask = await Task.WhenAny(rcmAvailabilityTcs.Task, timeoutTask, _processExit.Task).ConfigureAwait(false); + var completedTask = await Task.WhenAny(rcmAvailabilityTcs.Task, timeoutTask, _disposalSignal.Task).ConfigureAwait(false); if (completedTask == timeoutTask) { Log.Warning("Dynamic Instrumentation could not be enabled because Remote Configuration Management is not available after waiting {Timeout} seconds. Please note that Dynamic Instrumentation is not supported in all environments (e.g. AAS). Ensure that you are using datadog-agent version 7.41.1 or higher, and that Remote Configuration Management is enabled in datadog-agent's yaml configuration file.", rcmTimeout.TotalSeconds); @@ -930,34 +983,34 @@ void DiscoveryCallback(AgentConfiguration x) public void Dispose() { - // Already disposed if (Interlocked.CompareExchange(ref _disposeState, 1, 0) != 0) { return; } - if (_processExit.Task.IsCompleted) - { - return; - } + _disposalSignal.TrySetResult(true); - _processExit.TrySetResult(true); - AppDomain.CurrentDomain.AssemblyLoad -= CheckUnboundProbes; lock (_instanceLock) { + // Must stay under the lock: StartRuntimeIfNeeded subscribes AssemblyLoad under the same + // lock, so unsubscribing here is what prevents a subscribe-after-dispose handler leak. + AppDomain.CurrentDomain.AssemblyLoad -= CheckUnboundProbes; + SafeDisposal.New() .Execute(() => _subscriptionManager.Unsubscribe(_subscription), "unsubscribing from RCM") .Add(_snapshotUploader) .Add(_logUploader) .Add(_diagnosticsUploader) .Add(_probeStatusPoller) + .Add(_memoryPressureMonitor) .DisposeAll(); } - // Cannot await here because Dispose() is synchronous and callers hold locks. - // On master, _dogStats was disposed via SafeDisposal.Add() which called sync - // Dispose() — itself fire-and-forget internally via Task.Run(). - _dogStats?.DisposeAsync().ContinueWith(t => Log.Error(t.Exception, "Error waiting for StatsD disposal"), TaskContinuationOptions.OnlyOnFaulted); + _dogStats?.DisposeAsync().ContinueWith( + t => Log.Error(t.Exception, "Error waiting for StatsD disposal"), + CancellationToken.None, + TaskContinuationOptions.OnlyOnFaulted, + TaskScheduler.Default); } } } diff --git a/tracer/src/Datadog.Trace/Debugger/Expressions/ProbeProcessor.cs b/tracer/src/Datadog.Trace/Debugger/Expressions/ProbeProcessor.cs index dbefecf530eb..1981376890c8 100644 --- a/tracer/src/Datadog.Trace/Debugger/Expressions/ProbeProcessor.cs +++ b/tracer/src/Datadog.Trace/Debugger/Expressions/ProbeProcessor.cs @@ -106,6 +106,16 @@ private static CaptureLimitInfo ToCaptureLimitInfo(Capture? capture) return result.Count == 0 ? null : result.ToArray(); } + private static bool ShouldRefreshMemoryPressureBeforeCapture(MethodState methodState) + { + return methodState is MethodState.BeginLine + or MethodState.BeginLineAsync + or MethodState.EntryStart + or MethodState.EntryAsync + or MethodState.ExitStart + or MethodState.ExitStartAsync; + } + public bool TryBeginProcess(in ProbeData probeData, [NotNullWhen(true)] out IDebuggerSnapshotCreator? snapshotCreator) { var state = _state; @@ -138,8 +148,13 @@ public bool Process(ref CaptureInfo info, IDebuggerSnapshotC } var probeInfo = state.ProbeInfo; + var dynamicInstrumentation = DebuggerManager.Instance.DynamicInstrumentation; + if (dynamicInstrumentation is not null && ShouldRefreshMemoryPressureBeforeCapture(info.MethodState)) + { + dynamicInstrumentation.RefreshMemoryPressureIfStale(); + } - if (DebuggerManager.Instance.DynamicInstrumentation?.IsInitialized == false) + if (dynamicInstrumentation?.IsInitialized == false) { Log.Debug("Stop processing probe {ID} because Dynamic Instrumentation has not initialized yet or has been disabled, probably dynamically through Remote Config", probeData.ProbeId); snapshotCreator.Stop(); diff --git a/tracer/src/Datadog.Trace/Debugger/RateLimiting/MemoryPressureConfig.cs b/tracer/src/Datadog.Trace/Debugger/RateLimiting/MemoryPressureConfig.cs new file mode 100644 index 000000000000..c1f1681e57f1 --- /dev/null +++ b/tracer/src/Datadog.Trace/Debugger/RateLimiting/MemoryPressureConfig.cs @@ -0,0 +1,65 @@ +// +// 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. +// + +#nullable enable + +using System; +using System.Globalization; +using Datadog.Trace.Util; + +namespace Datadog.Trace.Debugger.RateLimiting +{ + internal readonly struct MemoryPressureConfig + { + public static MemoryPressureConfig Default { get; } = new() + { + HighPressureThresholdRatio = 0.85, + MaxGen2PerSecond = 2, + MemoryExitMargin = 0.05, + Gen2ExitMargin = 1, + ConsecutiveHighToEnter = 1, + ConsecutiveLowToExit = 1, + RefreshInterval = TimeSpan.FromSeconds(1) + }; + + // Enter high pressure at this fraction (0.0–1.0) of available memory in use. Machine-wide on .NET Framework. + public double HighPressureThresholdRatio { get; init; } + + 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); + } + } +} diff --git a/tracer/src/Datadog.Trace/Debugger/RateLimiting/MemoryPressureMonitor.cs b/tracer/src/Datadog.Trace/Debugger/RateLimiting/MemoryPressureMonitor.cs new file mode 100644 index 000000000000..60592f0474ed --- /dev/null +++ b/tracer/src/Datadog.Trace/Debugger/RateLimiting/MemoryPressureMonitor.cs @@ -0,0 +1,467 @@ +// +// 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. +// + +#nullable enable +using System; +using System.Runtime.CompilerServices; +using System.Threading; +using Datadog.Trace.Logging; +using Datadog.Trace.Telemetry; +using Datadog.Trace.Telemetry.Metrics; + +namespace Datadog.Trace.Debugger.RateLimiting +{ + /// Reads the current system/container memory-load ratio (0-1+). Returns false when unsupported. + internal delegate bool TryReadMemoryUsageRatio(out double ratio); + + /// Reads the cumulative gen2 collection count. Returns false when unsupported. + internal delegate bool TryReadGen2CollectionCount(out int count); + + internal delegate void MemoryPressureTransitionHandler( + bool isHighPressure, + MetricTags.DebuggerMemoryPressureTrigger trigger, + double? memoryUsagePercent, + double? gen2CollectionsPerSecond, + double highPressureDurationMs); + + /// + /// Runtime memory pressure monitor for Dynamic Instrumentation. + /// Monitors system memory usage and GC Gen2 collection frequency with debounce + /// to avoid flapping between high and normal pressure states. + /// This monitor is currently observational only: pressure transitions are reported via + /// the transition callback (telemetry in production); the computed + /// state is not yet used to gate or throttle probe capture. + /// + /// + /// Concurrency model: the hot fast path is lock-free (volatile reads only). + /// The actual sampling/commit in is serialized by a non-blocking + /// _refreshInProgress CAS so there is exactly one writer; every mutated field therefore needs no lock. + /// Disposal is lock-free and best-effort: an in-flight refresh may publish one final, benign sample/telemetry + /// transition concurrently with disposal. This is acceptable because the monitor is observational only. + /// Disposal can occur at runtime (when Dynamic Instrumentation is dynamically disabled via remote config), + /// concurrently with hot-path refreshes, as well as at process shutdown. + /// + internal sealed class MemoryPressureMonitor : IDisposable + { + private const long DefaultRefreshIntervalMs = 1000; + + private static readonly IDatadogLogger Log = DatadogLogging.GetLoggerFor(); + private static readonly TryReadMemoryUsageRatio DefaultMemoryReader = SystemMemorySource.TryGetMemoryUsageRatio; + private static readonly TryReadGen2CollectionCount DefaultGen2Reader = SystemMemorySource.TryGetGen2CollectionCount; + private static readonly MemoryPressureTransitionHandler DefaultTransition = RecordTransitionTelemetry; + private static readonly Action DefaultDisabled = RecordDisabledTelemetry; + + private readonly long _staleThresholdMs; + private readonly double _memoryExitThreshold; + private readonly int _gen2ExitThreshold; + private readonly int _consecutiveHighToEnter; + private readonly int _consecutiveLowToExit; + private readonly TryReadMemoryUsageRatio _tryReadMemoryRatio; + private readonly TryReadGen2CollectionCount _tryReadGen2; + private readonly MemoryPressureTransitionHandler _onTransition; + private readonly Action _onDisabled; + + // Refresh writes these fields through a single CAS-guarded writer; every cross-thread access + // goes through Volatile.Read/Volatile.Write (and Interlocked where a read-modify-write is needed). + // We deliberately use the Volatile.* helpers rather than the `volatile` keyword so the idiom is + // uniform across the int/long fields too (`long` cannot be marked `volatile`). + private int _currentMemoryUsagePercentTenths; + private int _gen2CollectionsPerSecondHundredths; + private bool _isHighPressure; + private int _disabled; + private int _disposed; + private int _refreshInProgress; + private bool _hasRefreshed; + + // Single-writer statistics (only mutated inside the CAS-guarded Refresh). + private long _lastGen2Count; + private long _lastRefreshMs; + private long _highPressureStartMs; + private bool _hasHighPressureStart; + private int _highStreak; + private int _lowStreak; + private bool _hasGen2Baseline; + + /// + /// Initializes a new instance of the class using the real + /// system memory/GC sources and telemetry transition reporting. + /// + /// Memory pressure configuration thresholds and debounce. + public MemoryPressureMonitor(MemoryPressureConfig config) + : this(config, memoryRatioReader: null, gen2Reader: null, onTransition: null, onDisabled: null) + { + } + + /// + /// Initializes a new instance of the class. The optional + /// delegates exist so tests can supply deterministic memory/GC samples and observe transitions + /// without real GC interactions; production passes null and the system defaults are used. + /// + internal MemoryPressureMonitor( + MemoryPressureConfig config, + TryReadMemoryUsageRatio? memoryRatioReader, + TryReadGen2CollectionCount? gen2Reader, + MemoryPressureTransitionHandler? onTransition, + Action? onDisabled = null) + { + // Clamp non-sensical configuration to safe minimums. Negative thresholds would otherwise + // make `value > threshold` true for any reading and leave the monitor permanently high. + // Upper bounds are intentionally not enforced - callers can use ratios > 1.0 to effectively + // disable memory-based detection (Gen2 still applies). + HighPressureThreshold = Math.Max(0.0, config.HighPressureThresholdRatio); + MaxGen2PerSecond = Math.Max(0, config.MaxGen2PerSecond); + _memoryExitThreshold = Math.Max(0.0, HighPressureThreshold - Math.Max(0.0, config.MemoryExitMargin)); + _gen2ExitThreshold = Math.Max(0, MaxGen2PerSecond - Math.Max(0, config.Gen2ExitMargin)); + _consecutiveHighToEnter = Math.Max(1, config.ConsecutiveHighToEnter); + _consecutiveLowToExit = Math.Max(1, config.ConsecutiveLowToExit); + _staleThresholdMs = config.RefreshInterval > TimeSpan.Zero ? (long)config.RefreshInterval.TotalMilliseconds : DefaultRefreshIntervalMs; + _tryReadMemoryRatio = memoryRatioReader ?? DefaultMemoryReader; + _tryReadGen2 = gen2Reader ?? DefaultGen2Reader; + _onTransition = onTransition ?? DefaultTransition; + _onDisabled = onDisabled ?? DefaultDisabled; + } + + public double Gen2CollectionsPerSecond + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => Volatile.Read(ref _gen2CollectionsPerSecondHundredths) * 0.01; + } + + public double MemoryUsagePercent + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => Volatile.Read(ref _currentMemoryUsagePercentTenths) * 0.1; + } + + public bool IsHighMemoryPressure + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => Volatile.Read(ref _isHighPressure); + } + + public double HighPressureThreshold { get; } + + public int MaxGen2PerSecond { get; } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void RefreshIfStale() + { + // Read the cheap monotonic clock directly (no abstraction) to keep this hot, + // per-probe-activity entry point as light as possible. + RefreshIfStale(GetTimestampMs()); + } + + // Time is passed in so tests can drive staleness deterministically without a clock seam. + internal void RefreshIfStale(long nowMs) + { + // Once disposed or self-disabled the monitor never samples again, so short-circuit early. + if (IsDisposed() || IsDisabled()) + { + return; + } + + // Sampling is intentionally activity-driven so idle customer processes do not pay a + // continuous timer tax just because Dynamic Instrumentation is enabled. + // The first sample always runs; afterwards we throttle by the stale interval. A dedicated + // flag (rather than a sentinel timestamp) avoids re-sampling when the monotonic clock legitimately reads 0. + if (Volatile.Read(ref _hasRefreshed) && !IsStale(nowMs, Volatile.Read(ref _lastRefreshMs))) + { + return; + } + + Refresh(nowMs); + } + + public void Dispose() + { + // Lock-free, best-effort: an in-flight refresh may publish one last benign sample. We simply + // mark disposed and clear the published state; new refreshes short-circuit on IsDisposed(). + if (Interlocked.Exchange(ref _disposed, 1) != 0) + { + return; + } + + Volatile.Write(ref _isHighPressure, false); + Volatile.Write(ref _currentMemoryUsagePercentTenths, 0); + Volatile.Write(ref _gen2CollectionsPerSecondHundredths, 0); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static long GetTimestampMs() + { +#if NETCOREAPP3_0_OR_GREATER + return Environment.TickCount64; +#else + // net461/netstandard2.0 lack TickCount64. A 1s cadence does not need high resolution, but + // Environment.TickCount (32-bit) wraps every ~24.9 days, so use Stopwatch for a stable monotonic ms. + return unchecked((long)(System.Diagnostics.Stopwatch.GetTimestamp() / (double)System.Diagnostics.Stopwatch.Frequency * 1000.0)); +#endif + } + + private static void RecordTransitionTelemetry(bool isHighPressure, MetricTags.DebuggerMemoryPressureTrigger trigger, double? memoryUsagePercent, double? gen2CollectionsPerSecond, double highPressureDurationMs) + { + var state = isHighPressure + ? MetricTags.DebuggerMemoryPressureState.Enter + : MetricTags.DebuggerMemoryPressureState.Exit; + + TelemetryFactory.Metrics.RecordCountDebuggerMemoryPressureTransitions(state, trigger); + + if (memoryUsagePercent.HasValue) + { + TelemetryFactory.Metrics.RecordCountDebuggerMemoryPressureMemoryUsagePct(state, GetMemoryBucket(memoryUsagePercent.Value)); + } + + if (gen2CollectionsPerSecond.HasValue) + { + TelemetryFactory.Metrics.RecordCountDebuggerMemoryPressureGcActivity(state, GetGcBucket(gen2CollectionsPerSecond.Value)); + } + + if (!isHighPressure) + { + TelemetryFactory.Metrics.RecordCountDebuggerMemoryPressureDuration(GetDurationBucket(highPressureDurationMs)); + } + } + + private static MetricTags.DebuggerMemoryPressureMemoryBucket GetMemoryBucket(double memoryUsagePercent) + => memoryUsagePercent switch + { + < 70 => MetricTags.DebuggerMemoryPressureMemoryBucket.LessThan70, + < 80 => MetricTags.DebuggerMemoryPressureMemoryBucket.From70To80, + < 85 => MetricTags.DebuggerMemoryPressureMemoryBucket.From80To85, + < 90 => MetricTags.DebuggerMemoryPressureMemoryBucket.From85To90, + _ => MetricTags.DebuggerMemoryPressureMemoryBucket.GreaterThanOrEqual90, + }; + + private static MetricTags.DebuggerMemoryPressureGcBucket GetGcBucket(double gen2CollectionsPerSecond) + => gen2CollectionsPerSecond switch + { + < 1 => MetricTags.DebuggerMemoryPressureGcBucket.LessThan1, + < 2 => MetricTags.DebuggerMemoryPressureGcBucket.From1To2, + < 5 => MetricTags.DebuggerMemoryPressureGcBucket.From2To5, + _ => MetricTags.DebuggerMemoryPressureGcBucket.GreaterThanOrEqual5, + }; + + private static MetricTags.DebuggerMemoryPressureDurationBucket GetDurationBucket(double durationMs) + => durationMs switch + { + < 1_000 => MetricTags.DebuggerMemoryPressureDurationBucket.LessThan1Second, + < 5_000 => MetricTags.DebuggerMemoryPressureDurationBucket.From1To5Seconds, + < 30_000 => MetricTags.DebuggerMemoryPressureDurationBucket.From5To30Seconds, + _ => MetricTags.DebuggerMemoryPressureDurationBucket.GreaterThanOrEqual30Seconds, + }; + + private static void RecordDisabledTelemetry(MetricTags.DebuggerMemoryPressureDisabledReason reason) + { + TelemetryFactory.Metrics.RecordCountDebuggerMemoryPressureDisabled(reason); + } + + private void Refresh(long nowMs) + { + // Ensure exactly one sampler/writer at a time. This is a non-blocking guard, not a lock: + // a concurrent caller simply skips this refresh (best-effort, observational). + if (Interlocked.CompareExchange(ref _refreshInProgress, 1, 0) != 0) + { + return; + } + + try + { + if (IsDisposed() || IsDisabled()) + { + return; + } + + var memAvailable = _tryReadMemoryRatio(out var memRatio); + var gcAvailable = _tryReadGen2(out var gen2Count); + + // Re-check after sampling (which may have blocked) so a Dispose() that ran in the meantime + // wins: we publish nothing and fire no transition. This is a single volatile read, not a lock; + // a Dispose() landing after this check is the accepted, benign race for an observational monitor. + if (IsDisposed()) + { + return; + } + + // If neither signal is available on this runtime/platform, stop sampling permanently. + if (!memAvailable && !gcAvailable) + { + if (DisableCore(MetricTags.DebuggerMemoryPressureDisabledReason.NoSignals)) + { + Log.Debug("MemoryPressureMonitor disabled: no memory or GC info available on this runtime/platform."); + } + + return; + } + + // Establish Gen2 baseline if needed. + if (!_hasGen2Baseline && gcAvailable) + { + Volatile.Write(ref _lastRefreshMs, nowMs); + _lastGen2Count = gen2Count; + _hasGen2Baseline = true; + } + + var elapsedMs = nowMs - _lastRefreshMs; + + double gen2PerSecond = 0; + if (_hasGen2Baseline && gcAvailable) + { + var delta = gen2Count - _lastGen2Count; + if (delta < 0) + { + delta = 0; + } + + _lastGen2Count = gen2Count; + gen2PerSecond = elapsedMs > 0 ? delta * 1000.0 / elapsedMs : 0; + } + + var aboveEnterMem = memAvailable && (memRatio > HighPressureThreshold); + var aboveExitMem = memAvailable && (memRatio > _memoryExitThreshold); + var aboveEnterGc = _hasGen2Baseline && gcAvailable && (gen2PerSecond > MaxGen2PerSecond); + var aboveExitGc = _hasGen2Baseline && gcAvailable && (gen2PerSecond > _gen2ExitThreshold); + + var wasHigh = Volatile.Read(ref _isHighPressure); + var meetsHighNow = wasHigh + ? (aboveExitMem || aboveExitGc) + : (aboveEnterMem || aboveEnterGc); + + var nextHigh = ComputeNextHigh(meetsHighNow, wasHigh); + double? usagePercent = memAvailable ? memRatio * 100 : null; + double? sampledGen2PerSecond = _hasGen2Baseline && gcAvailable ? gen2PerSecond : null; + + // Only publish a gauge we actually sampled this cycle; keep the previous value instead of + // clobbering it with 0 when a signal is unavailable on this particular refresh. + if (usagePercent.HasValue) + { + Volatile.Write(ref _currentMemoryUsagePercentTenths, ToScaledInt(usagePercent.Value, 10)); + } + + if (sampledGen2PerSecond.HasValue) + { + Volatile.Write(ref _gen2CollectionsPerSecondHundredths, ToScaledInt(sampledGen2PerSecond.Value, 100)); + } + + Volatile.Write(ref _isHighPressure, nextHigh); + Volatile.Write(ref _lastRefreshMs, nowMs); + Volatile.Write(ref _hasRefreshed, true); + + if (nextHigh == wasHigh) + { + return; + } + + double durationMs = 0; + var trigger = MetricTags.DebuggerMemoryPressureTrigger.None; + if (nextHigh) + { + // Entry always coincides with a high-streak cycle, so at least one "above enter" signal is set. + trigger = aboveEnterMem && aboveEnterGc + ? MetricTags.DebuggerMemoryPressureTrigger.Both + : aboveEnterMem + ? MetricTags.DebuggerMemoryPressureTrigger.Memory + : MetricTags.DebuggerMemoryPressureTrigger.Gc; + _highPressureStartMs = nowMs; + _hasHighPressureStart = true; + } + else + { + durationMs = _hasHighPressureStart ? nowMs - _highPressureStartMs : 0; + _highPressureStartMs = 0; + _hasHighPressureStart = false; + } + + // Keep transition emission inside the single-writer refresh so observers see serialized, + // ordered state transitions. This only runs on enter/exit, not every refresh. + _onTransition(nextHigh, trigger, usagePercent, sampledGen2PerSecond, durationMs); + + Log.Debug( + "Memory pressure {State}: Usage={Usage:F1}%, Gen2/sec={Gen2:F2}", + property0: nextHigh ? "ENTER" : "EXIT", + property1: usagePercent ?? 0, + property2: sampledGen2PerSecond ?? 0); + } + catch (Exception ex) + { + DisableCore(MetricTags.DebuggerMemoryPressureDisabledReason.Error); + Log.Error(ex, "Error refreshing memory pressure"); + } + finally + { + Volatile.Write(ref _refreshInProgress, 0); + } + + bool ComputeNextHigh(bool meetsHighNow, bool isCurrentlyHigh) + { + if (meetsHighNow) + { + if (_highStreak < _consecutiveHighToEnter) + { + _highStreak++; + } + + _lowStreak = 0; + } + else + { + if (_lowStreak < _consecutiveLowToExit) + { + _lowStreak++; + } + + _highStreak = 0; + } + + return isCurrentlyHigh + ? _lowStreak < _consecutiveLowToExit // stay high until enough low cycles + : _highStreak >= _consecutiveHighToEnter; // enter after enough high cycles + } + + static int ToScaledInt(double value, int scale) + { + if (value <= 0) + { + return 0; + } + + var scaled = value * scale; + return scaled >= int.MaxValue ? int.MaxValue : (int)scaled; + } + + bool DisableCore(MetricTags.DebuggerMemoryPressureDisabledReason reason) + { + if (IsDisposed() || IsDisabled()) + { + return false; + } + + Volatile.Write(ref _disabled, 1); + Volatile.Write(ref _isHighPressure, false); + Volatile.Write(ref _currentMemoryUsagePercentTenths, 0); + Volatile.Write(ref _gen2CollectionsPerSecondHundredths, 0); + _onDisabled(reason); + return true; + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private bool IsStale(long nowMs, long lastRefreshMs) + { + return (nowMs - lastRefreshMs) >= _staleThresholdMs; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private bool IsDisposed() + { + return Volatile.Read(ref _disposed) != 0; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private bool IsDisabled() + { + return Volatile.Read(ref _disabled) != 0; + } + } +} diff --git a/tracer/src/Datadog.Trace/Debugger/RateLimiting/SystemMemorySource.cs b/tracer/src/Datadog.Trace/Debugger/RateLimiting/SystemMemorySource.cs new file mode 100644 index 000000000000..bab8b18b2f03 --- /dev/null +++ b/tracer/src/Datadog.Trace/Debugger/RateLimiting/SystemMemorySource.cs @@ -0,0 +1,61 @@ +// +// 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. +// + +#nullable enable + +using System; +using Datadog.Trace.Logging; + +namespace Datadog.Trace.Debugger.RateLimiting +{ + /// + /// Reads real GC and system memory information. These methods match the + /// / delegate shapes + /// so they can be used directly as the production sources for . + /// + internal static class SystemMemorySource + { +#if NETCOREAPP3_1_OR_GREATER || NETFRAMEWORK + private static readonly IDatadogLogger Log = DatadogLogging.GetLoggerFor(typeof(SystemMemorySource)); +#endif + + public static bool TryGetGen2CollectionCount(out int count) + { + count = GC.CollectionCount(2); + return true; + } + + // Returns system/container-wide memory load, not process-private bytes: the container OOMs on total + // usage, so that is the right scope for "back off allocating". Both paths report fraction-of-available. + // Future (heavier) option: switch both to processWorkingSet / explicit limit (cgroup/job-object). + public static bool TryGetMemoryUsageRatio(out double ratio) + { + ratio = 0; +#if NETCOREAPP3_1_OR_GREATER + // Fraction of available memory in use (container/cgroup-aware), not the GC high-load threshold, + // which is incomparable to the Framework path. + var gcInfo = GC.GetGCMemoryInfo(); + if (gcInfo.TotalAvailableMemoryBytes > 0) + { + ratio = (double)gcInfo.MemoryLoadBytes / gcInfo.TotalAvailableMemoryBytes; + return true; + } + + Log.Debug("GC.GetGCMemoryInfo returned non-positive TotalAvailableMemoryBytes. Treating memory usage ratio as unsupported on this runtime."); +#elif NETFRAMEWORK + // Machine-wide load: GlobalMemoryStatusEx is not container/job-object aware, so it under-reports + // pressure inside memory-limited Windows containers (sees the host, not the limit). Clamped to [0,1]. + if (WindowsMemoryInfo.TryGetMemoryLoadRatio(out ratio)) + { + return true; + } + + Log.Debug("GlobalMemoryStatusEx did not return memory information. Treating memory usage ratio as unsupported on this platform."); +#else +#endif + return false; + } + } +} diff --git a/tracer/src/Datadog.Trace/Debugger/RateLimiting/WindowsMemoryInfo.cs b/tracer/src/Datadog.Trace/Debugger/RateLimiting/WindowsMemoryInfo.cs new file mode 100644 index 000000000000..0bfd7ac9c3d7 --- /dev/null +++ b/tracer/src/Datadog.Trace/Debugger/RateLimiting/WindowsMemoryInfo.cs @@ -0,0 +1,72 @@ +// +// 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. +// + +#nullable enable + +using System; +using System.Runtime.InteropServices; +using Datadog.Trace.Logging; + +namespace Datadog.Trace.Debugger.RateLimiting +{ + internal static class WindowsMemoryInfo + { + private static readonly IDatadogLogger Log = DatadogLogging.GetLoggerFor(typeof(WindowsMemoryInfo)); + + [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] + private static extern bool GlobalMemoryStatusEx(ref MEMORYSTATUSEX lpBuffer); + + internal static bool TryGetMemoryLoadRatio(out double ratio) + { + var status = new MEMORYSTATUSEX { dwLength = (uint)Marshal.SizeOf() }; + if (GlobalMemoryStatusEx(ref status)) + { + ratio = Math.Min(1.0, status.dwMemoryLoad / 100.0); + return true; + } + + var error = Marshal.GetLastWin32Error(); + Log.Debug( + "GlobalMemoryStatusEx failed when getting memory load ratio. ErrorCode={ErrorCode}", + property: error); + ratio = 0; + return false; + } + + internal static bool TryGetAvailablePhysicalMemory(out ulong bytes) + { + var status = new MEMORYSTATUSEX { dwLength = (uint)Marshal.SizeOf() }; + if (GlobalMemoryStatusEx(ref status)) + { + bytes = status.ullAvailPhys; + return true; + } + + var error = Marshal.GetLastWin32Error(); + Log.Debug( + "GlobalMemoryStatusEx failed when getting available physical memory. ErrorCode={ErrorCode}", + property: error); + bytes = 0; + return false; + } + + [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)] + private struct MEMORYSTATUSEX + { +#pragma warning disable SA1307 + public uint dwLength; + public uint dwMemoryLoad; + public ulong ullTotalPhys; + public ulong ullAvailPhys; + public ulong ullTotalPageFile; + public ulong ullAvailPageFile; + public ulong ullTotalVirtual; + public ulong ullAvailVirtual; + public ulong ullAvailExtendedVirtual; +#pragma warning restore SA1307 + } + } +} diff --git a/tracer/src/Datadog.Trace/Generated/net461/Datadog.Trace.SourceGenerators/TelemetryMetricGenerator/CiVisibilityMetricsTelemetryCollector_Count.g.cs b/tracer/src/Datadog.Trace/Generated/net461/Datadog.Trace.SourceGenerators/TelemetryMetricGenerator/CiVisibilityMetricsTelemetryCollector_Count.g.cs index 8c6817ebca98..b2cd615aef09 100644 --- a/tracer/src/Datadog.Trace/Generated/net461/Datadog.Trace.SourceGenerators/TelemetryMetricGenerator/CiVisibilityMetricsTelemetryCollector_Count.g.cs +++ b/tracer/src/Datadog.Trace/Generated/net461/Datadog.Trace.SourceGenerators/TelemetryMetricGenerator/CiVisibilityMetricsTelemetryCollector_Count.g.cs @@ -156,6 +156,26 @@ public void RecordCountDirectLogApiErrors(Datadog.Trace.Telemetry.Metrics.Metric { } + public void RecordCountDebuggerMemoryPressureTransitions(Datadog.Trace.Telemetry.Metrics.MetricTags.DebuggerMemoryPressureState tag1, Datadog.Trace.Telemetry.Metrics.MetricTags.DebuggerMemoryPressureTrigger tag2, int increment = 1) + { + } + + public void RecordCountDebuggerMemoryPressureDisabled(Datadog.Trace.Telemetry.Metrics.MetricTags.DebuggerMemoryPressureDisabledReason tag, int increment = 1) + { + } + + public void RecordCountDebuggerMemoryPressureMemoryUsagePct(Datadog.Trace.Telemetry.Metrics.MetricTags.DebuggerMemoryPressureState tag1, Datadog.Trace.Telemetry.Metrics.MetricTags.DebuggerMemoryPressureMemoryBucket tag2, int increment = 1) + { + } + + public void RecordCountDebuggerMemoryPressureGcActivity(Datadog.Trace.Telemetry.Metrics.MetricTags.DebuggerMemoryPressureState tag1, Datadog.Trace.Telemetry.Metrics.MetricTags.DebuggerMemoryPressureGcBucket tag2, int increment = 1) + { + } + + public void RecordCountDebuggerMemoryPressureDuration(Datadog.Trace.Telemetry.Metrics.MetricTags.DebuggerMemoryPressureDurationBucket tag, int increment = 1) + { + } + public void RecordCountWafInit(Datadog.Trace.Telemetry.Metrics.MetricTags.WafStatus tag, int increment = 1) { } diff --git a/tracer/src/Datadog.Trace/Generated/net461/Datadog.Trace.SourceGenerators/TelemetryMetricGenerator/CountExtensions.g.cs b/tracer/src/Datadog.Trace/Generated/net461/Datadog.Trace.SourceGenerators/TelemetryMetricGenerator/CountExtensions.g.cs index 7078b0d04857..f1a010f936f4 100644 --- a/tracer/src/Datadog.Trace/Generated/net461/Datadog.Trace.SourceGenerators/TelemetryMetricGenerator/CountExtensions.g.cs +++ b/tracer/src/Datadog.Trace/Generated/net461/Datadog.Trace.SourceGenerators/TelemetryMetricGenerator/CountExtensions.g.cs @@ -12,7 +12,7 @@ internal static partial class CountExtensions /// /// The number of separate metrics in the metric. /// - public const int Length = 51; + public const int Length = 56; /// /// Gets the metric name for the provided metric @@ -58,6 +58,11 @@ public static string GetName(this Datadog.Trace.Telemetry.Metrics.Count metric) Datadog.Trace.Telemetry.Metrics.Count.DirectLogApiRequests => "direct_log_api.requests", Datadog.Trace.Telemetry.Metrics.Count.DirectLogApiResponses => "direct_log_api.responses", Datadog.Trace.Telemetry.Metrics.Count.DirectLogApiErrors => "direct_log_api.errors", + Datadog.Trace.Telemetry.Metrics.Count.DebuggerMemoryPressureTransitions => "memory_pressure.transitions", + Datadog.Trace.Telemetry.Metrics.Count.DebuggerMemoryPressureDisabled => "memory_pressure.disabled", + Datadog.Trace.Telemetry.Metrics.Count.DebuggerMemoryPressureMemoryUsagePct => "memory_pressure.memory_usage_pct", + Datadog.Trace.Telemetry.Metrics.Count.DebuggerMemoryPressureGcActivity => "memory_pressure.gc_activity", + Datadog.Trace.Telemetry.Metrics.Count.DebuggerMemoryPressureDuration => "memory_pressure.duration", Datadog.Trace.Telemetry.Metrics.Count.WafInit => "waf.init", Datadog.Trace.Telemetry.Metrics.Count.WafUpdates => "waf.updates", Datadog.Trace.Telemetry.Metrics.Count.WafRequests => "waf.requests", @@ -111,6 +116,11 @@ public static bool IsCommon(this Datadog.Trace.Telemetry.Metrics.Count metric) Datadog.Trace.Telemetry.Metrics.Count.TelemetryApiRequests => "telemetry", Datadog.Trace.Telemetry.Metrics.Count.TelemetryApiResponses => "telemetry", Datadog.Trace.Telemetry.Metrics.Count.TelemetryApiErrors => "telemetry", + Datadog.Trace.Telemetry.Metrics.Count.DebuggerMemoryPressureTransitions => "live_debugger", + Datadog.Trace.Telemetry.Metrics.Count.DebuggerMemoryPressureDisabled => "live_debugger", + Datadog.Trace.Telemetry.Metrics.Count.DebuggerMemoryPressureMemoryUsagePct => "live_debugger", + Datadog.Trace.Telemetry.Metrics.Count.DebuggerMemoryPressureGcActivity => "live_debugger", + Datadog.Trace.Telemetry.Metrics.Count.DebuggerMemoryPressureDuration => "live_debugger", Datadog.Trace.Telemetry.Metrics.Count.WafInit => "appsec", Datadog.Trace.Telemetry.Metrics.Count.WafUpdates => "appsec", Datadog.Trace.Telemetry.Metrics.Count.WafRequests => "appsec", diff --git a/tracer/src/Datadog.Trace/Generated/net461/Datadog.Trace.SourceGenerators/TelemetryMetricGenerator/IMetricsTelemetryCollector_Count.g.cs b/tracer/src/Datadog.Trace/Generated/net461/Datadog.Trace.SourceGenerators/TelemetryMetricGenerator/IMetricsTelemetryCollector_Count.g.cs index 0a4e7e205295..85f010d8084a 100644 --- a/tracer/src/Datadog.Trace/Generated/net461/Datadog.Trace.SourceGenerators/TelemetryMetricGenerator/IMetricsTelemetryCollector_Count.g.cs +++ b/tracer/src/Datadog.Trace/Generated/net461/Datadog.Trace.SourceGenerators/TelemetryMetricGenerator/IMetricsTelemetryCollector_Count.g.cs @@ -81,6 +81,16 @@ internal partial interface IMetricsTelemetryCollector public void RecordCountDirectLogApiErrors(Datadog.Trace.Telemetry.Metrics.MetricTags.ApiError tag, int increment = 1); + public void RecordCountDebuggerMemoryPressureTransitions(Datadog.Trace.Telemetry.Metrics.MetricTags.DebuggerMemoryPressureState tag1, Datadog.Trace.Telemetry.Metrics.MetricTags.DebuggerMemoryPressureTrigger tag2, int increment = 1); + + public void RecordCountDebuggerMemoryPressureDisabled(Datadog.Trace.Telemetry.Metrics.MetricTags.DebuggerMemoryPressureDisabledReason tag, int increment = 1); + + public void RecordCountDebuggerMemoryPressureMemoryUsagePct(Datadog.Trace.Telemetry.Metrics.MetricTags.DebuggerMemoryPressureState tag1, Datadog.Trace.Telemetry.Metrics.MetricTags.DebuggerMemoryPressureMemoryBucket tag2, int increment = 1); + + public void RecordCountDebuggerMemoryPressureGcActivity(Datadog.Trace.Telemetry.Metrics.MetricTags.DebuggerMemoryPressureState tag1, Datadog.Trace.Telemetry.Metrics.MetricTags.DebuggerMemoryPressureGcBucket tag2, int increment = 1); + + public void RecordCountDebuggerMemoryPressureDuration(Datadog.Trace.Telemetry.Metrics.MetricTags.DebuggerMemoryPressureDurationBucket tag, int increment = 1); + public void RecordCountWafInit(Datadog.Trace.Telemetry.Metrics.MetricTags.WafStatus tag, int increment = 1); public void RecordCountWafUpdates(Datadog.Trace.Telemetry.Metrics.MetricTags.WafStatus tag, int increment = 1); diff --git a/tracer/src/Datadog.Trace/Generated/net461/Datadog.Trace.SourceGenerators/TelemetryMetricGenerator/MetricsTelemetryCollector_Count.g.cs b/tracer/src/Datadog.Trace/Generated/net461/Datadog.Trace.SourceGenerators/TelemetryMetricGenerator/MetricsTelemetryCollector_Count.g.cs index 5f507e7f2e88..0a01025405c7 100644 --- a/tracer/src/Datadog.Trace/Generated/net461/Datadog.Trace.SourceGenerators/TelemetryMetricGenerator/MetricsTelemetryCollector_Count.g.cs +++ b/tracer/src/Datadog.Trace/Generated/net461/Datadog.Trace.SourceGenerators/TelemetryMetricGenerator/MetricsTelemetryCollector_Count.g.cs @@ -11,7 +11,7 @@ namespace Datadog.Trace.Telemetry; internal sealed partial class MetricsTelemetryCollector { - private const int CountLength = 662; + private const int CountLength = 694; /// /// Creates the buffer for the values. @@ -594,13 +594,50 @@ private static AggregatedMetric[] GetCountBuffer() new(new[] { "type:timeout" }), new(new[] { "type:network" }), new(new[] { "type:status_code" }), - // waf.init, index = 539 + // memory_pressure.transitions, index = 539 + new(new[] { "state:enter", "trigger:none" }), + new(new[] { "state:enter", "trigger:memory" }), + new(new[] { "state:enter", "trigger:gc" }), + new(new[] { "state:enter", "trigger:both" }), + new(new[] { "state:exit", "trigger:none" }), + new(new[] { "state:exit", "trigger:memory" }), + new(new[] { "state:exit", "trigger:gc" }), + new(new[] { "state:exit", "trigger:both" }), + // memory_pressure.disabled, index = 547 + new(new[] { "reason:no_signals" }), + new(new[] { "reason:error" }), + // memory_pressure.memory_usage_pct, index = 549 + new(new[] { "state:enter", "bucket:lt_70" }), + new(new[] { "state:enter", "bucket:70_80" }), + new(new[] { "state:enter", "bucket:80_85" }), + new(new[] { "state:enter", "bucket:85_90" }), + new(new[] { "state:enter", "bucket:gte_90" }), + new(new[] { "state:exit", "bucket:lt_70" }), + new(new[] { "state:exit", "bucket:70_80" }), + new(new[] { "state:exit", "bucket:80_85" }), + new(new[] { "state:exit", "bucket:85_90" }), + new(new[] { "state:exit", "bucket:gte_90" }), + // memory_pressure.gc_activity, index = 559 + new(new[] { "state:enter", "bucket:lt_1" }), + new(new[] { "state:enter", "bucket:1_2" }), + new(new[] { "state:enter", "bucket:2_5" }), + new(new[] { "state:enter", "bucket:gte_5" }), + new(new[] { "state:exit", "bucket:lt_1" }), + new(new[] { "state:exit", "bucket:1_2" }), + new(new[] { "state:exit", "bucket:2_5" }), + new(new[] { "state:exit", "bucket:gte_5" }), + // memory_pressure.duration, index = 567 + new(new[] { "bucket:lt_1s" }), + new(new[] { "bucket:1_5s" }), + new(new[] { "bucket:5_30s" }), + new(new[] { "bucket:gte_30s" }), + // waf.init, index = 571 new(new[] { "waf_version", "event_rules_version", "success:true" }), new(new[] { "waf_version", "event_rules_version", "success:false" }), - // waf.updates, index = 541 + // waf.updates, index = 573 new(new[] { "waf_version", "event_rules_version", "success:true" }), new(new[] { "waf_version", "event_rules_version", "success:false" }), - // waf.requests, index = 543 + // waf.requests, index = 575 new(new[] { "waf_version", "event_rules_version", "rule_triggered:false", "request_blocked:false", "waf_timeout:false", "block_failure:false", "rate_limited:false", "input_truncated:false" }), new(new[] { "waf_version", "event_rules_version", "rule_triggered:true", "request_blocked:false", "waf_timeout:false", "block_failure:false", "rate_limited:false", "input_truncated:false" }), new(new[] { "waf_version", "event_rules_version", "rule_triggered:true", "request_blocked:true", "waf_timeout:false", "block_failure:false", "rate_limited:false", "input_truncated:false" }), @@ -609,17 +646,17 @@ private static AggregatedMetric[] GetCountBuffer() new(new[] { "waf_version", "event_rules_version", "rule_triggered:true", "request_blocked:false", "waf_timeout:false", "block_failure:false", "rate_limited:false", "input_truncated:true" }), new(new[] { "waf_version", "event_rules_version", "rule_triggered:true", "request_blocked:true", "waf_timeout:false", "block_failure:false", "rate_limited:false", "input_truncated:true" }), new(new[] { "waf_version", "event_rules_version", "rule_triggered:false", "request_blocked:false", "waf_timeout:true", "block_failure:false", "rate_limited:false", "input_truncated:true" }), - // waf.input_truncated, index = 551 + // waf.input_truncated, index = 583 new(new[] { "truncation_reason:string_too_long" }), new(new[] { "truncation_reason:list_or_map_too_large" }), new(new[] { "truncation_reason:object_too_deep" }), - // rasp.rule.eval, index = 554 + // rasp.rule.eval, index = 586 new(new[] { "waf_version", "event_rules_version", "rule_type:lfi" }), new(new[] { "waf_version", "event_rules_version", "rule_type:ssrf" }), new(new[] { "waf_version", "event_rules_version", "rule_type:sql_injection" }), new(new[] { "waf_version", "event_rules_version", "rule_type:command_injection", "rule_variant:shell" }), new(new[] { "waf_version", "event_rules_version", "rule_type:command_injection", "rule_variant:exec" }), - // rasp.rule.match, index = 559 + // rasp.rule.match, index = 591 new(new[] { "waf_version", "event_rules_version", "block:success", "rule_type:lfi" }), new(new[] { "waf_version", "event_rules_version", "block:success", "rule_type:ssrf" }), new(new[] { "waf_version", "event_rules_version", "block:success", "rule_type:sql_injection" }), @@ -635,29 +672,29 @@ private static AggregatedMetric[] GetCountBuffer() new(new[] { "waf_version", "event_rules_version", "block:irrelevant", "rule_type:sql_injection" }), new(new[] { "waf_version", "event_rules_version", "block:irrelevant", "rule_type:command_injection", "rule_variant:shell" }), new(new[] { "waf_version", "event_rules_version", "block:irrelevant", "rule_type:command_injection", "rule_variant:exec" }), - // rasp.timeout, index = 574 + // rasp.timeout, index = 606 new(new[] { "waf_version", "event_rules_version", "rule_type:lfi" }), new(new[] { "waf_version", "event_rules_version", "rule_type:ssrf" }), new(new[] { "waf_version", "event_rules_version", "rule_type:sql_injection" }), new(new[] { "waf_version", "event_rules_version", "rule_type:command_injection", "rule_variant:shell" }), new(new[] { "waf_version", "event_rules_version", "rule_type:command_injection", "rule_variant:exec" }), - // instrum.user_auth.missing_user_id, index = 579 + // instrum.user_auth.missing_user_id, index = 611 new(new[] { "framework:aspnetcore_identity", "event_type:login_success" }), new(new[] { "framework:aspnetcore_identity", "event_type:login_failure" }), new(new[] { "framework:aspnetcore_identity", "event_type:signup" }), new(new[] { "framework:unknown", "event_type:signup" }), - // instrum.user_auth.missing_user_login, index = 583 + // instrum.user_auth.missing_user_login, index = 615 new(new[] { "framework:aspnetcore_identity", "event_type:login_success" }), new(new[] { "framework:aspnetcore_identity", "event_type:login_failure" }), new(new[] { "framework:aspnetcore_identity", "event_type:signup" }), new(new[] { "framework:unknown", "event_type:signup" }), - // sdk.event, index = 587 + // sdk.event, index = 619 new(new[] { "event_type:login_success", "sdk_version:v1" }), new(new[] { "event_type:login_success", "sdk_version:v2" }), new(new[] { "event_type:login_failure", "sdk_version:v1" }), new(new[] { "event_type:login_failure", "sdk_version:v2" }), new(new[] { "event_type:custom", "sdk_version:v1" }), - // executed.source, index = 592 + // executed.source, index = 624 new(new[] { "source_type:http.request.body" }), new(new[] { "source_type:http.request.path" }), new(new[] { "source_type:http.request.parameter.name" }), @@ -672,9 +709,9 @@ private static AggregatedMetric[] GetCountBuffer() new(new[] { "source_type:http.request.uri" }), new(new[] { "source_type:grpc.request.body" }), new(new[] { "source_type:sql.row.value" }), - // executed.propagation, index = 606 + // executed.propagation, index = 638 new(null), - // executed.sink, index = 607 + // executed.sink, index = 639 new(new[] { "vulnerability_type:none" }), new(new[] { "vulnerability_type:weak_cipher" }), new(new[] { "vulnerability_type:weak_hash" }), @@ -702,9 +739,9 @@ private static AggregatedMetric[] GetCountBuffer() new(new[] { "vulnerability_type:directory_listing_leak" }), new(new[] { "vulnerability_type:session_timeout" }), new(new[] { "vulnerability_type:email_html_injection" }), - // request.tainted, index = 634 + // request.tainted, index = 666 new(null), - // suppressed.vulnerabilities, index = 635 + // suppressed.vulnerabilities, index = 667 new(new[] { "vulnerability_type:none" }), new(new[] { "vulnerability_type:weak_cipher" }), new(new[] { "vulnerability_type:weak_hash" }), @@ -740,7 +777,7 @@ private static AggregatedMetric[] GetCountBuffer() /// It is equal to the cardinality of the tag combinations (or 1 if there are no tags) /// private static int[] CountEntryCounts { get; } - = new int[]{ 4, 85, 1, 3, 5, 2, 2, 5, 1, 1, 1, 22, 3, 2, 5, 5, 4, 1, 1, 22, 3, 90, 90, 4, 4, 4, 4, 2, 44, 6, 1, 1, 85, 1, 22, 3, 2, 2, 8, 3, 5, 15, 5, 4, 4, 5, 14, 1, 27, 1, 27, }; + = new int[]{ 4, 85, 1, 3, 5, 2, 2, 5, 1, 1, 1, 22, 3, 2, 5, 5, 4, 1, 1, 22, 3, 90, 90, 4, 4, 4, 4, 2, 44, 6, 1, 1, 85, 1, 22, 3, 8, 2, 10, 8, 4, 2, 2, 8, 3, 5, 15, 5, 4, 4, 5, 14, 1, 27, 1, 27, }; public void RecordCountLogCreated(Datadog.Trace.Telemetry.Metrics.MetricTags.LogLevel tag, int increment = 1) { @@ -950,91 +987,121 @@ public void RecordCountDirectLogApiErrors(Datadog.Trace.Telemetry.Metrics.Metric Interlocked.Add(ref _buffer.Count[index], increment); } + public void RecordCountDebuggerMemoryPressureTransitions(Datadog.Trace.Telemetry.Metrics.MetricTags.DebuggerMemoryPressureState tag1, Datadog.Trace.Telemetry.Metrics.MetricTags.DebuggerMemoryPressureTrigger tag2, int increment = 1) + { + var index = 539 + ((int)tag1 * 4) + (int)tag2; + Interlocked.Add(ref _buffer.Count[index], increment); + } + + public void RecordCountDebuggerMemoryPressureDisabled(Datadog.Trace.Telemetry.Metrics.MetricTags.DebuggerMemoryPressureDisabledReason tag, int increment = 1) + { + var index = 547 + (int)tag; + Interlocked.Add(ref _buffer.Count[index], increment); + } + + public void RecordCountDebuggerMemoryPressureMemoryUsagePct(Datadog.Trace.Telemetry.Metrics.MetricTags.DebuggerMemoryPressureState tag1, Datadog.Trace.Telemetry.Metrics.MetricTags.DebuggerMemoryPressureMemoryBucket tag2, int increment = 1) + { + var index = 549 + ((int)tag1 * 5) + (int)tag2; + Interlocked.Add(ref _buffer.Count[index], increment); + } + + public void RecordCountDebuggerMemoryPressureGcActivity(Datadog.Trace.Telemetry.Metrics.MetricTags.DebuggerMemoryPressureState tag1, Datadog.Trace.Telemetry.Metrics.MetricTags.DebuggerMemoryPressureGcBucket tag2, int increment = 1) + { + var index = 559 + ((int)tag1 * 4) + (int)tag2; + Interlocked.Add(ref _buffer.Count[index], increment); + } + + public void RecordCountDebuggerMemoryPressureDuration(Datadog.Trace.Telemetry.Metrics.MetricTags.DebuggerMemoryPressureDurationBucket tag, int increment = 1) + { + var index = 567 + (int)tag; + Interlocked.Add(ref _buffer.Count[index], increment); + } + public void RecordCountWafInit(Datadog.Trace.Telemetry.Metrics.MetricTags.WafStatus tag, int increment = 1) { - var index = 539 + (int)tag; + var index = 571 + (int)tag; Interlocked.Add(ref _buffer.Count[index], increment); } public void RecordCountWafUpdates(Datadog.Trace.Telemetry.Metrics.MetricTags.WafStatus tag, int increment = 1) { - var index = 541 + (int)tag; + var index = 573 + (int)tag; Interlocked.Add(ref _buffer.Count[index], increment); } public void RecordCountWafRequests(Datadog.Trace.Telemetry.Metrics.MetricTags.WafAnalysis tag, int increment = 1) { - var index = 543 + (int)tag; + var index = 575 + (int)tag; Interlocked.Add(ref _buffer.Count[index], increment); } public void RecordCountInputTruncated(Datadog.Trace.Telemetry.Metrics.MetricTags.TruncationReason tag, int increment = 1) { - var index = 551 + (int)tag; + var index = 583 + (int)tag; Interlocked.Add(ref _buffer.Count[index], increment); } public void RecordCountRaspRuleEval(Datadog.Trace.Telemetry.Metrics.MetricTags.RaspRuleType tag, int increment = 1) { - var index = 554 + (int)tag; + var index = 586 + (int)tag; Interlocked.Add(ref _buffer.Count[index], increment); } public void RecordCountRaspRuleMatch(Datadog.Trace.Telemetry.Metrics.MetricTags.RaspRuleTypeMatch tag, int increment = 1) { - var index = 559 + (int)tag; + var index = 591 + (int)tag; Interlocked.Add(ref _buffer.Count[index], increment); } public void RecordCountRaspTimeout(Datadog.Trace.Telemetry.Metrics.MetricTags.RaspRuleType tag, int increment = 1) { - var index = 574 + (int)tag; + var index = 606 + (int)tag; Interlocked.Add(ref _buffer.Count[index], increment); } public void RecordCountMissingUserId(Datadog.Trace.Telemetry.Metrics.MetricTags.AuthenticationFrameworkWithEventType tag, int increment = 1) { - var index = 579 + (int)tag; + var index = 611 + (int)tag; Interlocked.Add(ref _buffer.Count[index], increment); } public void RecordCountMissingUserLogin(Datadog.Trace.Telemetry.Metrics.MetricTags.AuthenticationFrameworkWithEventType tag, int increment = 1) { - var index = 583 + (int)tag; + var index = 615 + (int)tag; Interlocked.Add(ref _buffer.Count[index], increment); } public void RecordCountUserEventSdk(Datadog.Trace.Telemetry.Metrics.MetricTags.UserEventSdk tag, int increment = 1) { - var index = 587 + (int)tag; + var index = 619 + (int)tag; Interlocked.Add(ref _buffer.Count[index], increment); } public void RecordCountIastExecutedSources(Datadog.Trace.Telemetry.Metrics.MetricTags.IastSourceType tag, int increment = 1) { - var index = 592 + (int)tag; + var index = 624 + (int)tag; Interlocked.Add(ref _buffer.Count[index], increment); } public void RecordCountIastExecutedPropagations(int increment = 1) { - Interlocked.Add(ref _buffer.Count[606], increment); + Interlocked.Add(ref _buffer.Count[638], increment); } public void RecordCountIastExecutedSinks(Datadog.Trace.Telemetry.Metrics.MetricTags.IastVulnerabilityType tag, int increment = 1) { - var index = 607 + (int)tag; + var index = 639 + (int)tag; Interlocked.Add(ref _buffer.Count[index], increment); } public void RecordCountIastRequestTainted(int increment = 1) { - Interlocked.Add(ref _buffer.Count[634], increment); + Interlocked.Add(ref _buffer.Count[666], increment); } public void RecordCountIastSuppressedVulnerabilities(Datadog.Trace.Telemetry.Metrics.MetricTags.IastVulnerabilityType tag, int increment = 1) { - var index = 635 + (int)tag; + var index = 667 + (int)tag; Interlocked.Add(ref _buffer.Count[index], increment); } } \ No newline at end of file diff --git a/tracer/src/Datadog.Trace/Generated/net461/Datadog.Trace.SourceGenerators/TelemetryMetricGenerator/NullMetricsTelemetryCollector_Count.g.cs b/tracer/src/Datadog.Trace/Generated/net461/Datadog.Trace.SourceGenerators/TelemetryMetricGenerator/NullMetricsTelemetryCollector_Count.g.cs index b2aa00db73bb..4bfc229adb4e 100644 --- a/tracer/src/Datadog.Trace/Generated/net461/Datadog.Trace.SourceGenerators/TelemetryMetricGenerator/NullMetricsTelemetryCollector_Count.g.cs +++ b/tracer/src/Datadog.Trace/Generated/net461/Datadog.Trace.SourceGenerators/TelemetryMetricGenerator/NullMetricsTelemetryCollector_Count.g.cs @@ -156,6 +156,26 @@ public void RecordCountDirectLogApiErrors(Datadog.Trace.Telemetry.Metrics.Metric { } + public void RecordCountDebuggerMemoryPressureTransitions(Datadog.Trace.Telemetry.Metrics.MetricTags.DebuggerMemoryPressureState tag1, Datadog.Trace.Telemetry.Metrics.MetricTags.DebuggerMemoryPressureTrigger tag2, int increment = 1) + { + } + + public void RecordCountDebuggerMemoryPressureDisabled(Datadog.Trace.Telemetry.Metrics.MetricTags.DebuggerMemoryPressureDisabledReason tag, int increment = 1) + { + } + + public void RecordCountDebuggerMemoryPressureMemoryUsagePct(Datadog.Trace.Telemetry.Metrics.MetricTags.DebuggerMemoryPressureState tag1, Datadog.Trace.Telemetry.Metrics.MetricTags.DebuggerMemoryPressureMemoryBucket tag2, int increment = 1) + { + } + + public void RecordCountDebuggerMemoryPressureGcActivity(Datadog.Trace.Telemetry.Metrics.MetricTags.DebuggerMemoryPressureState tag1, Datadog.Trace.Telemetry.Metrics.MetricTags.DebuggerMemoryPressureGcBucket tag2, int increment = 1) + { + } + + public void RecordCountDebuggerMemoryPressureDuration(Datadog.Trace.Telemetry.Metrics.MetricTags.DebuggerMemoryPressureDurationBucket tag, int increment = 1) + { + } + public void RecordCountWafInit(Datadog.Trace.Telemetry.Metrics.MetricTags.WafStatus tag, int increment = 1) { } diff --git a/tracer/src/Datadog.Trace/Generated/net6.0/Datadog.Trace.SourceGenerators/TelemetryMetricGenerator/CiVisibilityMetricsTelemetryCollector_Count.g.cs b/tracer/src/Datadog.Trace/Generated/net6.0/Datadog.Trace.SourceGenerators/TelemetryMetricGenerator/CiVisibilityMetricsTelemetryCollector_Count.g.cs index 8c6817ebca98..b2cd615aef09 100644 --- a/tracer/src/Datadog.Trace/Generated/net6.0/Datadog.Trace.SourceGenerators/TelemetryMetricGenerator/CiVisibilityMetricsTelemetryCollector_Count.g.cs +++ b/tracer/src/Datadog.Trace/Generated/net6.0/Datadog.Trace.SourceGenerators/TelemetryMetricGenerator/CiVisibilityMetricsTelemetryCollector_Count.g.cs @@ -156,6 +156,26 @@ public void RecordCountDirectLogApiErrors(Datadog.Trace.Telemetry.Metrics.Metric { } + public void RecordCountDebuggerMemoryPressureTransitions(Datadog.Trace.Telemetry.Metrics.MetricTags.DebuggerMemoryPressureState tag1, Datadog.Trace.Telemetry.Metrics.MetricTags.DebuggerMemoryPressureTrigger tag2, int increment = 1) + { + } + + public void RecordCountDebuggerMemoryPressureDisabled(Datadog.Trace.Telemetry.Metrics.MetricTags.DebuggerMemoryPressureDisabledReason tag, int increment = 1) + { + } + + public void RecordCountDebuggerMemoryPressureMemoryUsagePct(Datadog.Trace.Telemetry.Metrics.MetricTags.DebuggerMemoryPressureState tag1, Datadog.Trace.Telemetry.Metrics.MetricTags.DebuggerMemoryPressureMemoryBucket tag2, int increment = 1) + { + } + + public void RecordCountDebuggerMemoryPressureGcActivity(Datadog.Trace.Telemetry.Metrics.MetricTags.DebuggerMemoryPressureState tag1, Datadog.Trace.Telemetry.Metrics.MetricTags.DebuggerMemoryPressureGcBucket tag2, int increment = 1) + { + } + + public void RecordCountDebuggerMemoryPressureDuration(Datadog.Trace.Telemetry.Metrics.MetricTags.DebuggerMemoryPressureDurationBucket tag, int increment = 1) + { + } + public void RecordCountWafInit(Datadog.Trace.Telemetry.Metrics.MetricTags.WafStatus tag, int increment = 1) { } diff --git a/tracer/src/Datadog.Trace/Generated/net6.0/Datadog.Trace.SourceGenerators/TelemetryMetricGenerator/CountExtensions.g.cs b/tracer/src/Datadog.Trace/Generated/net6.0/Datadog.Trace.SourceGenerators/TelemetryMetricGenerator/CountExtensions.g.cs index 7078b0d04857..f1a010f936f4 100644 --- a/tracer/src/Datadog.Trace/Generated/net6.0/Datadog.Trace.SourceGenerators/TelemetryMetricGenerator/CountExtensions.g.cs +++ b/tracer/src/Datadog.Trace/Generated/net6.0/Datadog.Trace.SourceGenerators/TelemetryMetricGenerator/CountExtensions.g.cs @@ -12,7 +12,7 @@ internal static partial class CountExtensions /// /// The number of separate metrics in the metric. /// - public const int Length = 51; + public const int Length = 56; /// /// Gets the metric name for the provided metric @@ -58,6 +58,11 @@ public static string GetName(this Datadog.Trace.Telemetry.Metrics.Count metric) Datadog.Trace.Telemetry.Metrics.Count.DirectLogApiRequests => "direct_log_api.requests", Datadog.Trace.Telemetry.Metrics.Count.DirectLogApiResponses => "direct_log_api.responses", Datadog.Trace.Telemetry.Metrics.Count.DirectLogApiErrors => "direct_log_api.errors", + Datadog.Trace.Telemetry.Metrics.Count.DebuggerMemoryPressureTransitions => "memory_pressure.transitions", + Datadog.Trace.Telemetry.Metrics.Count.DebuggerMemoryPressureDisabled => "memory_pressure.disabled", + Datadog.Trace.Telemetry.Metrics.Count.DebuggerMemoryPressureMemoryUsagePct => "memory_pressure.memory_usage_pct", + Datadog.Trace.Telemetry.Metrics.Count.DebuggerMemoryPressureGcActivity => "memory_pressure.gc_activity", + Datadog.Trace.Telemetry.Metrics.Count.DebuggerMemoryPressureDuration => "memory_pressure.duration", Datadog.Trace.Telemetry.Metrics.Count.WafInit => "waf.init", Datadog.Trace.Telemetry.Metrics.Count.WafUpdates => "waf.updates", Datadog.Trace.Telemetry.Metrics.Count.WafRequests => "waf.requests", @@ -111,6 +116,11 @@ public static bool IsCommon(this Datadog.Trace.Telemetry.Metrics.Count metric) Datadog.Trace.Telemetry.Metrics.Count.TelemetryApiRequests => "telemetry", Datadog.Trace.Telemetry.Metrics.Count.TelemetryApiResponses => "telemetry", Datadog.Trace.Telemetry.Metrics.Count.TelemetryApiErrors => "telemetry", + Datadog.Trace.Telemetry.Metrics.Count.DebuggerMemoryPressureTransitions => "live_debugger", + Datadog.Trace.Telemetry.Metrics.Count.DebuggerMemoryPressureDisabled => "live_debugger", + Datadog.Trace.Telemetry.Metrics.Count.DebuggerMemoryPressureMemoryUsagePct => "live_debugger", + Datadog.Trace.Telemetry.Metrics.Count.DebuggerMemoryPressureGcActivity => "live_debugger", + Datadog.Trace.Telemetry.Metrics.Count.DebuggerMemoryPressureDuration => "live_debugger", Datadog.Trace.Telemetry.Metrics.Count.WafInit => "appsec", Datadog.Trace.Telemetry.Metrics.Count.WafUpdates => "appsec", Datadog.Trace.Telemetry.Metrics.Count.WafRequests => "appsec", diff --git a/tracer/src/Datadog.Trace/Generated/net6.0/Datadog.Trace.SourceGenerators/TelemetryMetricGenerator/IMetricsTelemetryCollector_Count.g.cs b/tracer/src/Datadog.Trace/Generated/net6.0/Datadog.Trace.SourceGenerators/TelemetryMetricGenerator/IMetricsTelemetryCollector_Count.g.cs index 0a4e7e205295..85f010d8084a 100644 --- a/tracer/src/Datadog.Trace/Generated/net6.0/Datadog.Trace.SourceGenerators/TelemetryMetricGenerator/IMetricsTelemetryCollector_Count.g.cs +++ b/tracer/src/Datadog.Trace/Generated/net6.0/Datadog.Trace.SourceGenerators/TelemetryMetricGenerator/IMetricsTelemetryCollector_Count.g.cs @@ -81,6 +81,16 @@ internal partial interface IMetricsTelemetryCollector public void RecordCountDirectLogApiErrors(Datadog.Trace.Telemetry.Metrics.MetricTags.ApiError tag, int increment = 1); + public void RecordCountDebuggerMemoryPressureTransitions(Datadog.Trace.Telemetry.Metrics.MetricTags.DebuggerMemoryPressureState tag1, Datadog.Trace.Telemetry.Metrics.MetricTags.DebuggerMemoryPressureTrigger tag2, int increment = 1); + + public void RecordCountDebuggerMemoryPressureDisabled(Datadog.Trace.Telemetry.Metrics.MetricTags.DebuggerMemoryPressureDisabledReason tag, int increment = 1); + + public void RecordCountDebuggerMemoryPressureMemoryUsagePct(Datadog.Trace.Telemetry.Metrics.MetricTags.DebuggerMemoryPressureState tag1, Datadog.Trace.Telemetry.Metrics.MetricTags.DebuggerMemoryPressureMemoryBucket tag2, int increment = 1); + + public void RecordCountDebuggerMemoryPressureGcActivity(Datadog.Trace.Telemetry.Metrics.MetricTags.DebuggerMemoryPressureState tag1, Datadog.Trace.Telemetry.Metrics.MetricTags.DebuggerMemoryPressureGcBucket tag2, int increment = 1); + + public void RecordCountDebuggerMemoryPressureDuration(Datadog.Trace.Telemetry.Metrics.MetricTags.DebuggerMemoryPressureDurationBucket tag, int increment = 1); + public void RecordCountWafInit(Datadog.Trace.Telemetry.Metrics.MetricTags.WafStatus tag, int increment = 1); public void RecordCountWafUpdates(Datadog.Trace.Telemetry.Metrics.MetricTags.WafStatus tag, int increment = 1); diff --git a/tracer/src/Datadog.Trace/Generated/net6.0/Datadog.Trace.SourceGenerators/TelemetryMetricGenerator/MetricsTelemetryCollector_Count.g.cs b/tracer/src/Datadog.Trace/Generated/net6.0/Datadog.Trace.SourceGenerators/TelemetryMetricGenerator/MetricsTelemetryCollector_Count.g.cs index 5f507e7f2e88..0a01025405c7 100644 --- a/tracer/src/Datadog.Trace/Generated/net6.0/Datadog.Trace.SourceGenerators/TelemetryMetricGenerator/MetricsTelemetryCollector_Count.g.cs +++ b/tracer/src/Datadog.Trace/Generated/net6.0/Datadog.Trace.SourceGenerators/TelemetryMetricGenerator/MetricsTelemetryCollector_Count.g.cs @@ -11,7 +11,7 @@ namespace Datadog.Trace.Telemetry; internal sealed partial class MetricsTelemetryCollector { - private const int CountLength = 662; + private const int CountLength = 694; /// /// Creates the buffer for the values. @@ -594,13 +594,50 @@ private static AggregatedMetric[] GetCountBuffer() new(new[] { "type:timeout" }), new(new[] { "type:network" }), new(new[] { "type:status_code" }), - // waf.init, index = 539 + // memory_pressure.transitions, index = 539 + new(new[] { "state:enter", "trigger:none" }), + new(new[] { "state:enter", "trigger:memory" }), + new(new[] { "state:enter", "trigger:gc" }), + new(new[] { "state:enter", "trigger:both" }), + new(new[] { "state:exit", "trigger:none" }), + new(new[] { "state:exit", "trigger:memory" }), + new(new[] { "state:exit", "trigger:gc" }), + new(new[] { "state:exit", "trigger:both" }), + // memory_pressure.disabled, index = 547 + new(new[] { "reason:no_signals" }), + new(new[] { "reason:error" }), + // memory_pressure.memory_usage_pct, index = 549 + new(new[] { "state:enter", "bucket:lt_70" }), + new(new[] { "state:enter", "bucket:70_80" }), + new(new[] { "state:enter", "bucket:80_85" }), + new(new[] { "state:enter", "bucket:85_90" }), + new(new[] { "state:enter", "bucket:gte_90" }), + new(new[] { "state:exit", "bucket:lt_70" }), + new(new[] { "state:exit", "bucket:70_80" }), + new(new[] { "state:exit", "bucket:80_85" }), + new(new[] { "state:exit", "bucket:85_90" }), + new(new[] { "state:exit", "bucket:gte_90" }), + // memory_pressure.gc_activity, index = 559 + new(new[] { "state:enter", "bucket:lt_1" }), + new(new[] { "state:enter", "bucket:1_2" }), + new(new[] { "state:enter", "bucket:2_5" }), + new(new[] { "state:enter", "bucket:gte_5" }), + new(new[] { "state:exit", "bucket:lt_1" }), + new(new[] { "state:exit", "bucket:1_2" }), + new(new[] { "state:exit", "bucket:2_5" }), + new(new[] { "state:exit", "bucket:gte_5" }), + // memory_pressure.duration, index = 567 + new(new[] { "bucket:lt_1s" }), + new(new[] { "bucket:1_5s" }), + new(new[] { "bucket:5_30s" }), + new(new[] { "bucket:gte_30s" }), + // waf.init, index = 571 new(new[] { "waf_version", "event_rules_version", "success:true" }), new(new[] { "waf_version", "event_rules_version", "success:false" }), - // waf.updates, index = 541 + // waf.updates, index = 573 new(new[] { "waf_version", "event_rules_version", "success:true" }), new(new[] { "waf_version", "event_rules_version", "success:false" }), - // waf.requests, index = 543 + // waf.requests, index = 575 new(new[] { "waf_version", "event_rules_version", "rule_triggered:false", "request_blocked:false", "waf_timeout:false", "block_failure:false", "rate_limited:false", "input_truncated:false" }), new(new[] { "waf_version", "event_rules_version", "rule_triggered:true", "request_blocked:false", "waf_timeout:false", "block_failure:false", "rate_limited:false", "input_truncated:false" }), new(new[] { "waf_version", "event_rules_version", "rule_triggered:true", "request_blocked:true", "waf_timeout:false", "block_failure:false", "rate_limited:false", "input_truncated:false" }), @@ -609,17 +646,17 @@ private static AggregatedMetric[] GetCountBuffer() new(new[] { "waf_version", "event_rules_version", "rule_triggered:true", "request_blocked:false", "waf_timeout:false", "block_failure:false", "rate_limited:false", "input_truncated:true" }), new(new[] { "waf_version", "event_rules_version", "rule_triggered:true", "request_blocked:true", "waf_timeout:false", "block_failure:false", "rate_limited:false", "input_truncated:true" }), new(new[] { "waf_version", "event_rules_version", "rule_triggered:false", "request_blocked:false", "waf_timeout:true", "block_failure:false", "rate_limited:false", "input_truncated:true" }), - // waf.input_truncated, index = 551 + // waf.input_truncated, index = 583 new(new[] { "truncation_reason:string_too_long" }), new(new[] { "truncation_reason:list_or_map_too_large" }), new(new[] { "truncation_reason:object_too_deep" }), - // rasp.rule.eval, index = 554 + // rasp.rule.eval, index = 586 new(new[] { "waf_version", "event_rules_version", "rule_type:lfi" }), new(new[] { "waf_version", "event_rules_version", "rule_type:ssrf" }), new(new[] { "waf_version", "event_rules_version", "rule_type:sql_injection" }), new(new[] { "waf_version", "event_rules_version", "rule_type:command_injection", "rule_variant:shell" }), new(new[] { "waf_version", "event_rules_version", "rule_type:command_injection", "rule_variant:exec" }), - // rasp.rule.match, index = 559 + // rasp.rule.match, index = 591 new(new[] { "waf_version", "event_rules_version", "block:success", "rule_type:lfi" }), new(new[] { "waf_version", "event_rules_version", "block:success", "rule_type:ssrf" }), new(new[] { "waf_version", "event_rules_version", "block:success", "rule_type:sql_injection" }), @@ -635,29 +672,29 @@ private static AggregatedMetric[] GetCountBuffer() new(new[] { "waf_version", "event_rules_version", "block:irrelevant", "rule_type:sql_injection" }), new(new[] { "waf_version", "event_rules_version", "block:irrelevant", "rule_type:command_injection", "rule_variant:shell" }), new(new[] { "waf_version", "event_rules_version", "block:irrelevant", "rule_type:command_injection", "rule_variant:exec" }), - // rasp.timeout, index = 574 + // rasp.timeout, index = 606 new(new[] { "waf_version", "event_rules_version", "rule_type:lfi" }), new(new[] { "waf_version", "event_rules_version", "rule_type:ssrf" }), new(new[] { "waf_version", "event_rules_version", "rule_type:sql_injection" }), new(new[] { "waf_version", "event_rules_version", "rule_type:command_injection", "rule_variant:shell" }), new(new[] { "waf_version", "event_rules_version", "rule_type:command_injection", "rule_variant:exec" }), - // instrum.user_auth.missing_user_id, index = 579 + // instrum.user_auth.missing_user_id, index = 611 new(new[] { "framework:aspnetcore_identity", "event_type:login_success" }), new(new[] { "framework:aspnetcore_identity", "event_type:login_failure" }), new(new[] { "framework:aspnetcore_identity", "event_type:signup" }), new(new[] { "framework:unknown", "event_type:signup" }), - // instrum.user_auth.missing_user_login, index = 583 + // instrum.user_auth.missing_user_login, index = 615 new(new[] { "framework:aspnetcore_identity", "event_type:login_success" }), new(new[] { "framework:aspnetcore_identity", "event_type:login_failure" }), new(new[] { "framework:aspnetcore_identity", "event_type:signup" }), new(new[] { "framework:unknown", "event_type:signup" }), - // sdk.event, index = 587 + // sdk.event, index = 619 new(new[] { "event_type:login_success", "sdk_version:v1" }), new(new[] { "event_type:login_success", "sdk_version:v2" }), new(new[] { "event_type:login_failure", "sdk_version:v1" }), new(new[] { "event_type:login_failure", "sdk_version:v2" }), new(new[] { "event_type:custom", "sdk_version:v1" }), - // executed.source, index = 592 + // executed.source, index = 624 new(new[] { "source_type:http.request.body" }), new(new[] { "source_type:http.request.path" }), new(new[] { "source_type:http.request.parameter.name" }), @@ -672,9 +709,9 @@ private static AggregatedMetric[] GetCountBuffer() new(new[] { "source_type:http.request.uri" }), new(new[] { "source_type:grpc.request.body" }), new(new[] { "source_type:sql.row.value" }), - // executed.propagation, index = 606 + // executed.propagation, index = 638 new(null), - // executed.sink, index = 607 + // executed.sink, index = 639 new(new[] { "vulnerability_type:none" }), new(new[] { "vulnerability_type:weak_cipher" }), new(new[] { "vulnerability_type:weak_hash" }), @@ -702,9 +739,9 @@ private static AggregatedMetric[] GetCountBuffer() new(new[] { "vulnerability_type:directory_listing_leak" }), new(new[] { "vulnerability_type:session_timeout" }), new(new[] { "vulnerability_type:email_html_injection" }), - // request.tainted, index = 634 + // request.tainted, index = 666 new(null), - // suppressed.vulnerabilities, index = 635 + // suppressed.vulnerabilities, index = 667 new(new[] { "vulnerability_type:none" }), new(new[] { "vulnerability_type:weak_cipher" }), new(new[] { "vulnerability_type:weak_hash" }), @@ -740,7 +777,7 @@ private static AggregatedMetric[] GetCountBuffer() /// It is equal to the cardinality of the tag combinations (or 1 if there are no tags) /// private static int[] CountEntryCounts { get; } - = new int[]{ 4, 85, 1, 3, 5, 2, 2, 5, 1, 1, 1, 22, 3, 2, 5, 5, 4, 1, 1, 22, 3, 90, 90, 4, 4, 4, 4, 2, 44, 6, 1, 1, 85, 1, 22, 3, 2, 2, 8, 3, 5, 15, 5, 4, 4, 5, 14, 1, 27, 1, 27, }; + = new int[]{ 4, 85, 1, 3, 5, 2, 2, 5, 1, 1, 1, 22, 3, 2, 5, 5, 4, 1, 1, 22, 3, 90, 90, 4, 4, 4, 4, 2, 44, 6, 1, 1, 85, 1, 22, 3, 8, 2, 10, 8, 4, 2, 2, 8, 3, 5, 15, 5, 4, 4, 5, 14, 1, 27, 1, 27, }; public void RecordCountLogCreated(Datadog.Trace.Telemetry.Metrics.MetricTags.LogLevel tag, int increment = 1) { @@ -950,91 +987,121 @@ public void RecordCountDirectLogApiErrors(Datadog.Trace.Telemetry.Metrics.Metric Interlocked.Add(ref _buffer.Count[index], increment); } + public void RecordCountDebuggerMemoryPressureTransitions(Datadog.Trace.Telemetry.Metrics.MetricTags.DebuggerMemoryPressureState tag1, Datadog.Trace.Telemetry.Metrics.MetricTags.DebuggerMemoryPressureTrigger tag2, int increment = 1) + { + var index = 539 + ((int)tag1 * 4) + (int)tag2; + Interlocked.Add(ref _buffer.Count[index], increment); + } + + public void RecordCountDebuggerMemoryPressureDisabled(Datadog.Trace.Telemetry.Metrics.MetricTags.DebuggerMemoryPressureDisabledReason tag, int increment = 1) + { + var index = 547 + (int)tag; + Interlocked.Add(ref _buffer.Count[index], increment); + } + + public void RecordCountDebuggerMemoryPressureMemoryUsagePct(Datadog.Trace.Telemetry.Metrics.MetricTags.DebuggerMemoryPressureState tag1, Datadog.Trace.Telemetry.Metrics.MetricTags.DebuggerMemoryPressureMemoryBucket tag2, int increment = 1) + { + var index = 549 + ((int)tag1 * 5) + (int)tag2; + Interlocked.Add(ref _buffer.Count[index], increment); + } + + public void RecordCountDebuggerMemoryPressureGcActivity(Datadog.Trace.Telemetry.Metrics.MetricTags.DebuggerMemoryPressureState tag1, Datadog.Trace.Telemetry.Metrics.MetricTags.DebuggerMemoryPressureGcBucket tag2, int increment = 1) + { + var index = 559 + ((int)tag1 * 4) + (int)tag2; + Interlocked.Add(ref _buffer.Count[index], increment); + } + + public void RecordCountDebuggerMemoryPressureDuration(Datadog.Trace.Telemetry.Metrics.MetricTags.DebuggerMemoryPressureDurationBucket tag, int increment = 1) + { + var index = 567 + (int)tag; + Interlocked.Add(ref _buffer.Count[index], increment); + } + public void RecordCountWafInit(Datadog.Trace.Telemetry.Metrics.MetricTags.WafStatus tag, int increment = 1) { - var index = 539 + (int)tag; + var index = 571 + (int)tag; Interlocked.Add(ref _buffer.Count[index], increment); } public void RecordCountWafUpdates(Datadog.Trace.Telemetry.Metrics.MetricTags.WafStatus tag, int increment = 1) { - var index = 541 + (int)tag; + var index = 573 + (int)tag; Interlocked.Add(ref _buffer.Count[index], increment); } public void RecordCountWafRequests(Datadog.Trace.Telemetry.Metrics.MetricTags.WafAnalysis tag, int increment = 1) { - var index = 543 + (int)tag; + var index = 575 + (int)tag; Interlocked.Add(ref _buffer.Count[index], increment); } public void RecordCountInputTruncated(Datadog.Trace.Telemetry.Metrics.MetricTags.TruncationReason tag, int increment = 1) { - var index = 551 + (int)tag; + var index = 583 + (int)tag; Interlocked.Add(ref _buffer.Count[index], increment); } public void RecordCountRaspRuleEval(Datadog.Trace.Telemetry.Metrics.MetricTags.RaspRuleType tag, int increment = 1) { - var index = 554 + (int)tag; + var index = 586 + (int)tag; Interlocked.Add(ref _buffer.Count[index], increment); } public void RecordCountRaspRuleMatch(Datadog.Trace.Telemetry.Metrics.MetricTags.RaspRuleTypeMatch tag, int increment = 1) { - var index = 559 + (int)tag; + var index = 591 + (int)tag; Interlocked.Add(ref _buffer.Count[index], increment); } public void RecordCountRaspTimeout(Datadog.Trace.Telemetry.Metrics.MetricTags.RaspRuleType tag, int increment = 1) { - var index = 574 + (int)tag; + var index = 606 + (int)tag; Interlocked.Add(ref _buffer.Count[index], increment); } public void RecordCountMissingUserId(Datadog.Trace.Telemetry.Metrics.MetricTags.AuthenticationFrameworkWithEventType tag, int increment = 1) { - var index = 579 + (int)tag; + var index = 611 + (int)tag; Interlocked.Add(ref _buffer.Count[index], increment); } public void RecordCountMissingUserLogin(Datadog.Trace.Telemetry.Metrics.MetricTags.AuthenticationFrameworkWithEventType tag, int increment = 1) { - var index = 583 + (int)tag; + var index = 615 + (int)tag; Interlocked.Add(ref _buffer.Count[index], increment); } public void RecordCountUserEventSdk(Datadog.Trace.Telemetry.Metrics.MetricTags.UserEventSdk tag, int increment = 1) { - var index = 587 + (int)tag; + var index = 619 + (int)tag; Interlocked.Add(ref _buffer.Count[index], increment); } public void RecordCountIastExecutedSources(Datadog.Trace.Telemetry.Metrics.MetricTags.IastSourceType tag, int increment = 1) { - var index = 592 + (int)tag; + var index = 624 + (int)tag; Interlocked.Add(ref _buffer.Count[index], increment); } public void RecordCountIastExecutedPropagations(int increment = 1) { - Interlocked.Add(ref _buffer.Count[606], increment); + Interlocked.Add(ref _buffer.Count[638], increment); } public void RecordCountIastExecutedSinks(Datadog.Trace.Telemetry.Metrics.MetricTags.IastVulnerabilityType tag, int increment = 1) { - var index = 607 + (int)tag; + var index = 639 + (int)tag; Interlocked.Add(ref _buffer.Count[index], increment); } public void RecordCountIastRequestTainted(int increment = 1) { - Interlocked.Add(ref _buffer.Count[634], increment); + Interlocked.Add(ref _buffer.Count[666], increment); } public void RecordCountIastSuppressedVulnerabilities(Datadog.Trace.Telemetry.Metrics.MetricTags.IastVulnerabilityType tag, int increment = 1) { - var index = 635 + (int)tag; + var index = 667 + (int)tag; Interlocked.Add(ref _buffer.Count[index], increment); } } \ No newline at end of file diff --git a/tracer/src/Datadog.Trace/Generated/net6.0/Datadog.Trace.SourceGenerators/TelemetryMetricGenerator/NullMetricsTelemetryCollector_Count.g.cs b/tracer/src/Datadog.Trace/Generated/net6.0/Datadog.Trace.SourceGenerators/TelemetryMetricGenerator/NullMetricsTelemetryCollector_Count.g.cs index b2aa00db73bb..4bfc229adb4e 100644 --- a/tracer/src/Datadog.Trace/Generated/net6.0/Datadog.Trace.SourceGenerators/TelemetryMetricGenerator/NullMetricsTelemetryCollector_Count.g.cs +++ b/tracer/src/Datadog.Trace/Generated/net6.0/Datadog.Trace.SourceGenerators/TelemetryMetricGenerator/NullMetricsTelemetryCollector_Count.g.cs @@ -156,6 +156,26 @@ public void RecordCountDirectLogApiErrors(Datadog.Trace.Telemetry.Metrics.Metric { } + public void RecordCountDebuggerMemoryPressureTransitions(Datadog.Trace.Telemetry.Metrics.MetricTags.DebuggerMemoryPressureState tag1, Datadog.Trace.Telemetry.Metrics.MetricTags.DebuggerMemoryPressureTrigger tag2, int increment = 1) + { + } + + public void RecordCountDebuggerMemoryPressureDisabled(Datadog.Trace.Telemetry.Metrics.MetricTags.DebuggerMemoryPressureDisabledReason tag, int increment = 1) + { + } + + public void RecordCountDebuggerMemoryPressureMemoryUsagePct(Datadog.Trace.Telemetry.Metrics.MetricTags.DebuggerMemoryPressureState tag1, Datadog.Trace.Telemetry.Metrics.MetricTags.DebuggerMemoryPressureMemoryBucket tag2, int increment = 1) + { + } + + public void RecordCountDebuggerMemoryPressureGcActivity(Datadog.Trace.Telemetry.Metrics.MetricTags.DebuggerMemoryPressureState tag1, Datadog.Trace.Telemetry.Metrics.MetricTags.DebuggerMemoryPressureGcBucket tag2, int increment = 1) + { + } + + public void RecordCountDebuggerMemoryPressureDuration(Datadog.Trace.Telemetry.Metrics.MetricTags.DebuggerMemoryPressureDurationBucket tag, int increment = 1) + { + } + public void RecordCountWafInit(Datadog.Trace.Telemetry.Metrics.MetricTags.WafStatus tag, int increment = 1) { } diff --git a/tracer/src/Datadog.Trace/Generated/netcoreapp3.1/Datadog.Trace.SourceGenerators/TelemetryMetricGenerator/CiVisibilityMetricsTelemetryCollector_Count.g.cs b/tracer/src/Datadog.Trace/Generated/netcoreapp3.1/Datadog.Trace.SourceGenerators/TelemetryMetricGenerator/CiVisibilityMetricsTelemetryCollector_Count.g.cs index 8c6817ebca98..b2cd615aef09 100644 --- a/tracer/src/Datadog.Trace/Generated/netcoreapp3.1/Datadog.Trace.SourceGenerators/TelemetryMetricGenerator/CiVisibilityMetricsTelemetryCollector_Count.g.cs +++ b/tracer/src/Datadog.Trace/Generated/netcoreapp3.1/Datadog.Trace.SourceGenerators/TelemetryMetricGenerator/CiVisibilityMetricsTelemetryCollector_Count.g.cs @@ -156,6 +156,26 @@ public void RecordCountDirectLogApiErrors(Datadog.Trace.Telemetry.Metrics.Metric { } + public void RecordCountDebuggerMemoryPressureTransitions(Datadog.Trace.Telemetry.Metrics.MetricTags.DebuggerMemoryPressureState tag1, Datadog.Trace.Telemetry.Metrics.MetricTags.DebuggerMemoryPressureTrigger tag2, int increment = 1) + { + } + + public void RecordCountDebuggerMemoryPressureDisabled(Datadog.Trace.Telemetry.Metrics.MetricTags.DebuggerMemoryPressureDisabledReason tag, int increment = 1) + { + } + + public void RecordCountDebuggerMemoryPressureMemoryUsagePct(Datadog.Trace.Telemetry.Metrics.MetricTags.DebuggerMemoryPressureState tag1, Datadog.Trace.Telemetry.Metrics.MetricTags.DebuggerMemoryPressureMemoryBucket tag2, int increment = 1) + { + } + + public void RecordCountDebuggerMemoryPressureGcActivity(Datadog.Trace.Telemetry.Metrics.MetricTags.DebuggerMemoryPressureState tag1, Datadog.Trace.Telemetry.Metrics.MetricTags.DebuggerMemoryPressureGcBucket tag2, int increment = 1) + { + } + + public void RecordCountDebuggerMemoryPressureDuration(Datadog.Trace.Telemetry.Metrics.MetricTags.DebuggerMemoryPressureDurationBucket tag, int increment = 1) + { + } + public void RecordCountWafInit(Datadog.Trace.Telemetry.Metrics.MetricTags.WafStatus tag, int increment = 1) { } diff --git a/tracer/src/Datadog.Trace/Generated/netcoreapp3.1/Datadog.Trace.SourceGenerators/TelemetryMetricGenerator/CountExtensions.g.cs b/tracer/src/Datadog.Trace/Generated/netcoreapp3.1/Datadog.Trace.SourceGenerators/TelemetryMetricGenerator/CountExtensions.g.cs index 7078b0d04857..f1a010f936f4 100644 --- a/tracer/src/Datadog.Trace/Generated/netcoreapp3.1/Datadog.Trace.SourceGenerators/TelemetryMetricGenerator/CountExtensions.g.cs +++ b/tracer/src/Datadog.Trace/Generated/netcoreapp3.1/Datadog.Trace.SourceGenerators/TelemetryMetricGenerator/CountExtensions.g.cs @@ -12,7 +12,7 @@ internal static partial class CountExtensions /// /// The number of separate metrics in the metric. /// - public const int Length = 51; + public const int Length = 56; /// /// Gets the metric name for the provided metric @@ -58,6 +58,11 @@ public static string GetName(this Datadog.Trace.Telemetry.Metrics.Count metric) Datadog.Trace.Telemetry.Metrics.Count.DirectLogApiRequests => "direct_log_api.requests", Datadog.Trace.Telemetry.Metrics.Count.DirectLogApiResponses => "direct_log_api.responses", Datadog.Trace.Telemetry.Metrics.Count.DirectLogApiErrors => "direct_log_api.errors", + Datadog.Trace.Telemetry.Metrics.Count.DebuggerMemoryPressureTransitions => "memory_pressure.transitions", + Datadog.Trace.Telemetry.Metrics.Count.DebuggerMemoryPressureDisabled => "memory_pressure.disabled", + Datadog.Trace.Telemetry.Metrics.Count.DebuggerMemoryPressureMemoryUsagePct => "memory_pressure.memory_usage_pct", + Datadog.Trace.Telemetry.Metrics.Count.DebuggerMemoryPressureGcActivity => "memory_pressure.gc_activity", + Datadog.Trace.Telemetry.Metrics.Count.DebuggerMemoryPressureDuration => "memory_pressure.duration", Datadog.Trace.Telemetry.Metrics.Count.WafInit => "waf.init", Datadog.Trace.Telemetry.Metrics.Count.WafUpdates => "waf.updates", Datadog.Trace.Telemetry.Metrics.Count.WafRequests => "waf.requests", @@ -111,6 +116,11 @@ public static bool IsCommon(this Datadog.Trace.Telemetry.Metrics.Count metric) Datadog.Trace.Telemetry.Metrics.Count.TelemetryApiRequests => "telemetry", Datadog.Trace.Telemetry.Metrics.Count.TelemetryApiResponses => "telemetry", Datadog.Trace.Telemetry.Metrics.Count.TelemetryApiErrors => "telemetry", + Datadog.Trace.Telemetry.Metrics.Count.DebuggerMemoryPressureTransitions => "live_debugger", + Datadog.Trace.Telemetry.Metrics.Count.DebuggerMemoryPressureDisabled => "live_debugger", + Datadog.Trace.Telemetry.Metrics.Count.DebuggerMemoryPressureMemoryUsagePct => "live_debugger", + Datadog.Trace.Telemetry.Metrics.Count.DebuggerMemoryPressureGcActivity => "live_debugger", + Datadog.Trace.Telemetry.Metrics.Count.DebuggerMemoryPressureDuration => "live_debugger", Datadog.Trace.Telemetry.Metrics.Count.WafInit => "appsec", Datadog.Trace.Telemetry.Metrics.Count.WafUpdates => "appsec", Datadog.Trace.Telemetry.Metrics.Count.WafRequests => "appsec", diff --git a/tracer/src/Datadog.Trace/Generated/netcoreapp3.1/Datadog.Trace.SourceGenerators/TelemetryMetricGenerator/IMetricsTelemetryCollector_Count.g.cs b/tracer/src/Datadog.Trace/Generated/netcoreapp3.1/Datadog.Trace.SourceGenerators/TelemetryMetricGenerator/IMetricsTelemetryCollector_Count.g.cs index 0a4e7e205295..85f010d8084a 100644 --- a/tracer/src/Datadog.Trace/Generated/netcoreapp3.1/Datadog.Trace.SourceGenerators/TelemetryMetricGenerator/IMetricsTelemetryCollector_Count.g.cs +++ b/tracer/src/Datadog.Trace/Generated/netcoreapp3.1/Datadog.Trace.SourceGenerators/TelemetryMetricGenerator/IMetricsTelemetryCollector_Count.g.cs @@ -81,6 +81,16 @@ internal partial interface IMetricsTelemetryCollector public void RecordCountDirectLogApiErrors(Datadog.Trace.Telemetry.Metrics.MetricTags.ApiError tag, int increment = 1); + public void RecordCountDebuggerMemoryPressureTransitions(Datadog.Trace.Telemetry.Metrics.MetricTags.DebuggerMemoryPressureState tag1, Datadog.Trace.Telemetry.Metrics.MetricTags.DebuggerMemoryPressureTrigger tag2, int increment = 1); + + public void RecordCountDebuggerMemoryPressureDisabled(Datadog.Trace.Telemetry.Metrics.MetricTags.DebuggerMemoryPressureDisabledReason tag, int increment = 1); + + public void RecordCountDebuggerMemoryPressureMemoryUsagePct(Datadog.Trace.Telemetry.Metrics.MetricTags.DebuggerMemoryPressureState tag1, Datadog.Trace.Telemetry.Metrics.MetricTags.DebuggerMemoryPressureMemoryBucket tag2, int increment = 1); + + public void RecordCountDebuggerMemoryPressureGcActivity(Datadog.Trace.Telemetry.Metrics.MetricTags.DebuggerMemoryPressureState tag1, Datadog.Trace.Telemetry.Metrics.MetricTags.DebuggerMemoryPressureGcBucket tag2, int increment = 1); + + public void RecordCountDebuggerMemoryPressureDuration(Datadog.Trace.Telemetry.Metrics.MetricTags.DebuggerMemoryPressureDurationBucket tag, int increment = 1); + public void RecordCountWafInit(Datadog.Trace.Telemetry.Metrics.MetricTags.WafStatus tag, int increment = 1); public void RecordCountWafUpdates(Datadog.Trace.Telemetry.Metrics.MetricTags.WafStatus tag, int increment = 1); diff --git a/tracer/src/Datadog.Trace/Generated/netcoreapp3.1/Datadog.Trace.SourceGenerators/TelemetryMetricGenerator/MetricsTelemetryCollector_Count.g.cs b/tracer/src/Datadog.Trace/Generated/netcoreapp3.1/Datadog.Trace.SourceGenerators/TelemetryMetricGenerator/MetricsTelemetryCollector_Count.g.cs index 5f507e7f2e88..0a01025405c7 100644 --- a/tracer/src/Datadog.Trace/Generated/netcoreapp3.1/Datadog.Trace.SourceGenerators/TelemetryMetricGenerator/MetricsTelemetryCollector_Count.g.cs +++ b/tracer/src/Datadog.Trace/Generated/netcoreapp3.1/Datadog.Trace.SourceGenerators/TelemetryMetricGenerator/MetricsTelemetryCollector_Count.g.cs @@ -11,7 +11,7 @@ namespace Datadog.Trace.Telemetry; internal sealed partial class MetricsTelemetryCollector { - private const int CountLength = 662; + private const int CountLength = 694; /// /// Creates the buffer for the values. @@ -594,13 +594,50 @@ private static AggregatedMetric[] GetCountBuffer() new(new[] { "type:timeout" }), new(new[] { "type:network" }), new(new[] { "type:status_code" }), - // waf.init, index = 539 + // memory_pressure.transitions, index = 539 + new(new[] { "state:enter", "trigger:none" }), + new(new[] { "state:enter", "trigger:memory" }), + new(new[] { "state:enter", "trigger:gc" }), + new(new[] { "state:enter", "trigger:both" }), + new(new[] { "state:exit", "trigger:none" }), + new(new[] { "state:exit", "trigger:memory" }), + new(new[] { "state:exit", "trigger:gc" }), + new(new[] { "state:exit", "trigger:both" }), + // memory_pressure.disabled, index = 547 + new(new[] { "reason:no_signals" }), + new(new[] { "reason:error" }), + // memory_pressure.memory_usage_pct, index = 549 + new(new[] { "state:enter", "bucket:lt_70" }), + new(new[] { "state:enter", "bucket:70_80" }), + new(new[] { "state:enter", "bucket:80_85" }), + new(new[] { "state:enter", "bucket:85_90" }), + new(new[] { "state:enter", "bucket:gte_90" }), + new(new[] { "state:exit", "bucket:lt_70" }), + new(new[] { "state:exit", "bucket:70_80" }), + new(new[] { "state:exit", "bucket:80_85" }), + new(new[] { "state:exit", "bucket:85_90" }), + new(new[] { "state:exit", "bucket:gte_90" }), + // memory_pressure.gc_activity, index = 559 + new(new[] { "state:enter", "bucket:lt_1" }), + new(new[] { "state:enter", "bucket:1_2" }), + new(new[] { "state:enter", "bucket:2_5" }), + new(new[] { "state:enter", "bucket:gte_5" }), + new(new[] { "state:exit", "bucket:lt_1" }), + new(new[] { "state:exit", "bucket:1_2" }), + new(new[] { "state:exit", "bucket:2_5" }), + new(new[] { "state:exit", "bucket:gte_5" }), + // memory_pressure.duration, index = 567 + new(new[] { "bucket:lt_1s" }), + new(new[] { "bucket:1_5s" }), + new(new[] { "bucket:5_30s" }), + new(new[] { "bucket:gte_30s" }), + // waf.init, index = 571 new(new[] { "waf_version", "event_rules_version", "success:true" }), new(new[] { "waf_version", "event_rules_version", "success:false" }), - // waf.updates, index = 541 + // waf.updates, index = 573 new(new[] { "waf_version", "event_rules_version", "success:true" }), new(new[] { "waf_version", "event_rules_version", "success:false" }), - // waf.requests, index = 543 + // waf.requests, index = 575 new(new[] { "waf_version", "event_rules_version", "rule_triggered:false", "request_blocked:false", "waf_timeout:false", "block_failure:false", "rate_limited:false", "input_truncated:false" }), new(new[] { "waf_version", "event_rules_version", "rule_triggered:true", "request_blocked:false", "waf_timeout:false", "block_failure:false", "rate_limited:false", "input_truncated:false" }), new(new[] { "waf_version", "event_rules_version", "rule_triggered:true", "request_blocked:true", "waf_timeout:false", "block_failure:false", "rate_limited:false", "input_truncated:false" }), @@ -609,17 +646,17 @@ private static AggregatedMetric[] GetCountBuffer() new(new[] { "waf_version", "event_rules_version", "rule_triggered:true", "request_blocked:false", "waf_timeout:false", "block_failure:false", "rate_limited:false", "input_truncated:true" }), new(new[] { "waf_version", "event_rules_version", "rule_triggered:true", "request_blocked:true", "waf_timeout:false", "block_failure:false", "rate_limited:false", "input_truncated:true" }), new(new[] { "waf_version", "event_rules_version", "rule_triggered:false", "request_blocked:false", "waf_timeout:true", "block_failure:false", "rate_limited:false", "input_truncated:true" }), - // waf.input_truncated, index = 551 + // waf.input_truncated, index = 583 new(new[] { "truncation_reason:string_too_long" }), new(new[] { "truncation_reason:list_or_map_too_large" }), new(new[] { "truncation_reason:object_too_deep" }), - // rasp.rule.eval, index = 554 + // rasp.rule.eval, index = 586 new(new[] { "waf_version", "event_rules_version", "rule_type:lfi" }), new(new[] { "waf_version", "event_rules_version", "rule_type:ssrf" }), new(new[] { "waf_version", "event_rules_version", "rule_type:sql_injection" }), new(new[] { "waf_version", "event_rules_version", "rule_type:command_injection", "rule_variant:shell" }), new(new[] { "waf_version", "event_rules_version", "rule_type:command_injection", "rule_variant:exec" }), - // rasp.rule.match, index = 559 + // rasp.rule.match, index = 591 new(new[] { "waf_version", "event_rules_version", "block:success", "rule_type:lfi" }), new(new[] { "waf_version", "event_rules_version", "block:success", "rule_type:ssrf" }), new(new[] { "waf_version", "event_rules_version", "block:success", "rule_type:sql_injection" }), @@ -635,29 +672,29 @@ private static AggregatedMetric[] GetCountBuffer() new(new[] { "waf_version", "event_rules_version", "block:irrelevant", "rule_type:sql_injection" }), new(new[] { "waf_version", "event_rules_version", "block:irrelevant", "rule_type:command_injection", "rule_variant:shell" }), new(new[] { "waf_version", "event_rules_version", "block:irrelevant", "rule_type:command_injection", "rule_variant:exec" }), - // rasp.timeout, index = 574 + // rasp.timeout, index = 606 new(new[] { "waf_version", "event_rules_version", "rule_type:lfi" }), new(new[] { "waf_version", "event_rules_version", "rule_type:ssrf" }), new(new[] { "waf_version", "event_rules_version", "rule_type:sql_injection" }), new(new[] { "waf_version", "event_rules_version", "rule_type:command_injection", "rule_variant:shell" }), new(new[] { "waf_version", "event_rules_version", "rule_type:command_injection", "rule_variant:exec" }), - // instrum.user_auth.missing_user_id, index = 579 + // instrum.user_auth.missing_user_id, index = 611 new(new[] { "framework:aspnetcore_identity", "event_type:login_success" }), new(new[] { "framework:aspnetcore_identity", "event_type:login_failure" }), new(new[] { "framework:aspnetcore_identity", "event_type:signup" }), new(new[] { "framework:unknown", "event_type:signup" }), - // instrum.user_auth.missing_user_login, index = 583 + // instrum.user_auth.missing_user_login, index = 615 new(new[] { "framework:aspnetcore_identity", "event_type:login_success" }), new(new[] { "framework:aspnetcore_identity", "event_type:login_failure" }), new(new[] { "framework:aspnetcore_identity", "event_type:signup" }), new(new[] { "framework:unknown", "event_type:signup" }), - // sdk.event, index = 587 + // sdk.event, index = 619 new(new[] { "event_type:login_success", "sdk_version:v1" }), new(new[] { "event_type:login_success", "sdk_version:v2" }), new(new[] { "event_type:login_failure", "sdk_version:v1" }), new(new[] { "event_type:login_failure", "sdk_version:v2" }), new(new[] { "event_type:custom", "sdk_version:v1" }), - // executed.source, index = 592 + // executed.source, index = 624 new(new[] { "source_type:http.request.body" }), new(new[] { "source_type:http.request.path" }), new(new[] { "source_type:http.request.parameter.name" }), @@ -672,9 +709,9 @@ private static AggregatedMetric[] GetCountBuffer() new(new[] { "source_type:http.request.uri" }), new(new[] { "source_type:grpc.request.body" }), new(new[] { "source_type:sql.row.value" }), - // executed.propagation, index = 606 + // executed.propagation, index = 638 new(null), - // executed.sink, index = 607 + // executed.sink, index = 639 new(new[] { "vulnerability_type:none" }), new(new[] { "vulnerability_type:weak_cipher" }), new(new[] { "vulnerability_type:weak_hash" }), @@ -702,9 +739,9 @@ private static AggregatedMetric[] GetCountBuffer() new(new[] { "vulnerability_type:directory_listing_leak" }), new(new[] { "vulnerability_type:session_timeout" }), new(new[] { "vulnerability_type:email_html_injection" }), - // request.tainted, index = 634 + // request.tainted, index = 666 new(null), - // suppressed.vulnerabilities, index = 635 + // suppressed.vulnerabilities, index = 667 new(new[] { "vulnerability_type:none" }), new(new[] { "vulnerability_type:weak_cipher" }), new(new[] { "vulnerability_type:weak_hash" }), @@ -740,7 +777,7 @@ private static AggregatedMetric[] GetCountBuffer() /// It is equal to the cardinality of the tag combinations (or 1 if there are no tags) /// private static int[] CountEntryCounts { get; } - = new int[]{ 4, 85, 1, 3, 5, 2, 2, 5, 1, 1, 1, 22, 3, 2, 5, 5, 4, 1, 1, 22, 3, 90, 90, 4, 4, 4, 4, 2, 44, 6, 1, 1, 85, 1, 22, 3, 2, 2, 8, 3, 5, 15, 5, 4, 4, 5, 14, 1, 27, 1, 27, }; + = new int[]{ 4, 85, 1, 3, 5, 2, 2, 5, 1, 1, 1, 22, 3, 2, 5, 5, 4, 1, 1, 22, 3, 90, 90, 4, 4, 4, 4, 2, 44, 6, 1, 1, 85, 1, 22, 3, 8, 2, 10, 8, 4, 2, 2, 8, 3, 5, 15, 5, 4, 4, 5, 14, 1, 27, 1, 27, }; public void RecordCountLogCreated(Datadog.Trace.Telemetry.Metrics.MetricTags.LogLevel tag, int increment = 1) { @@ -950,91 +987,121 @@ public void RecordCountDirectLogApiErrors(Datadog.Trace.Telemetry.Metrics.Metric Interlocked.Add(ref _buffer.Count[index], increment); } + public void RecordCountDebuggerMemoryPressureTransitions(Datadog.Trace.Telemetry.Metrics.MetricTags.DebuggerMemoryPressureState tag1, Datadog.Trace.Telemetry.Metrics.MetricTags.DebuggerMemoryPressureTrigger tag2, int increment = 1) + { + var index = 539 + ((int)tag1 * 4) + (int)tag2; + Interlocked.Add(ref _buffer.Count[index], increment); + } + + public void RecordCountDebuggerMemoryPressureDisabled(Datadog.Trace.Telemetry.Metrics.MetricTags.DebuggerMemoryPressureDisabledReason tag, int increment = 1) + { + var index = 547 + (int)tag; + Interlocked.Add(ref _buffer.Count[index], increment); + } + + public void RecordCountDebuggerMemoryPressureMemoryUsagePct(Datadog.Trace.Telemetry.Metrics.MetricTags.DebuggerMemoryPressureState tag1, Datadog.Trace.Telemetry.Metrics.MetricTags.DebuggerMemoryPressureMemoryBucket tag2, int increment = 1) + { + var index = 549 + ((int)tag1 * 5) + (int)tag2; + Interlocked.Add(ref _buffer.Count[index], increment); + } + + public void RecordCountDebuggerMemoryPressureGcActivity(Datadog.Trace.Telemetry.Metrics.MetricTags.DebuggerMemoryPressureState tag1, Datadog.Trace.Telemetry.Metrics.MetricTags.DebuggerMemoryPressureGcBucket tag2, int increment = 1) + { + var index = 559 + ((int)tag1 * 4) + (int)tag2; + Interlocked.Add(ref _buffer.Count[index], increment); + } + + public void RecordCountDebuggerMemoryPressureDuration(Datadog.Trace.Telemetry.Metrics.MetricTags.DebuggerMemoryPressureDurationBucket tag, int increment = 1) + { + var index = 567 + (int)tag; + Interlocked.Add(ref _buffer.Count[index], increment); + } + public void RecordCountWafInit(Datadog.Trace.Telemetry.Metrics.MetricTags.WafStatus tag, int increment = 1) { - var index = 539 + (int)tag; + var index = 571 + (int)tag; Interlocked.Add(ref _buffer.Count[index], increment); } public void RecordCountWafUpdates(Datadog.Trace.Telemetry.Metrics.MetricTags.WafStatus tag, int increment = 1) { - var index = 541 + (int)tag; + var index = 573 + (int)tag; Interlocked.Add(ref _buffer.Count[index], increment); } public void RecordCountWafRequests(Datadog.Trace.Telemetry.Metrics.MetricTags.WafAnalysis tag, int increment = 1) { - var index = 543 + (int)tag; + var index = 575 + (int)tag; Interlocked.Add(ref _buffer.Count[index], increment); } public void RecordCountInputTruncated(Datadog.Trace.Telemetry.Metrics.MetricTags.TruncationReason tag, int increment = 1) { - var index = 551 + (int)tag; + var index = 583 + (int)tag; Interlocked.Add(ref _buffer.Count[index], increment); } public void RecordCountRaspRuleEval(Datadog.Trace.Telemetry.Metrics.MetricTags.RaspRuleType tag, int increment = 1) { - var index = 554 + (int)tag; + var index = 586 + (int)tag; Interlocked.Add(ref _buffer.Count[index], increment); } public void RecordCountRaspRuleMatch(Datadog.Trace.Telemetry.Metrics.MetricTags.RaspRuleTypeMatch tag, int increment = 1) { - var index = 559 + (int)tag; + var index = 591 + (int)tag; Interlocked.Add(ref _buffer.Count[index], increment); } public void RecordCountRaspTimeout(Datadog.Trace.Telemetry.Metrics.MetricTags.RaspRuleType tag, int increment = 1) { - var index = 574 + (int)tag; + var index = 606 + (int)tag; Interlocked.Add(ref _buffer.Count[index], increment); } public void RecordCountMissingUserId(Datadog.Trace.Telemetry.Metrics.MetricTags.AuthenticationFrameworkWithEventType tag, int increment = 1) { - var index = 579 + (int)tag; + var index = 611 + (int)tag; Interlocked.Add(ref _buffer.Count[index], increment); } public void RecordCountMissingUserLogin(Datadog.Trace.Telemetry.Metrics.MetricTags.AuthenticationFrameworkWithEventType tag, int increment = 1) { - var index = 583 + (int)tag; + var index = 615 + (int)tag; Interlocked.Add(ref _buffer.Count[index], increment); } public void RecordCountUserEventSdk(Datadog.Trace.Telemetry.Metrics.MetricTags.UserEventSdk tag, int increment = 1) { - var index = 587 + (int)tag; + var index = 619 + (int)tag; Interlocked.Add(ref _buffer.Count[index], increment); } public void RecordCountIastExecutedSources(Datadog.Trace.Telemetry.Metrics.MetricTags.IastSourceType tag, int increment = 1) { - var index = 592 + (int)tag; + var index = 624 + (int)tag; Interlocked.Add(ref _buffer.Count[index], increment); } public void RecordCountIastExecutedPropagations(int increment = 1) { - Interlocked.Add(ref _buffer.Count[606], increment); + Interlocked.Add(ref _buffer.Count[638], increment); } public void RecordCountIastExecutedSinks(Datadog.Trace.Telemetry.Metrics.MetricTags.IastVulnerabilityType tag, int increment = 1) { - var index = 607 + (int)tag; + var index = 639 + (int)tag; Interlocked.Add(ref _buffer.Count[index], increment); } public void RecordCountIastRequestTainted(int increment = 1) { - Interlocked.Add(ref _buffer.Count[634], increment); + Interlocked.Add(ref _buffer.Count[666], increment); } public void RecordCountIastSuppressedVulnerabilities(Datadog.Trace.Telemetry.Metrics.MetricTags.IastVulnerabilityType tag, int increment = 1) { - var index = 635 + (int)tag; + var index = 667 + (int)tag; Interlocked.Add(ref _buffer.Count[index], increment); } } \ No newline at end of file diff --git a/tracer/src/Datadog.Trace/Generated/netcoreapp3.1/Datadog.Trace.SourceGenerators/TelemetryMetricGenerator/NullMetricsTelemetryCollector_Count.g.cs b/tracer/src/Datadog.Trace/Generated/netcoreapp3.1/Datadog.Trace.SourceGenerators/TelemetryMetricGenerator/NullMetricsTelemetryCollector_Count.g.cs index b2aa00db73bb..4bfc229adb4e 100644 --- a/tracer/src/Datadog.Trace/Generated/netcoreapp3.1/Datadog.Trace.SourceGenerators/TelemetryMetricGenerator/NullMetricsTelemetryCollector_Count.g.cs +++ b/tracer/src/Datadog.Trace/Generated/netcoreapp3.1/Datadog.Trace.SourceGenerators/TelemetryMetricGenerator/NullMetricsTelemetryCollector_Count.g.cs @@ -156,6 +156,26 @@ public void RecordCountDirectLogApiErrors(Datadog.Trace.Telemetry.Metrics.Metric { } + public void RecordCountDebuggerMemoryPressureTransitions(Datadog.Trace.Telemetry.Metrics.MetricTags.DebuggerMemoryPressureState tag1, Datadog.Trace.Telemetry.Metrics.MetricTags.DebuggerMemoryPressureTrigger tag2, int increment = 1) + { + } + + public void RecordCountDebuggerMemoryPressureDisabled(Datadog.Trace.Telemetry.Metrics.MetricTags.DebuggerMemoryPressureDisabledReason tag, int increment = 1) + { + } + + public void RecordCountDebuggerMemoryPressureMemoryUsagePct(Datadog.Trace.Telemetry.Metrics.MetricTags.DebuggerMemoryPressureState tag1, Datadog.Trace.Telemetry.Metrics.MetricTags.DebuggerMemoryPressureMemoryBucket tag2, int increment = 1) + { + } + + public void RecordCountDebuggerMemoryPressureGcActivity(Datadog.Trace.Telemetry.Metrics.MetricTags.DebuggerMemoryPressureState tag1, Datadog.Trace.Telemetry.Metrics.MetricTags.DebuggerMemoryPressureGcBucket tag2, int increment = 1) + { + } + + public void RecordCountDebuggerMemoryPressureDuration(Datadog.Trace.Telemetry.Metrics.MetricTags.DebuggerMemoryPressureDurationBucket tag, int increment = 1) + { + } + public void RecordCountWafInit(Datadog.Trace.Telemetry.Metrics.MetricTags.WafStatus tag, int increment = 1) { } diff --git a/tracer/src/Datadog.Trace/Generated/netstandard2.0/Datadog.Trace.SourceGenerators/TelemetryMetricGenerator/CiVisibilityMetricsTelemetryCollector_Count.g.cs b/tracer/src/Datadog.Trace/Generated/netstandard2.0/Datadog.Trace.SourceGenerators/TelemetryMetricGenerator/CiVisibilityMetricsTelemetryCollector_Count.g.cs index 8c6817ebca98..b2cd615aef09 100644 --- a/tracer/src/Datadog.Trace/Generated/netstandard2.0/Datadog.Trace.SourceGenerators/TelemetryMetricGenerator/CiVisibilityMetricsTelemetryCollector_Count.g.cs +++ b/tracer/src/Datadog.Trace/Generated/netstandard2.0/Datadog.Trace.SourceGenerators/TelemetryMetricGenerator/CiVisibilityMetricsTelemetryCollector_Count.g.cs @@ -156,6 +156,26 @@ public void RecordCountDirectLogApiErrors(Datadog.Trace.Telemetry.Metrics.Metric { } + public void RecordCountDebuggerMemoryPressureTransitions(Datadog.Trace.Telemetry.Metrics.MetricTags.DebuggerMemoryPressureState tag1, Datadog.Trace.Telemetry.Metrics.MetricTags.DebuggerMemoryPressureTrigger tag2, int increment = 1) + { + } + + public void RecordCountDebuggerMemoryPressureDisabled(Datadog.Trace.Telemetry.Metrics.MetricTags.DebuggerMemoryPressureDisabledReason tag, int increment = 1) + { + } + + public void RecordCountDebuggerMemoryPressureMemoryUsagePct(Datadog.Trace.Telemetry.Metrics.MetricTags.DebuggerMemoryPressureState tag1, Datadog.Trace.Telemetry.Metrics.MetricTags.DebuggerMemoryPressureMemoryBucket tag2, int increment = 1) + { + } + + public void RecordCountDebuggerMemoryPressureGcActivity(Datadog.Trace.Telemetry.Metrics.MetricTags.DebuggerMemoryPressureState tag1, Datadog.Trace.Telemetry.Metrics.MetricTags.DebuggerMemoryPressureGcBucket tag2, int increment = 1) + { + } + + public void RecordCountDebuggerMemoryPressureDuration(Datadog.Trace.Telemetry.Metrics.MetricTags.DebuggerMemoryPressureDurationBucket tag, int increment = 1) + { + } + public void RecordCountWafInit(Datadog.Trace.Telemetry.Metrics.MetricTags.WafStatus tag, int increment = 1) { } diff --git a/tracer/src/Datadog.Trace/Generated/netstandard2.0/Datadog.Trace.SourceGenerators/TelemetryMetricGenerator/CountExtensions.g.cs b/tracer/src/Datadog.Trace/Generated/netstandard2.0/Datadog.Trace.SourceGenerators/TelemetryMetricGenerator/CountExtensions.g.cs index 7078b0d04857..f1a010f936f4 100644 --- a/tracer/src/Datadog.Trace/Generated/netstandard2.0/Datadog.Trace.SourceGenerators/TelemetryMetricGenerator/CountExtensions.g.cs +++ b/tracer/src/Datadog.Trace/Generated/netstandard2.0/Datadog.Trace.SourceGenerators/TelemetryMetricGenerator/CountExtensions.g.cs @@ -12,7 +12,7 @@ internal static partial class CountExtensions /// /// The number of separate metrics in the metric. /// - public const int Length = 51; + public const int Length = 56; /// /// Gets the metric name for the provided metric @@ -58,6 +58,11 @@ public static string GetName(this Datadog.Trace.Telemetry.Metrics.Count metric) Datadog.Trace.Telemetry.Metrics.Count.DirectLogApiRequests => "direct_log_api.requests", Datadog.Trace.Telemetry.Metrics.Count.DirectLogApiResponses => "direct_log_api.responses", Datadog.Trace.Telemetry.Metrics.Count.DirectLogApiErrors => "direct_log_api.errors", + Datadog.Trace.Telemetry.Metrics.Count.DebuggerMemoryPressureTransitions => "memory_pressure.transitions", + Datadog.Trace.Telemetry.Metrics.Count.DebuggerMemoryPressureDisabled => "memory_pressure.disabled", + Datadog.Trace.Telemetry.Metrics.Count.DebuggerMemoryPressureMemoryUsagePct => "memory_pressure.memory_usage_pct", + Datadog.Trace.Telemetry.Metrics.Count.DebuggerMemoryPressureGcActivity => "memory_pressure.gc_activity", + Datadog.Trace.Telemetry.Metrics.Count.DebuggerMemoryPressureDuration => "memory_pressure.duration", Datadog.Trace.Telemetry.Metrics.Count.WafInit => "waf.init", Datadog.Trace.Telemetry.Metrics.Count.WafUpdates => "waf.updates", Datadog.Trace.Telemetry.Metrics.Count.WafRequests => "waf.requests", @@ -111,6 +116,11 @@ public static bool IsCommon(this Datadog.Trace.Telemetry.Metrics.Count metric) Datadog.Trace.Telemetry.Metrics.Count.TelemetryApiRequests => "telemetry", Datadog.Trace.Telemetry.Metrics.Count.TelemetryApiResponses => "telemetry", Datadog.Trace.Telemetry.Metrics.Count.TelemetryApiErrors => "telemetry", + Datadog.Trace.Telemetry.Metrics.Count.DebuggerMemoryPressureTransitions => "live_debugger", + Datadog.Trace.Telemetry.Metrics.Count.DebuggerMemoryPressureDisabled => "live_debugger", + Datadog.Trace.Telemetry.Metrics.Count.DebuggerMemoryPressureMemoryUsagePct => "live_debugger", + Datadog.Trace.Telemetry.Metrics.Count.DebuggerMemoryPressureGcActivity => "live_debugger", + Datadog.Trace.Telemetry.Metrics.Count.DebuggerMemoryPressureDuration => "live_debugger", Datadog.Trace.Telemetry.Metrics.Count.WafInit => "appsec", Datadog.Trace.Telemetry.Metrics.Count.WafUpdates => "appsec", Datadog.Trace.Telemetry.Metrics.Count.WafRequests => "appsec", diff --git a/tracer/src/Datadog.Trace/Generated/netstandard2.0/Datadog.Trace.SourceGenerators/TelemetryMetricGenerator/IMetricsTelemetryCollector_Count.g.cs b/tracer/src/Datadog.Trace/Generated/netstandard2.0/Datadog.Trace.SourceGenerators/TelemetryMetricGenerator/IMetricsTelemetryCollector_Count.g.cs index 0a4e7e205295..85f010d8084a 100644 --- a/tracer/src/Datadog.Trace/Generated/netstandard2.0/Datadog.Trace.SourceGenerators/TelemetryMetricGenerator/IMetricsTelemetryCollector_Count.g.cs +++ b/tracer/src/Datadog.Trace/Generated/netstandard2.0/Datadog.Trace.SourceGenerators/TelemetryMetricGenerator/IMetricsTelemetryCollector_Count.g.cs @@ -81,6 +81,16 @@ internal partial interface IMetricsTelemetryCollector public void RecordCountDirectLogApiErrors(Datadog.Trace.Telemetry.Metrics.MetricTags.ApiError tag, int increment = 1); + public void RecordCountDebuggerMemoryPressureTransitions(Datadog.Trace.Telemetry.Metrics.MetricTags.DebuggerMemoryPressureState tag1, Datadog.Trace.Telemetry.Metrics.MetricTags.DebuggerMemoryPressureTrigger tag2, int increment = 1); + + public void RecordCountDebuggerMemoryPressureDisabled(Datadog.Trace.Telemetry.Metrics.MetricTags.DebuggerMemoryPressureDisabledReason tag, int increment = 1); + + public void RecordCountDebuggerMemoryPressureMemoryUsagePct(Datadog.Trace.Telemetry.Metrics.MetricTags.DebuggerMemoryPressureState tag1, Datadog.Trace.Telemetry.Metrics.MetricTags.DebuggerMemoryPressureMemoryBucket tag2, int increment = 1); + + public void RecordCountDebuggerMemoryPressureGcActivity(Datadog.Trace.Telemetry.Metrics.MetricTags.DebuggerMemoryPressureState tag1, Datadog.Trace.Telemetry.Metrics.MetricTags.DebuggerMemoryPressureGcBucket tag2, int increment = 1); + + public void RecordCountDebuggerMemoryPressureDuration(Datadog.Trace.Telemetry.Metrics.MetricTags.DebuggerMemoryPressureDurationBucket tag, int increment = 1); + public void RecordCountWafInit(Datadog.Trace.Telemetry.Metrics.MetricTags.WafStatus tag, int increment = 1); public void RecordCountWafUpdates(Datadog.Trace.Telemetry.Metrics.MetricTags.WafStatus tag, int increment = 1); diff --git a/tracer/src/Datadog.Trace/Generated/netstandard2.0/Datadog.Trace.SourceGenerators/TelemetryMetricGenerator/MetricsTelemetryCollector_Count.g.cs b/tracer/src/Datadog.Trace/Generated/netstandard2.0/Datadog.Trace.SourceGenerators/TelemetryMetricGenerator/MetricsTelemetryCollector_Count.g.cs index 5f507e7f2e88..0a01025405c7 100644 --- a/tracer/src/Datadog.Trace/Generated/netstandard2.0/Datadog.Trace.SourceGenerators/TelemetryMetricGenerator/MetricsTelemetryCollector_Count.g.cs +++ b/tracer/src/Datadog.Trace/Generated/netstandard2.0/Datadog.Trace.SourceGenerators/TelemetryMetricGenerator/MetricsTelemetryCollector_Count.g.cs @@ -11,7 +11,7 @@ namespace Datadog.Trace.Telemetry; internal sealed partial class MetricsTelemetryCollector { - private const int CountLength = 662; + private const int CountLength = 694; /// /// Creates the buffer for the values. @@ -594,13 +594,50 @@ private static AggregatedMetric[] GetCountBuffer() new(new[] { "type:timeout" }), new(new[] { "type:network" }), new(new[] { "type:status_code" }), - // waf.init, index = 539 + // memory_pressure.transitions, index = 539 + new(new[] { "state:enter", "trigger:none" }), + new(new[] { "state:enter", "trigger:memory" }), + new(new[] { "state:enter", "trigger:gc" }), + new(new[] { "state:enter", "trigger:both" }), + new(new[] { "state:exit", "trigger:none" }), + new(new[] { "state:exit", "trigger:memory" }), + new(new[] { "state:exit", "trigger:gc" }), + new(new[] { "state:exit", "trigger:both" }), + // memory_pressure.disabled, index = 547 + new(new[] { "reason:no_signals" }), + new(new[] { "reason:error" }), + // memory_pressure.memory_usage_pct, index = 549 + new(new[] { "state:enter", "bucket:lt_70" }), + new(new[] { "state:enter", "bucket:70_80" }), + new(new[] { "state:enter", "bucket:80_85" }), + new(new[] { "state:enter", "bucket:85_90" }), + new(new[] { "state:enter", "bucket:gte_90" }), + new(new[] { "state:exit", "bucket:lt_70" }), + new(new[] { "state:exit", "bucket:70_80" }), + new(new[] { "state:exit", "bucket:80_85" }), + new(new[] { "state:exit", "bucket:85_90" }), + new(new[] { "state:exit", "bucket:gte_90" }), + // memory_pressure.gc_activity, index = 559 + new(new[] { "state:enter", "bucket:lt_1" }), + new(new[] { "state:enter", "bucket:1_2" }), + new(new[] { "state:enter", "bucket:2_5" }), + new(new[] { "state:enter", "bucket:gte_5" }), + new(new[] { "state:exit", "bucket:lt_1" }), + new(new[] { "state:exit", "bucket:1_2" }), + new(new[] { "state:exit", "bucket:2_5" }), + new(new[] { "state:exit", "bucket:gte_5" }), + // memory_pressure.duration, index = 567 + new(new[] { "bucket:lt_1s" }), + new(new[] { "bucket:1_5s" }), + new(new[] { "bucket:5_30s" }), + new(new[] { "bucket:gte_30s" }), + // waf.init, index = 571 new(new[] { "waf_version", "event_rules_version", "success:true" }), new(new[] { "waf_version", "event_rules_version", "success:false" }), - // waf.updates, index = 541 + // waf.updates, index = 573 new(new[] { "waf_version", "event_rules_version", "success:true" }), new(new[] { "waf_version", "event_rules_version", "success:false" }), - // waf.requests, index = 543 + // waf.requests, index = 575 new(new[] { "waf_version", "event_rules_version", "rule_triggered:false", "request_blocked:false", "waf_timeout:false", "block_failure:false", "rate_limited:false", "input_truncated:false" }), new(new[] { "waf_version", "event_rules_version", "rule_triggered:true", "request_blocked:false", "waf_timeout:false", "block_failure:false", "rate_limited:false", "input_truncated:false" }), new(new[] { "waf_version", "event_rules_version", "rule_triggered:true", "request_blocked:true", "waf_timeout:false", "block_failure:false", "rate_limited:false", "input_truncated:false" }), @@ -609,17 +646,17 @@ private static AggregatedMetric[] GetCountBuffer() new(new[] { "waf_version", "event_rules_version", "rule_triggered:true", "request_blocked:false", "waf_timeout:false", "block_failure:false", "rate_limited:false", "input_truncated:true" }), new(new[] { "waf_version", "event_rules_version", "rule_triggered:true", "request_blocked:true", "waf_timeout:false", "block_failure:false", "rate_limited:false", "input_truncated:true" }), new(new[] { "waf_version", "event_rules_version", "rule_triggered:false", "request_blocked:false", "waf_timeout:true", "block_failure:false", "rate_limited:false", "input_truncated:true" }), - // waf.input_truncated, index = 551 + // waf.input_truncated, index = 583 new(new[] { "truncation_reason:string_too_long" }), new(new[] { "truncation_reason:list_or_map_too_large" }), new(new[] { "truncation_reason:object_too_deep" }), - // rasp.rule.eval, index = 554 + // rasp.rule.eval, index = 586 new(new[] { "waf_version", "event_rules_version", "rule_type:lfi" }), new(new[] { "waf_version", "event_rules_version", "rule_type:ssrf" }), new(new[] { "waf_version", "event_rules_version", "rule_type:sql_injection" }), new(new[] { "waf_version", "event_rules_version", "rule_type:command_injection", "rule_variant:shell" }), new(new[] { "waf_version", "event_rules_version", "rule_type:command_injection", "rule_variant:exec" }), - // rasp.rule.match, index = 559 + // rasp.rule.match, index = 591 new(new[] { "waf_version", "event_rules_version", "block:success", "rule_type:lfi" }), new(new[] { "waf_version", "event_rules_version", "block:success", "rule_type:ssrf" }), new(new[] { "waf_version", "event_rules_version", "block:success", "rule_type:sql_injection" }), @@ -635,29 +672,29 @@ private static AggregatedMetric[] GetCountBuffer() new(new[] { "waf_version", "event_rules_version", "block:irrelevant", "rule_type:sql_injection" }), new(new[] { "waf_version", "event_rules_version", "block:irrelevant", "rule_type:command_injection", "rule_variant:shell" }), new(new[] { "waf_version", "event_rules_version", "block:irrelevant", "rule_type:command_injection", "rule_variant:exec" }), - // rasp.timeout, index = 574 + // rasp.timeout, index = 606 new(new[] { "waf_version", "event_rules_version", "rule_type:lfi" }), new(new[] { "waf_version", "event_rules_version", "rule_type:ssrf" }), new(new[] { "waf_version", "event_rules_version", "rule_type:sql_injection" }), new(new[] { "waf_version", "event_rules_version", "rule_type:command_injection", "rule_variant:shell" }), new(new[] { "waf_version", "event_rules_version", "rule_type:command_injection", "rule_variant:exec" }), - // instrum.user_auth.missing_user_id, index = 579 + // instrum.user_auth.missing_user_id, index = 611 new(new[] { "framework:aspnetcore_identity", "event_type:login_success" }), new(new[] { "framework:aspnetcore_identity", "event_type:login_failure" }), new(new[] { "framework:aspnetcore_identity", "event_type:signup" }), new(new[] { "framework:unknown", "event_type:signup" }), - // instrum.user_auth.missing_user_login, index = 583 + // instrum.user_auth.missing_user_login, index = 615 new(new[] { "framework:aspnetcore_identity", "event_type:login_success" }), new(new[] { "framework:aspnetcore_identity", "event_type:login_failure" }), new(new[] { "framework:aspnetcore_identity", "event_type:signup" }), new(new[] { "framework:unknown", "event_type:signup" }), - // sdk.event, index = 587 + // sdk.event, index = 619 new(new[] { "event_type:login_success", "sdk_version:v1" }), new(new[] { "event_type:login_success", "sdk_version:v2" }), new(new[] { "event_type:login_failure", "sdk_version:v1" }), new(new[] { "event_type:login_failure", "sdk_version:v2" }), new(new[] { "event_type:custom", "sdk_version:v1" }), - // executed.source, index = 592 + // executed.source, index = 624 new(new[] { "source_type:http.request.body" }), new(new[] { "source_type:http.request.path" }), new(new[] { "source_type:http.request.parameter.name" }), @@ -672,9 +709,9 @@ private static AggregatedMetric[] GetCountBuffer() new(new[] { "source_type:http.request.uri" }), new(new[] { "source_type:grpc.request.body" }), new(new[] { "source_type:sql.row.value" }), - // executed.propagation, index = 606 + // executed.propagation, index = 638 new(null), - // executed.sink, index = 607 + // executed.sink, index = 639 new(new[] { "vulnerability_type:none" }), new(new[] { "vulnerability_type:weak_cipher" }), new(new[] { "vulnerability_type:weak_hash" }), @@ -702,9 +739,9 @@ private static AggregatedMetric[] GetCountBuffer() new(new[] { "vulnerability_type:directory_listing_leak" }), new(new[] { "vulnerability_type:session_timeout" }), new(new[] { "vulnerability_type:email_html_injection" }), - // request.tainted, index = 634 + // request.tainted, index = 666 new(null), - // suppressed.vulnerabilities, index = 635 + // suppressed.vulnerabilities, index = 667 new(new[] { "vulnerability_type:none" }), new(new[] { "vulnerability_type:weak_cipher" }), new(new[] { "vulnerability_type:weak_hash" }), @@ -740,7 +777,7 @@ private static AggregatedMetric[] GetCountBuffer() /// It is equal to the cardinality of the tag combinations (or 1 if there are no tags) /// private static int[] CountEntryCounts { get; } - = new int[]{ 4, 85, 1, 3, 5, 2, 2, 5, 1, 1, 1, 22, 3, 2, 5, 5, 4, 1, 1, 22, 3, 90, 90, 4, 4, 4, 4, 2, 44, 6, 1, 1, 85, 1, 22, 3, 2, 2, 8, 3, 5, 15, 5, 4, 4, 5, 14, 1, 27, 1, 27, }; + = new int[]{ 4, 85, 1, 3, 5, 2, 2, 5, 1, 1, 1, 22, 3, 2, 5, 5, 4, 1, 1, 22, 3, 90, 90, 4, 4, 4, 4, 2, 44, 6, 1, 1, 85, 1, 22, 3, 8, 2, 10, 8, 4, 2, 2, 8, 3, 5, 15, 5, 4, 4, 5, 14, 1, 27, 1, 27, }; public void RecordCountLogCreated(Datadog.Trace.Telemetry.Metrics.MetricTags.LogLevel tag, int increment = 1) { @@ -950,91 +987,121 @@ public void RecordCountDirectLogApiErrors(Datadog.Trace.Telemetry.Metrics.Metric Interlocked.Add(ref _buffer.Count[index], increment); } + public void RecordCountDebuggerMemoryPressureTransitions(Datadog.Trace.Telemetry.Metrics.MetricTags.DebuggerMemoryPressureState tag1, Datadog.Trace.Telemetry.Metrics.MetricTags.DebuggerMemoryPressureTrigger tag2, int increment = 1) + { + var index = 539 + ((int)tag1 * 4) + (int)tag2; + Interlocked.Add(ref _buffer.Count[index], increment); + } + + public void RecordCountDebuggerMemoryPressureDisabled(Datadog.Trace.Telemetry.Metrics.MetricTags.DebuggerMemoryPressureDisabledReason tag, int increment = 1) + { + var index = 547 + (int)tag; + Interlocked.Add(ref _buffer.Count[index], increment); + } + + public void RecordCountDebuggerMemoryPressureMemoryUsagePct(Datadog.Trace.Telemetry.Metrics.MetricTags.DebuggerMemoryPressureState tag1, Datadog.Trace.Telemetry.Metrics.MetricTags.DebuggerMemoryPressureMemoryBucket tag2, int increment = 1) + { + var index = 549 + ((int)tag1 * 5) + (int)tag2; + Interlocked.Add(ref _buffer.Count[index], increment); + } + + public void RecordCountDebuggerMemoryPressureGcActivity(Datadog.Trace.Telemetry.Metrics.MetricTags.DebuggerMemoryPressureState tag1, Datadog.Trace.Telemetry.Metrics.MetricTags.DebuggerMemoryPressureGcBucket tag2, int increment = 1) + { + var index = 559 + ((int)tag1 * 4) + (int)tag2; + Interlocked.Add(ref _buffer.Count[index], increment); + } + + public void RecordCountDebuggerMemoryPressureDuration(Datadog.Trace.Telemetry.Metrics.MetricTags.DebuggerMemoryPressureDurationBucket tag, int increment = 1) + { + var index = 567 + (int)tag; + Interlocked.Add(ref _buffer.Count[index], increment); + } + public void RecordCountWafInit(Datadog.Trace.Telemetry.Metrics.MetricTags.WafStatus tag, int increment = 1) { - var index = 539 + (int)tag; + var index = 571 + (int)tag; Interlocked.Add(ref _buffer.Count[index], increment); } public void RecordCountWafUpdates(Datadog.Trace.Telemetry.Metrics.MetricTags.WafStatus tag, int increment = 1) { - var index = 541 + (int)tag; + var index = 573 + (int)tag; Interlocked.Add(ref _buffer.Count[index], increment); } public void RecordCountWafRequests(Datadog.Trace.Telemetry.Metrics.MetricTags.WafAnalysis tag, int increment = 1) { - var index = 543 + (int)tag; + var index = 575 + (int)tag; Interlocked.Add(ref _buffer.Count[index], increment); } public void RecordCountInputTruncated(Datadog.Trace.Telemetry.Metrics.MetricTags.TruncationReason tag, int increment = 1) { - var index = 551 + (int)tag; + var index = 583 + (int)tag; Interlocked.Add(ref _buffer.Count[index], increment); } public void RecordCountRaspRuleEval(Datadog.Trace.Telemetry.Metrics.MetricTags.RaspRuleType tag, int increment = 1) { - var index = 554 + (int)tag; + var index = 586 + (int)tag; Interlocked.Add(ref _buffer.Count[index], increment); } public void RecordCountRaspRuleMatch(Datadog.Trace.Telemetry.Metrics.MetricTags.RaspRuleTypeMatch tag, int increment = 1) { - var index = 559 + (int)tag; + var index = 591 + (int)tag; Interlocked.Add(ref _buffer.Count[index], increment); } public void RecordCountRaspTimeout(Datadog.Trace.Telemetry.Metrics.MetricTags.RaspRuleType tag, int increment = 1) { - var index = 574 + (int)tag; + var index = 606 + (int)tag; Interlocked.Add(ref _buffer.Count[index], increment); } public void RecordCountMissingUserId(Datadog.Trace.Telemetry.Metrics.MetricTags.AuthenticationFrameworkWithEventType tag, int increment = 1) { - var index = 579 + (int)tag; + var index = 611 + (int)tag; Interlocked.Add(ref _buffer.Count[index], increment); } public void RecordCountMissingUserLogin(Datadog.Trace.Telemetry.Metrics.MetricTags.AuthenticationFrameworkWithEventType tag, int increment = 1) { - var index = 583 + (int)tag; + var index = 615 + (int)tag; Interlocked.Add(ref _buffer.Count[index], increment); } public void RecordCountUserEventSdk(Datadog.Trace.Telemetry.Metrics.MetricTags.UserEventSdk tag, int increment = 1) { - var index = 587 + (int)tag; + var index = 619 + (int)tag; Interlocked.Add(ref _buffer.Count[index], increment); } public void RecordCountIastExecutedSources(Datadog.Trace.Telemetry.Metrics.MetricTags.IastSourceType tag, int increment = 1) { - var index = 592 + (int)tag; + var index = 624 + (int)tag; Interlocked.Add(ref _buffer.Count[index], increment); } public void RecordCountIastExecutedPropagations(int increment = 1) { - Interlocked.Add(ref _buffer.Count[606], increment); + Interlocked.Add(ref _buffer.Count[638], increment); } public void RecordCountIastExecutedSinks(Datadog.Trace.Telemetry.Metrics.MetricTags.IastVulnerabilityType tag, int increment = 1) { - var index = 607 + (int)tag; + var index = 639 + (int)tag; Interlocked.Add(ref _buffer.Count[index], increment); } public void RecordCountIastRequestTainted(int increment = 1) { - Interlocked.Add(ref _buffer.Count[634], increment); + Interlocked.Add(ref _buffer.Count[666], increment); } public void RecordCountIastSuppressedVulnerabilities(Datadog.Trace.Telemetry.Metrics.MetricTags.IastVulnerabilityType tag, int increment = 1) { - var index = 635 + (int)tag; + var index = 667 + (int)tag; Interlocked.Add(ref _buffer.Count[index], increment); } } \ No newline at end of file diff --git a/tracer/src/Datadog.Trace/Generated/netstandard2.0/Datadog.Trace.SourceGenerators/TelemetryMetricGenerator/NullMetricsTelemetryCollector_Count.g.cs b/tracer/src/Datadog.Trace/Generated/netstandard2.0/Datadog.Trace.SourceGenerators/TelemetryMetricGenerator/NullMetricsTelemetryCollector_Count.g.cs index b2aa00db73bb..4bfc229adb4e 100644 --- a/tracer/src/Datadog.Trace/Generated/netstandard2.0/Datadog.Trace.SourceGenerators/TelemetryMetricGenerator/NullMetricsTelemetryCollector_Count.g.cs +++ b/tracer/src/Datadog.Trace/Generated/netstandard2.0/Datadog.Trace.SourceGenerators/TelemetryMetricGenerator/NullMetricsTelemetryCollector_Count.g.cs @@ -156,6 +156,26 @@ public void RecordCountDirectLogApiErrors(Datadog.Trace.Telemetry.Metrics.Metric { } + public void RecordCountDebuggerMemoryPressureTransitions(Datadog.Trace.Telemetry.Metrics.MetricTags.DebuggerMemoryPressureState tag1, Datadog.Trace.Telemetry.Metrics.MetricTags.DebuggerMemoryPressureTrigger tag2, int increment = 1) + { + } + + public void RecordCountDebuggerMemoryPressureDisabled(Datadog.Trace.Telemetry.Metrics.MetricTags.DebuggerMemoryPressureDisabledReason tag, int increment = 1) + { + } + + public void RecordCountDebuggerMemoryPressureMemoryUsagePct(Datadog.Trace.Telemetry.Metrics.MetricTags.DebuggerMemoryPressureState tag1, Datadog.Trace.Telemetry.Metrics.MetricTags.DebuggerMemoryPressureMemoryBucket tag2, int increment = 1) + { + } + + public void RecordCountDebuggerMemoryPressureGcActivity(Datadog.Trace.Telemetry.Metrics.MetricTags.DebuggerMemoryPressureState tag1, Datadog.Trace.Telemetry.Metrics.MetricTags.DebuggerMemoryPressureGcBucket tag2, int increment = 1) + { + } + + public void RecordCountDebuggerMemoryPressureDuration(Datadog.Trace.Telemetry.Metrics.MetricTags.DebuggerMemoryPressureDurationBucket tag, int increment = 1) + { + } + public void RecordCountWafInit(Datadog.Trace.Telemetry.Metrics.MetricTags.WafStatus tag, int increment = 1) { } diff --git a/tracer/src/Datadog.Trace/Telemetry/DTOs/MetricNamespaceConstants.cs b/tracer/src/Datadog.Trace/Telemetry/DTOs/MetricNamespaceConstants.cs index 31787b55864e..571736265bad 100644 --- a/tracer/src/Datadog.Trace/Telemetry/DTOs/MetricNamespaceConstants.cs +++ b/tracer/src/Datadog.Trace/Telemetry/DTOs/MetricNamespaceConstants.cs @@ -15,4 +15,5 @@ internal static class MetricNamespaceConstants public const string ASM = "appsec"; public const string Iast = "iast"; public const string CIVisibility = "civisibility"; + public const string LiveDebugger = "live_debugger"; } diff --git a/tracer/src/Datadog.Trace/Telemetry/Metrics/Count.cs b/tracer/src/Datadog.Trace/Telemetry/Metrics/Count.cs index fa8ed0f94152..7680bb050990 100644 --- a/tracer/src/Datadog.Trace/Telemetry/Metrics/Count.cs +++ b/tracer/src/Datadog.Trace/Telemetry/Metrics/Count.cs @@ -206,6 +206,33 @@ internal enum Count [TelemetryMetric("direct_log_api.errors", isCommon: false)] DirectLogApiErrors, #endregion +#region Live Debugger Namespace + + /// + /// The number of Dynamic Instrumentation memory-pressure state transitions, tagged by state and the signal that triggered entry. + /// + [TelemetryMetric("memory_pressure.transitions", isCommon: true, NS.LiveDebugger)] DebuggerMemoryPressureTransitions, + + /// + /// The number of times the Dynamic Instrumentation memory-pressure monitor disabled itself, tagged by reason. + /// + [TelemetryMetric("memory_pressure.disabled", isCommon: true, NS.LiveDebugger)] DebuggerMemoryPressureDisabled, + + /// + /// Count of Dynamic Instrumentation memory-pressure transitions, tagged by memory load percentage bucket at the transition. + /// + [TelemetryMetric("memory_pressure.memory_usage_pct", isCommon: true, NS.LiveDebugger)] DebuggerMemoryPressureMemoryUsagePct, + + /// + /// Count of Dynamic Instrumentation memory-pressure transitions, tagged by GC activity bucket at the transition. + /// + [TelemetryMetric("memory_pressure.gc_activity", isCommon: true, NS.LiveDebugger)] DebuggerMemoryPressureGcActivity, + + /// + /// Count of Dynamic Instrumentation high-memory-pressure periods, incremented once on exit and tagged by duration bucket. + /// + [TelemetryMetric("memory_pressure.duration", isCommon: true, NS.LiveDebugger)] DebuggerMemoryPressureDuration, +#endregion #region AppSec Namespace /// diff --git a/tracer/src/Datadog.Trace/Telemetry/Metrics/MetricTags.cs b/tracer/src/Datadog.Trace/Telemetry/Metrics/MetricTags.cs index e01cd3cd97b9..c5e925ef08f4 100644 --- a/tracer/src/Datadog.Trace/Telemetry/Metrics/MetricTags.cs +++ b/tracer/src/Datadog.Trace/Telemetry/Metrics/MetricTags.cs @@ -188,6 +188,51 @@ internal enum InstrumentationComponent [Description("component_name:iast_aspects")] IastAspects, } + internal enum DebuggerMemoryPressureState + { + [Description("state:enter")] Enter, + [Description("state:exit")] Exit, + } + + internal enum DebuggerMemoryPressureTrigger + { + [Description("trigger:none")] None, + [Description("trigger:memory")] Memory, + [Description("trigger:gc")] Gc, + [Description("trigger:both")] Both, + } + + internal enum DebuggerMemoryPressureDisabledReason + { + [Description("reason:no_signals")] NoSignals, + [Description("reason:error")] Error, + } + + internal enum DebuggerMemoryPressureMemoryBucket + { + [Description("bucket:lt_70")] LessThan70, + [Description("bucket:70_80")] From70To80, + [Description("bucket:80_85")] From80To85, + [Description("bucket:85_90")] From85To90, + [Description("bucket:gte_90")] GreaterThanOrEqual90, + } + + internal enum DebuggerMemoryPressureGcBucket + { + [Description("bucket:lt_1")] LessThan1, + [Description("bucket:1_2")] From1To2, + [Description("bucket:2_5")] From2To5, + [Description("bucket:gte_5")] GreaterThanOrEqual5, + } + + internal enum DebuggerMemoryPressureDurationBucket + { + [Description("bucket:lt_1s")] LessThan1Second, + [Description("bucket:1_5s")] From1To5Seconds, + [Description("bucket:5_30s")] From5To30Seconds, + [Description("bucket:gte_30s")] GreaterThanOrEqual30Seconds, + } + internal enum IntegrationName { // manual integration diff --git a/tracer/test/Datadog.Trace.Tests/Debugger/DynamicInstrumentationTests.cs b/tracer/test/Datadog.Trace.Tests/Debugger/DynamicInstrumentationTests.cs index f5987be2602c..8bc528a66457 100644 --- a/tracer/test/Datadog.Trace.Tests/Debugger/DynamicInstrumentationTests.cs +++ b/tracer/test/Datadog.Trace.Tests/Debugger/DynamicInstrumentationTests.cs @@ -10,6 +10,7 @@ using System.Numerics; using System.Reflection; using System.Text; +using System.Threading; using System.Threading.Tasks; using Datadog.Trace.Agent.DiscoveryService; using Datadog.Trace.Configuration; @@ -28,6 +29,8 @@ using FluentAssertions; using Xunit; using DebuggerSampling = Datadog.Trace.Debugger.Configurations.Models.Sampling; +using ExpressionProbeInfo = Datadog.Trace.Debugger.Expressions.ProbeInfo; +using ExpressionProbeLocation = Datadog.Trace.Debugger.Expressions.ProbeLocation; #nullable enable @@ -69,6 +72,8 @@ public void DynamicInstrumentation_DoesNotDisposeGlobalRateLimiterOnDispose() var globalRateLimiter = new GlobalRateLimiterMock(); var logUploader = new LogUploaderMock(); + var memSource = new CountingMemorySource(); + var memoryPressureMonitor = new MemoryPressureMonitor(MemoryPressureConfig.Default, memSource.TryGetMemoryUsageRatio, memSource.TryGetGen2CollectionCount, null); var debugger = new DynamicInstrumentation( settings, new DiscoveryServiceMock(), @@ -80,12 +85,56 @@ public void DynamicInstrumentation_DoesNotDisposeGlobalRateLimiterOnDispose() new ProbeStatusPollerMock(), ConfigurationUpdater.Create(string.Empty, string.Empty, 0, globalRateLimiter), NoOpStatsd.Instance, - globalRateLimiter); + globalRateLimiter, + memoryPressureMonitor); debugger.Dispose(); globalRateLimiter.DisposeCallCount.Should().Be(0); logUploader.DisposeCallCount.Should().Be(1); + + // The monitor was disposed by DynamicInstrumentation: a forced refresh after disposal samples nothing. + memoryPressureMonitor.RefreshIfStale(long.MaxValue / 2); + memSource.SampleCount.Should().Be(0); + } + + [Fact] + public void DynamicInstrumentation_RefreshesMemoryPressure_OnlyThroughDedicatedHook() + { + var settings = DebuggerSettings.FromSource( + new NameValueConfigurationSource(new() { { ConfigurationKeys.Debugger.DynamicInstrumentationEnabled, "0" }, }), + NullConfigurationTelemetry.Instance); + + var memSource = new CountingMemorySource(); + var memoryPressureMonitor = new MemoryPressureMonitor(MemoryPressureConfig.Default, memSource.TryGetMemoryUsageRatio, memSource.TryGetGen2CollectionCount, null); + var globalRateLimiter = new GlobalRateLimiterMock(); + var debugger = new DynamicInstrumentation( + settings, + new DiscoveryServiceMock(), + new RcmSubscriptionManagerMock(), + new LineProbeResolverMock(), + new SnapshotUploaderMock(), + new LogUploaderMock(), + new UploaderMock(), + new ProbeStatusPollerMock(), + ConfigurationUpdater.Create(string.Empty, string.Empty, 0, globalRateLimiter), + NoOpStatsd.Instance, + globalRateLimiter, + memoryPressureMonitor); + var captureLimit = new CaptureLimitInfo(1, 1, 1, 1); + var fullSnapshotProbe = new ExpressionProbeInfo("snapshot-probe", 0, ProbeType.Snapshot, ExpressionProbeLocation.Method, EvaluateAt.Exit, null, string.Empty, false, [], null, captureLimit); + var logProbe = new ExpressionProbeInfo("log-probe", 0, ProbeType.Log, ExpressionProbeLocation.Method, EvaluateAt.Entry, null, string.Empty, false, [], null, captureLimit); + var metricProbe = new ExpressionProbeInfo("metric-probe", 0, ProbeType.Metric, ExpressionProbeLocation.Method, EvaluateAt.Entry, MetricKind.COUNT, "metric", false, [], null, captureLimit); + + // Emit paths must not sample memory pressure. Sampling is driven once per probe activity + // from ProbeProcessor via RefreshMemoryPressureIfStale(), not from each snapshot/log/metric. + debugger.AddSnapshot(fullSnapshotProbe, "{}"); + debugger.AddLog(logProbe, "{}"); + debugger.SendMetrics(metricProbe, MetricKind.COUNT, "metric", 1, "metric-probe"); + memSource.SampleCount.Should().Be(0); + + debugger.RefreshMemoryPressureIfStale(); + memSource.SampleCount.Should().Be(1); } [Fact] @@ -1467,4 +1516,26 @@ public void Dispose() DisposeCallCount++; } } + + // Counts how many times the real MemoryPressureMonitor sampled memory, so tests can verify the + // monitor is driven only through the dedicated hook and is disposed by DynamicInstrumentation. + private sealed class CountingMemorySource + { + private int _sampleCount; + + public int SampleCount => Volatile.Read(ref _sampleCount); + + public bool TryGetMemoryUsageRatio(out double ratio) + { + Interlocked.Increment(ref _sampleCount); + ratio = 0.1; + return true; + } + + public bool TryGetGen2CollectionCount(out int count) + { + count = 0; + return true; + } + } } diff --git a/tracer/test/Datadog.Trace.Tests/Debugger/RateLimiting/MemoryPressureMonitorTests.cs b/tracer/test/Datadog.Trace.Tests/Debugger/RateLimiting/MemoryPressureMonitorTests.cs new file mode 100644 index 000000000000..7bfb61350425 --- /dev/null +++ b/tracer/test/Datadog.Trace.Tests/Debugger/RateLimiting/MemoryPressureMonitorTests.cs @@ -0,0 +1,582 @@ +// +// 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. +// + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.InteropServices; +using System.Threading; +using System.Threading.Tasks; +using Datadog.Trace.Debugger.RateLimiting; +using Datadog.Trace.Telemetry; +using Datadog.Trace.Telemetry.Metrics; +using FluentAssertions; +using Xunit; + +#nullable enable + +namespace Datadog.Trace.Tests.Debugger.RateLimiting +{ +#if NETFRAMEWORK || NETCOREAPP3_1_OR_GREATER + public class MemoryPressureMonitorTests + { + [Fact] + public void RefreshIfStale_DoesNotSampleWhileIdle() + { + var gc = new CountingGCInfoProvider(); + + using var monitor = CreateMonitor(null, gc.TryGetMemoryUsageRatio, gc.TryGetGen2CollectionCount); + + gc.MemoryRatioCallCount.Should().Be(0); + gc.Gen2CallCount.Should().Be(0); + } + + [Fact] + public void RefreshIfStale_SamplesWhenStaleOnly() + { + var gc = new CountingGCInfoProvider(); + using var monitor = CreateMonitor(null, gc.TryGetMemoryUsageRatio, gc.TryGetGen2CollectionCount); + + monitor.RefreshIfStale(0); + monitor.RefreshIfStale(500); + monitor.RefreshIfStale(1000); + + gc.MemoryRatioCallCount.Should().Be(2); + gc.Gen2CallCount.Should().Be(2); + } + + [Fact] + public async Task ConcurrentRefresh_DoesNotOverlap() + { + using var gc = new BlockingMemoryRatioProvider(blockedRatio: 0.90); + using var monitor = CreateMonitor( + MemoryPressureConfig.Default with { HighPressureThresholdRatio = 0.80, MaxGen2PerSecond = 1000 }, + gc.TryGetMemoryUsageRatio, + gc.TryGetGen2CollectionCount); + + monitor.RefreshIfStale(0); + var refreshTask = Task.Run(() => monitor.RefreshIfStale(1000)); + gc.WaitForBlockedCall(); + + monitor.RefreshIfStale(2000); + gc.MemoryRatioCallCount.Should().Be(2); + + gc.ReleaseBlockedCall(); + await refreshTask; + monitor.IsHighMemoryPressure.Should().BeTrue(); + } + + [Fact] + public async Task Dispose_DuringInFlightRefresh_DoesNotCommitState() + { + using var gc = new BlockingMemoryRatioProvider(blockedRatio: 0.90); + var monitor = CreateMonitor( + MemoryPressureConfig.Default with { HighPressureThresholdRatio = 0.80, MaxGen2PerSecond = 1000 }, + gc.TryGetMemoryUsageRatio, + gc.TryGetGen2CollectionCount); + + monitor.RefreshIfStale(0); + var refreshTask = Task.Run(() => monitor.RefreshIfStale(1000)); + gc.WaitForBlockedCall(); + + var disposeTask = Task.Run(() => monitor.Dispose()); + gc.ReleaseBlockedCall(); + + await refreshTask; + await disposeTask; + monitor.IsHighMemoryPressure.Should().BeFalse(); + } + + [Fact] + public void Dispose_PreventsFurtherRefresh() + { + var gc = new CountingGCInfoProvider(); + var monitor = CreateMonitor(null, gc.TryGetMemoryUsageRatio, gc.TryGetGen2CollectionCount); + + monitor.Dispose(); + monitor.RefreshIfStale(1000); + + gc.MemoryRatioCallCount.Should().Be(0); + monitor.MemoryUsagePercent.Should().Be(0); + monitor.Gen2CollectionsPerSecond.Should().Be(0); + monitor.IsHighMemoryPressure.Should().BeFalse(); + } + + [Fact] + public void MemoryOnly_AppliesThresholdsAndHysteresis() + { + var gc = new MemoryOnlyGCInfoProvider(0.85, 0.76, 0.74); + using var monitor = CreateMonitor( + MemoryPressureConfig.Default with { HighPressureThresholdRatio = 0.80, MaxGen2PerSecond = 1000, MemoryExitMargin = 0.05 }, + gc.TryGetMemoryUsageRatio, + gc.TryGetGen2CollectionCount); + + monitor.RefreshIfStale(0); + monitor.IsHighMemoryPressure.Should().BeTrue(); + monitor.MemoryUsagePercent.Should().BeApproximately(85, 0.1); + + monitor.RefreshIfStale(1000); + monitor.IsHighMemoryPressure.Should().BeTrue(); + + monitor.RefreshIfStale(2000); + monitor.IsHighMemoryPressure.Should().BeFalse(); + } + + [Fact] + public void GcOnly_AppliesThresholdsAndRates() + { + var gc = new GCOnlyInfoProvider([0, 3, 5, 6]); + using var monitor = CreateMonitor( + MemoryPressureConfig.Default with { HighPressureThresholdRatio = 0.99, MaxGen2PerSecond = 2, Gen2ExitMargin = 1 }, + gc.TryGetMemoryUsageRatio, + gc.TryGetGen2CollectionCount); + + monitor.RefreshIfStale(0); + monitor.IsHighMemoryPressure.Should().BeFalse(); + + monitor.RefreshIfStale(1000); + monitor.Gen2CollectionsPerSecond.Should().BeApproximately(3.0, 0.01); + monitor.IsHighMemoryPressure.Should().BeTrue(); + + monitor.RefreshIfStale(2000); + monitor.IsHighMemoryPressure.Should().BeTrue(); + + monitor.RefreshIfStale(3000); + monitor.IsHighMemoryPressure.Should().BeFalse(); + } + + [Fact] + public void ConsecutiveHighAndLowCycles_AreHonored() + { + var config = MemoryPressureConfig.Default with + { + HighPressureThresholdRatio = 0.80, + MaxGen2PerSecond = 1000, + ConsecutiveHighToEnter = 2, + ConsecutiveLowToExit = 2 + }; + var gc = new MemoryOnlyGCInfoProvider(0.90, 0.91, 0.10, 0.10); + using var monitor = CreateMonitor(config, gc.TryGetMemoryUsageRatio, gc.TryGetGen2CollectionCount); + + monitor.RefreshIfStale(0); + monitor.IsHighMemoryPressure.Should().BeFalse(); + + monitor.RefreshIfStale(1000); + monitor.IsHighMemoryPressure.Should().BeTrue(); + + monitor.RefreshIfStale(2000); + monitor.IsHighMemoryPressure.Should().BeTrue(); + + monitor.RefreshIfStale(3000); + monitor.IsHighMemoryPressure.Should().BeFalse(); + } + + [Fact] + public void Monitor_Disables_WhenNoSignalsAvailable() + { + var observer = new TestMemoryPressureObserver(); + var gc = new NoSignalsGCInfoProvider(); + using var monitor = CreateMonitor(null, gc.TryGetMemoryUsageRatio, gc.TryGetGen2CollectionCount, observer); + + monitor.RefreshIfStale(0); + monitor.RefreshIfStale(1000); + + gc.MemoryRatioCallCount.Should().Be(1); + monitor.IsHighMemoryPressure.Should().BeFalse(); + monitor.MemoryUsagePercent.Should().Be(0); + monitor.Gen2CollectionsPerSecond.Should().Be(0); + observer.Disables.Should().ContainSingle().Which.Should().Be(MetricTags.DebuggerMemoryPressureDisabledReason.NoSignals); + } + + [Fact] + public void ProviderException_DoesNotCrashMonitor() + { + var observer = new TestMemoryPressureObserver(); + var gc = new ThrowingGCInfoProvider(); + using var monitor = CreateMonitor(null, gc.TryGetMemoryUsageRatio, gc.TryGetGen2CollectionCount, observer); + + Action act = () => monitor.RefreshIfStale(0); + + act.Should().NotThrow(); + monitor.RefreshIfStale(1000); + gc.MemoryRatioCallCount.Should().Be(1); + monitor.IsHighMemoryPressure.Should().BeFalse(); + observer.Disables.Should().ContainSingle().Which.Should().Be(MetricTags.DebuggerMemoryPressureDisabledReason.Error); + } + + [Fact] + public void Trigger_IsGen2_WhenOnlyGcSignalEntersHigh() + { + var observer = new TestMemoryPressureObserver(); + var gc = new GCOnlyInfoProvider([0, 5]); + using var monitor = CreateMonitor( + MemoryPressureConfig.Default with { HighPressureThresholdRatio = 0.99, MaxGen2PerSecond = 2 }, + gc.TryGetMemoryUsageRatio, + gc.TryGetGen2CollectionCount, + observer); + + monitor.RefreshIfStale(0); + monitor.RefreshIfStale(1000); + + monitor.IsHighMemoryPressure.Should().BeTrue(); + observer.Transitions.Should().ContainSingle().Which.Trigger.Should().Be(MetricTags.DebuggerMemoryPressureTrigger.Gc); + } + + [Fact] + public void GcOnlyTransition_DoesNotReportUnavailableMemoryAsZero() + { + var observer = new TestMemoryPressureObserver(); + var gc = new GCOnlyInfoProvider([0, 5]); + using var monitor = CreateMonitor( + MemoryPressureConfig.Default with { HighPressureThresholdRatio = 0.99, MaxGen2PerSecond = 2 }, + gc.TryGetMemoryUsageRatio, + gc.TryGetGen2CollectionCount, + observer); + + monitor.RefreshIfStale(0); + monitor.RefreshIfStale(1000); + + var transition = observer.Transitions.Should().ContainSingle().Subject; + transition.MemoryUsagePercent.Should().BeNull(); + transition.Gen2CollectionsPerSecond.Should().BeApproximately(5.0, 0.01); + } + + [Fact] + public void GcOnlyTransition_DoesNotRecordMemoryUsageTelemetry() + { + var collector = new MetricsTelemetryCollector(Timeout.InfiniteTimeSpan); + var previousMetrics = TelemetryFactory.SetMetricsForTesting(collector); + try + { + var gc = new GCOnlyInfoProvider([0, 5]); + using var monitor = new MemoryPressureMonitor( + MemoryPressureConfig.Default with { HighPressureThresholdRatio = 0.99, MaxGen2PerSecond = 2 }, + memoryRatioReader: gc.TryGetMemoryUsageRatio, + gen2Reader: gc.TryGetGen2CollectionCount, + onTransition: null); + + monitor.RefreshIfStale(0); + monitor.RefreshIfStale(1000); + collector.AggregateMetrics(); + + var metrics = collector.GetMetrics(); + metrics.Metrics.Should().NotContain(x => + x.Metric == Count.DebuggerMemoryPressureMemoryUsagePct.GetName()); + metrics.Metrics.Should().Contain(x => + x.Metric == Count.DebuggerMemoryPressureGcActivity.GetName() && + x.Tags != null && + x.Tags.Length == 2 && + x.Tags[0] == "state:enter" && + x.Tags[1] == "bucket:gte_5" && + x.Points.Single().Value == 1); + } + finally + { + TelemetryFactory.SetMetricsForTesting(previousMetrics); + } + } + + [Fact] + public void Trigger_IsBoth_WhenMemoryAndGcEnterHighTogether() + { + var observer = new TestMemoryPressureObserver(); + // First cycle stays below the memory threshold so entry only happens on the second cycle, + // where the gen2 rate is also computable - exercising the "both signals high at entry" path. + var gc = new MemoryAndGcInfoProvider(ratios: [0.10, 0.90], gen2Counts: [0, 5]); + using var monitor = CreateMonitor( + MemoryPressureConfig.Default with { HighPressureThresholdRatio = 0.80, MaxGen2PerSecond = 2 }, + gc.TryGetMemoryUsageRatio, + gc.TryGetGen2CollectionCount, + observer); + + monitor.RefreshIfStale(0); + monitor.RefreshIfStale(1000); + + monitor.IsHighMemoryPressure.Should().BeTrue(); + observer.Transitions.Should().ContainSingle().Which.Trigger.Should().Be(MetricTags.DebuggerMemoryPressureTrigger.Both); + } + + [Fact] + public void Observer_EmitsOnceOnEnterAndExit_WithSeverityAndDuration() + { + var observer = new TestMemoryPressureObserver(); + var gc = new MemoryOnlyGCInfoProvider(0.90, 0.91, 0.70, 0.70); + using var monitor = CreateMonitor( + MemoryPressureConfig.Default with { HighPressureThresholdRatio = 0.80, MaxGen2PerSecond = 1000 }, + gc.TryGetMemoryUsageRatio, + gc.TryGetGen2CollectionCount, + observer); + + monitor.RefreshIfStale(0); + monitor.RefreshIfStale(1000); + monitor.RefreshIfStale(2000); + monitor.RefreshIfStale(3000); + + observer.Transitions.Should().HaveCount(2); + observer.Transitions[0].Should().BeEquivalentTo(new Transition(true, MetricTags.DebuggerMemoryPressureTrigger.Memory, 90, null, 0)); + observer.Transitions[1].IsHighPressure.Should().BeFalse(); + observer.Transitions[1].Trigger.Should().Be(MetricTags.DebuggerMemoryPressureTrigger.None); + observer.Transitions[1].MemoryUsagePercent.Should().Be(70); + observer.Transitions[1].HighPressureDurationMs.Should().BeApproximately(2000, 0.1); + } + + [Fact] + public void Constructor_AcceptsExtremeParameterValues() + { + using var monitor1 = new MemoryPressureMonitor(MemoryPressureConfig.Default with { HighPressureThresholdRatio = -0.1, MaxGen2PerSecond = -5 }); + using var monitor2 = new MemoryPressureMonitor(MemoryPressureConfig.Default with { HighPressureThresholdRatio = 2 }); + using var monitor3 = new MemoryPressureMonitor(MemoryPressureConfig.Default with { HighPressureThresholdRatio = 0, MaxGen2PerSecond = 0 }); + + monitor1.Should().NotBeNull(); + monitor2.Should().NotBeNull(); + monitor3.Should().NotBeNull(); + } + + [Fact] + public void WindowsMemoryInfo_TryGetMemoryLoadRatio_ReturnsValidValue() + { + if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + { + return; + } + + WindowsMemoryInfo.TryGetMemoryLoadRatio(out var ratio).Should().BeTrue(); + ratio.Should().BeInRange(0, 1); + } + + [Fact] + public void WindowsMemoryInfo_TryGetAvailablePhysicalMemory_ReturnsValidValue() + { + if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + { + return; + } + + WindowsMemoryInfo.TryGetAvailablePhysicalMemory(out var bytes).Should().BeTrue(); + bytes.Should().BeGreaterThan(0); + } + + // Fakes are passed as production reader delegates (method groups), so no shared test-only interface is needed. + private static MemoryPressureMonitor CreateMonitor( + MemoryPressureConfig? config, + TryReadMemoryUsageRatio memoryReader, + TryReadGen2CollectionCount gen2Reader, + TestMemoryPressureObserver? observer = null) + { + return new MemoryPressureMonitor( + config ?? MemoryPressureConfig.Default, + memoryRatioReader: memoryReader, + gen2Reader: gen2Reader, + onTransition: observer is null ? null : observer.OnTransition, + onDisabled: observer is null ? null : observer.OnDisabled); + } + + private readonly record struct Transition(bool IsHighPressure, MetricTags.DebuggerMemoryPressureTrigger Trigger, double? MemoryUsagePercent, double? Gen2CollectionsPerSecond, double HighPressureDurationMs); + + private sealed class TestMemoryPressureObserver + { + public List Transitions { get; } = []; + + public List Disables { get; } = []; + + public void OnTransition(bool isHighPressure, MetricTags.DebuggerMemoryPressureTrigger trigger, double? memoryUsagePercent, double? gen2CollectionsPerSecond, double highPressureDurationMs) + { + Transitions.Add(new Transition(isHighPressure, trigger, memoryUsagePercent, gen2CollectionsPerSecond, highPressureDurationMs)); + } + + public void OnDisabled(MetricTags.DebuggerMemoryPressureDisabledReason reason) + { + Disables.Add(reason); + } + } + + private sealed class CountingGCInfoProvider + { + public int MemoryRatioCallCount { get; private set; } + + public int Gen2CallCount { get; private set; } + + public bool TryGetGen2CollectionCount(out int count) + { + Gen2CallCount++; + count = 0; + return true; + } + + public bool TryGetMemoryUsageRatio(out double ratio) + { + MemoryRatioCallCount++; + ratio = 0.10; + return true; + } + } + + private sealed class NoSignalsGCInfoProvider + { + public int MemoryRatioCallCount { get; private set; } + + public bool TryGetGen2CollectionCount(out int count) + { + count = 0; + return false; + } + + public bool TryGetMemoryUsageRatio(out double ratio) + { + MemoryRatioCallCount++; + ratio = 0; + return false; + } + } + + private sealed class MemoryOnlyGCInfoProvider + { + private readonly Queue _ratios = new(); + + public MemoryOnlyGCInfoProvider(params double[] ratios) + { + foreach (var ratio in ratios) + { + _ratios.Enqueue(ratio); + } + } + + public bool TryGetGen2CollectionCount(out int count) + { + count = 0; + return false; + } + + public bool TryGetMemoryUsageRatio(out double ratio) + { + ratio = _ratios.Count == 0 ? 0 : _ratios.Dequeue(); + return true; + } + } + + private sealed class GCOnlyInfoProvider + { + private readonly Queue _counts = new(); + + public GCOnlyInfoProvider(int[] counts) + { + foreach (var count in counts) + { + _counts.Enqueue(count); + } + } + + public bool TryGetGen2CollectionCount(out int count) + { + count = _counts.Count == 0 ? 0 : _counts.Dequeue(); + return true; + } + + public bool TryGetMemoryUsageRatio(out double ratio) + { + ratio = 0; + return false; + } + } + + private sealed class MemoryAndGcInfoProvider + { + private readonly Queue _ratios = new(); + private readonly Queue _gen2Counts = new(); + + public MemoryAndGcInfoProvider(double[] ratios, int[] gen2Counts) + { + foreach (var ratio in ratios) + { + _ratios.Enqueue(ratio); + } + + foreach (var count in gen2Counts) + { + _gen2Counts.Enqueue(count); + } + } + + public bool TryGetGen2CollectionCount(out int count) + { + count = _gen2Counts.Count == 0 ? 0 : _gen2Counts.Dequeue(); + return true; + } + + public bool TryGetMemoryUsageRatio(out double ratio) + { + ratio = _ratios.Count == 0 ? 0 : _ratios.Dequeue(); + return true; + } + } + + private sealed class ThrowingGCInfoProvider + { + public int MemoryRatioCallCount { get; private set; } + + public bool TryGetGen2CollectionCount(out int count) + { + count = 0; + throw new InvalidOperationException("Test exception from GC provider"); + } + + public bool TryGetMemoryUsageRatio(out double ratio) + { + MemoryRatioCallCount++; + ratio = 0; + throw new InvalidOperationException("Test exception from memory provider"); + } + } + + private sealed class BlockingMemoryRatioProvider : IDisposable + { + private static readonly TimeSpan WaitTimeout = TimeSpan.FromSeconds(5); + + private readonly ManualResetEventSlim _blocked = new(false); + private readonly ManualResetEventSlim _release = new(false); + private readonly double _blockedRatio; + private int _memoryRatioCallCount; + + public BlockingMemoryRatioProvider(double blockedRatio) + { + _blockedRatio = blockedRatio; + } + + public int MemoryRatioCallCount => Volatile.Read(ref _memoryRatioCallCount); + + public bool TryGetGen2CollectionCount(out int count) + { + count = 0; + return true; + } + + public bool TryGetMemoryUsageRatio(out double ratio) + { + if (Interlocked.Increment(ref _memoryRatioCallCount) == 1) + { + ratio = 0.10; + return true; + } + + _blocked.Set(); + _release.Wait(WaitTimeout).Should().BeTrue("the test should release the blocked memory sample"); + ratio = _blockedRatio; + return true; + } + + public void WaitForBlockedCall() => _blocked.Wait(WaitTimeout).Should().BeTrue("the memory sample should reach the blocking point"); + + public void ReleaseBlockedCall() => _release.Set(); + + public void Dispose() + { + _release.Set(); + _blocked.Dispose(); + _release.Dispose(); + } + } + } +#endif +} diff --git a/tracer/test/Datadog.Trace.Tests/Telemetry/Collectors/MetricsTelemetryCollectorTests.cs b/tracer/test/Datadog.Trace.Tests/Telemetry/Collectors/MetricsTelemetryCollectorTests.cs index 9610d9fedb08..2e63fc0bf788 100644 --- a/tracer/test/Datadog.Trace.Tests/Telemetry/Collectors/MetricsTelemetryCollectorTests.cs +++ b/tracer/test/Datadog.Trace.Tests/Telemetry/Collectors/MetricsTelemetryCollectorTests.cs @@ -705,4 +705,52 @@ public async Task ShouldAggregateMetricsAutomatically() .NotBeEmpty(); // we expect ~10 points, but don't assert that number to avoid flakiness await collector.DisposeAsync(); } + + [Fact] + public async Task DebuggerMemoryPressureMetrics_AreAggregated() + { + var collector = new MetricsTelemetryCollector(Timeout.InfiniteTimeSpan); + + collector.RecordCountDebuggerMemoryPressureTransitions(MetricTags.DebuggerMemoryPressureState.Enter, MetricTags.DebuggerMemoryPressureTrigger.Memory); + collector.RecordCountDebuggerMemoryPressureTransitions(MetricTags.DebuggerMemoryPressureState.Exit, MetricTags.DebuggerMemoryPressureTrigger.None); + collector.RecordCountDebuggerMemoryPressureDisabled(MetricTags.DebuggerMemoryPressureDisabledReason.NoSignals); + collector.RecordCountDebuggerMemoryPressureMemoryUsagePct(MetricTags.DebuggerMemoryPressureState.Enter, MetricTags.DebuggerMemoryPressureMemoryBucket.GreaterThanOrEqual90); + collector.RecordCountDebuggerMemoryPressureGcActivity(MetricTags.DebuggerMemoryPressureState.Enter, MetricTags.DebuggerMemoryPressureGcBucket.From2To5); + collector.RecordCountDebuggerMemoryPressureDuration(MetricTags.DebuggerMemoryPressureDurationBucket.From1To5Seconds); + collector.AggregateMetrics(); + + var metrics = collector.GetMetrics(); + + metrics.Metrics.Should().Contain(x => + x.Metric == Count.DebuggerMemoryPressureTransitions.GetName() && + x.Tags != null && + Enumerable.SequenceEqual(x.Tags, new[] { "state:enter", "trigger:memory" }) && + x.Points.Single().Value == 1); + metrics.Metrics.Should().Contain(x => + x.Metric == Count.DebuggerMemoryPressureTransitions.GetName() && + x.Tags != null && + Enumerable.SequenceEqual(x.Tags, new[] { "state:exit", "trigger:none" }) && + x.Points.Single().Value == 1); + metrics.Metrics.Should().Contain(x => + x.Metric == Count.DebuggerMemoryPressureDisabled.GetName() && + x.Tags != null && + Enumerable.SequenceEqual(x.Tags, new[] { "reason:no_signals" }) && + x.Points.Single().Value == 1); + metrics.Metrics.Should().Contain(x => + x.Metric == Count.DebuggerMemoryPressureMemoryUsagePct.GetName() && + x.Tags != null && + Enumerable.SequenceEqual(x.Tags, new[] { "state:enter", "bucket:gte_90" }) && + x.Points.Single().Value == 1); + metrics.Metrics.Should().Contain(x => + x.Metric == Count.DebuggerMemoryPressureGcActivity.GetName() && + x.Tags != null && + Enumerable.SequenceEqual(x.Tags, new[] { "state:enter", "bucket:2_5" }) && + x.Points.Single().Value == 1); + metrics.Metrics.Should().Contain(x => + x.Metric == Count.DebuggerMemoryPressureDuration.GetName() && + x.Tags != null && + Enumerable.SequenceEqual(x.Tags, new[] { "bucket:1_5s" }) && + x.Points.Single().Value == 1); + await collector.DisposeAsync(); + } } diff --git a/tracer/test/Datadog.Trace.Tests/Telemetry/Metrics/common_metrics.json b/tracer/test/Datadog.Trace.Tests/Telemetry/Metrics/common_metrics.json index 5642c7bb7101..36bffa4363f1 100644 --- a/tracer/test/Datadog.Trace.Tests/Telemetry/Metrics/common_metrics.json +++ b/tracer/test/Datadog.Trace.Tests/Telemetry/Metrics/common_metrics.json @@ -1,4 +1,59 @@ { + "live_debugger": { + "memory_pressure.transitions": { + "tags": [ + "state", + "trigger" + ], + "metric_type": "count", + "data_type": "transitions", + "description": "The number of Dynamic Instrumentation memory-pressure state transitions, tagged by state (`state:enter` or `state:exit`) and the signal that triggered entry (`trigger:none`, `trigger:memory`, `trigger:gc`, or `trigger:both`)", + "send_to_user": false, + "user_tags": [] + }, + "memory_pressure.disabled": { + "tags": [ + "reason" + ], + "metric_type": "count", + "data_type": "occurrences", + "description": "The number of times the Dynamic Instrumentation memory-pressure monitor disabled itself, tagged by reason (`reason:no_signals` or `reason:error`)", + "send_to_user": false, + "user_tags": [] + }, + "memory_pressure.memory_usage_pct": { + "tags": [ + "state", + "bucket" + ], + "metric_type": "count", + "data_type": "occurrences", + "description": "Count of Dynamic Instrumentation memory-pressure transitions, tagged by state (`state:enter` or `state:exit`) and memory load percent bucket at the transition (`bucket:lt_70`, `bucket:70_80`, `bucket:80_85`, `bucket:85_90`, or `bucket:gte_90`). Range buckets are lower-bound inclusive and upper-bound exclusive, so `bucket:80_85` means 80% <= usage < 85%", + "send_to_user": false, + "user_tags": [] + }, + "memory_pressure.gc_activity": { + "tags": [ + "state", + "bucket" + ], + "metric_type": "count", + "data_type": "occurrences", + "description": "Count of Dynamic Instrumentation memory-pressure transitions, tagged by state (`state:enter` or `state:exit`) and GC activity bucket at the transition (`bucket:lt_1`, `bucket:1_2`, `bucket:2_5`, or `bucket:gte_5`). In .NET this is Gen2 collections per second. Range buckets are lower-bound inclusive and upper-bound exclusive, so `bucket:2_5` means 2 <= collections/sec < 5", + "send_to_user": false, + "user_tags": [] + }, + "memory_pressure.duration": { + "tags": [ + "bucket" + ], + "metric_type": "count", + "data_type": "occurrences", + "description": "Count of Dynamic Instrumentation high-memory-pressure episodes, incremented once on exit and tagged by duration bucket (`bucket:lt_1s`, `bucket:1_5s`, `bucket:5_30s`, or `bucket:gte_30s`). Range buckets are lower-bound inclusive and upper-bound exclusive, so `bucket:1_5s` means 1s <= duration < 5s", + "send_to_user": false, + "user_tags": [] + } + }, "general": { "init_time": { "tags": [ @@ -940,14 +995,14 @@ "user_tags": [] }, "suppressed.vulnerabilities": { - "tags": [ - "vulnerability_type" - ], - "metric_type": "count", - "data_type": "event", - "description": "Number of vulnerabilities suppressed", - "send_to_user": false, - "user_tags":[] + "tags": [ + "vulnerability_type" + ], + "metric_type": "count", + "data_type": "event", + "description": "Number of vulnerabilities suppressed", + "send_to_user": false, + "user_tags": [] }, "metastruct.tag.size.exceeded": { "tags": [], @@ -1583,4 +1638,4 @@ "user_tags": [] } } -} \ No newline at end of file +}