Files
natsnet/dotnet/src/ZB.MOM.NatsNet.Server/Internal/ProcessStatsProvider.cs
Joseph Doherty 88b1391ef0 feat: port session 07 — Protocol Parser, Auth extras (TPM/certidp/certstore), Internal utilities & data structures
Session 07 scope (5 features, 17 tests, ~1165 Go LOC):
- Protocol/ParserTypes.cs: ParserState enum (79 states), PublishArgument, ParseContext
- Protocol/IProtocolHandler.cs: handler interface decoupling parser from client
- Protocol/ProtocolParser.cs: Parse(), ProtoSnippet(), OverMaxControlLineLimit(),
  ProcessPub/HeaderPub/RoutedMsgArgs/RoutedHeaderMsgArgs, ClonePubArg(), GetHeader()
- tests/Protocol/ProtocolParserTests.cs: 17 tests via TestProtocolHandler stub

Auth extras from session 06 (committed separately):
- Auth/TpmKeyProvider.cs, Auth/CertificateIdentityProvider/, Auth/CertificateStore/

Internal utilities & data structures (session 06 overflow):
- Internal/AccessTimeService.cs, ElasticPointer.cs, SystemMemory.cs, ProcessStatsProvider.cs
- Internal/DataStructures/GenericSublist.cs, HashWheel.cs
- Internal/DataStructures/SubjectTree.cs, SubjectTreeNode.cs, SubjectTreeParts.cs

All 461 tests pass (460 unit + 1 integration). DB updated for features 2588-2592 and tests 2598-2614.
2026-02-26 13:16:56 -05:00

107 lines
3.6 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using System.Diagnostics;
namespace ZB.MOM.NatsNet.Server.Internal;
/// <summary>
/// Provides cross-platform process CPU and memory usage statistics.
/// Mirrors the Go <c>pse</c> (Process Status Emulation) package, replacing
/// per-platform implementations (rusage, /proc/stat, PDH) with
/// <see cref="System.Diagnostics.Process"/>.
/// </summary>
public static class ProcessStatsProvider
{
private static readonly Process _self = Process.GetCurrentProcess();
private static readonly int _processorCount = Environment.ProcessorCount;
private static readonly object _lock = new();
private static TimeSpan _lastCpuTime;
private static DateTime _lastSampleTime;
private static double _cachedPcpu;
private static long _cachedRss;
private static long _cachedVss;
static ProcessStatsProvider()
{
UpdateUsage();
StartPeriodicSampling();
}
/// <summary>
/// Returns the current process CPU percentage, RSS (bytes), and VSS (bytes).
/// Values are refreshed approximately every second by a background timer.
/// </summary>
/// <param name="pcpu">Percent CPU utilization (0100 × core count).</param>
/// <param name="rss">Resident set size in bytes.</param>
/// <param name="vss">Virtual memory size in bytes.</param>
public static void ProcUsage(out double pcpu, out long rss, out long vss)
{
lock (_lock)
{
pcpu = _cachedPcpu;
rss = _cachedRss;
vss = _cachedVss;
}
}
private static void UpdateUsage()
{
try
{
_self.Refresh();
var now = DateTime.UtcNow;
var cpuTime = _self.TotalProcessorTime;
lock (_lock)
{
var elapsed = now - _lastSampleTime;
if (elapsed >= TimeSpan.FromMilliseconds(500))
{
var cpuDelta = (cpuTime - _lastCpuTime).TotalSeconds;
// Normalize against elapsed wall time.
// Result is 0100; does not multiply by ProcessorCount to match Go behaviour.
_cachedPcpu = elapsed.TotalSeconds > 0
? Math.Round(cpuDelta / elapsed.TotalSeconds * 1000.0) / 10.0
: 0;
_lastSampleTime = now;
_lastCpuTime = cpuTime;
}
_cachedRss = _self.WorkingSet64;
_cachedVss = _self.VirtualMemorySize64;
}
}
catch
{
// Suppress — diagnostics should never crash the server.
}
}
private static void StartPeriodicSampling()
{
var timer = new Timer(_ => UpdateUsage(), null,
dueTime: TimeSpan.FromSeconds(1),
period: TimeSpan.FromSeconds(1));
// Keep timer alive for the process lifetime.
GC.KeepAlive(timer);
}
// --- Windows PDH helpers (replaced by Process class in .NET) ---
// The following methods exist to satisfy the porting mapping but delegate
// to the cross-platform Process API above.
internal static string GetProcessImageName() =>
Path.GetFileNameWithoutExtension(Environment.ProcessPath ?? _self.ProcessName);
internal static void InitCounters()
{
// No-op: .NET Process class initializes lazily.
}
internal static double PdhOpenQuery() => 0; // Mapped to Process API.
internal static double PdhAddCounter() => 0;
internal static double PdhCollectQueryData() => 0;
internal static double PdhGetFormattedCounterArrayDouble() => 0;
internal static double GetCounterArrayData() => 0;
}