From c25b9f1997772fc1e46174252e3ab37b63bbda40 Mon Sep 17 00:00:00 2001 From: Jordan Dominion Date: Sun, 16 Feb 2025 17:11:40 -0500 Subject: [PATCH] Implement process CPU usage measurement --- .../Components/Session/SessionController.cs | 4 ++ .../System/IProcessBase.cs | 6 +++ src/Tgstation.Server.Host/System/Process.cs | 54 ++++++++++++++++++- 3 files changed, 63 insertions(+), 1 deletion(-) diff --git a/src/Tgstation.Server.Host/Components/Session/SessionController.cs b/src/Tgstation.Server.Host/Components/Session/SessionController.cs index ac6d3dd6a3..3992acc6ef 100644 --- a/src/Tgstation.Server.Host/Components/Session/SessionController.cs +++ b/src/Tgstation.Server.Host/Components/Session/SessionController.cs @@ -519,6 +519,10 @@ namespace Tgstation.Server.Host.Components.Session return process.CreateDump(outputFile, minidump, cancellationToken); } + /// + public double MeasureProcessorTimeDelta() + => process.MeasureProcessorTimeDelta(); + /// /// The for . /// diff --git a/src/Tgstation.Server.Host/System/IProcessBase.cs b/src/Tgstation.Server.Host/System/IProcessBase.cs index b95b0bf928..64d11da92d 100644 --- a/src/Tgstation.Server.Host/System/IProcessBase.cs +++ b/src/Tgstation.Server.Host/System/IProcessBase.cs @@ -24,6 +24,12 @@ namespace Tgstation.Server.Host.System /// long? MemoryUsage { get; } + /// + /// Gets the estimated CPU usage fraction of the process based on the last time this was called. + /// + /// The CPU's estimated usage as a value between 0 and 1. + double MeasureProcessorTimeDelta(); + /// /// Set's the owned to a non-normal value. /// diff --git a/src/Tgstation.Server.Host/System/Process.cs b/src/Tgstation.Server.Host/System/Process.cs index f8da0a396b..09ad4aa3af 100644 --- a/src/Tgstation.Server.Host/System/Process.cs +++ b/src/Tgstation.Server.Host/System/Process.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Diagnostics; using System.Threading; using System.Threading.Tasks; @@ -87,6 +87,26 @@ namespace Tgstation.Server.Host.System /// readonly Task? readTask; + /// + /// object for measuring processor time usage. + /// + readonly object processTimeMeasureLock; + + /// + /// The last time was called. + /// + DateTimeOffset lastProcessorMeasureTime; + + /// + /// The last value of . + /// + TimeSpan lastProcessorUsageTime; + + /// + /// The last valid return value of . + /// + double lastProcessorUsageEstimation; + /// /// If the was disposed. /// @@ -123,6 +143,8 @@ namespace Tgstation.Server.Host.System this.logger = logger ?? throw new ArgumentNullException(nameof(logger)); + processTimeMeasureLock = new object(); + Lifetime = WrapLifetimeTask(); if (preExisting) @@ -147,6 +169,8 @@ namespace Tgstation.Server.Host.System DefaultIOManager.BlockingTaskCreationOptions, TaskScheduler.Current); + MeasureProcessorTimeDelta(); + logger.LogTrace("Created process ID: {pid}", Id); } @@ -267,6 +291,34 @@ namespace Tgstation.Server.Host.System return processFeatures.CreateDump(handle, outputFile, minidump, cancellationToken); } + /// + public double MeasureProcessorTimeDelta() + { + lock (processTimeMeasureLock) + { + try + { + var now = DateTimeOffset.UtcNow; + var newTime = handle.TotalProcessorTime; + + var timeDelta = now - lastProcessorMeasureTime; + var newUsage = newTime - lastProcessorUsageTime; + + lastProcessorMeasureTime = now; + lastProcessorUsageTime = newTime; + + if (timeDelta != TimeSpan.Zero) + lastProcessorUsageEstimation = newUsage / (Environment.ProcessorCount * timeDelta); + } + catch (Exception ex) + { + logger.LogWarning(ex, "Error measuring processor time delta!"); + } + + return lastProcessorUsageEstimation; + } + } + /// /// Attaches a log message to the process' exit event. ///