diff --git a/tools/scripts/Heartbeat.cs b/tools/scripts/Heartbeat.cs index 3c6c70ab73a..b56855df92e 100644 --- a/tools/scripts/Heartbeat.cs +++ b/tools/scripts/Heartbeat.cs @@ -167,14 +167,15 @@ string GetCpuUsage(ref long prevIdle, ref long prevTotal, ref TimeSpan prevCpu, } else if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { - // Use wmic for Windows - var (success, output) = RunCommand("wmic", "cpu get loadpercentage /value"); + // Use PowerShell for Windows (wmic is deprecated) + // Get average CPU load across all processors + var (success, output) = RunCommand("powershell", "-NoProfile -NonInteractive -Command \"(Get-CimInstance Win32_Processor | Measure-Object -Property LoadPercentage -Average).Average\""); if (success) { - var match = System.Text.RegularExpressions.Regex.Match(output, @"LoadPercentage=(\d+)"); - if (match.Success) + var trimmed = output.Trim(); + if (double.TryParse(trimmed, out var loadPercentage)) { - return $"{match.Groups[1].Value}%"; + return $"{loadPercentage:F1}%"; } } } @@ -254,19 +255,16 @@ long GetPages(string key) => } else if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { - // Use GC memory info as a baseline, plus wmic for system memory - var (success, output) = RunCommand("wmic", "OS get FreePhysicalMemory,TotalVisibleMemorySize /value"); + // Use PowerShell for Windows (wmic is deprecated) + var (success, output) = RunCommand("powershell", "-NoProfile -NonInteractive -Command \"$os = Get-CimInstance Win32_OperatingSystem; Write-Host \\\"$($os.FreePhysicalMemory),$($os.TotalVisibleMemorySize)\\\"\""); if (success) { - var freeMatch = System.Text.RegularExpressions.Regex.Match(output, @"FreePhysicalMemory=(\d+)"); - var totalMatch = System.Text.RegularExpressions.Regex.Match(output, @"TotalVisibleMemorySize=(\d+)"); - - if (freeMatch.Success && totalMatch.Success) + var parts = output.Trim().Split(','); + if (parts.Length == 2 && + long.TryParse(parts[0].Trim(), out var freeKb) && + long.TryParse(parts[1].Trim(), out var totalKb)) { - var freeKb = long.Parse(freeMatch.Groups[1].Value, CultureInfo.InvariantCulture); - var totalKb = long.Parse(totalMatch.Groups[1].Value, CultureInfo.InvariantCulture); var usedKb = totalKb - freeKb; - var totalGb = totalKb / 1024.0 / 1024.0; var usedGb = usedKb / 1024.0 / 1024.0; var pct = totalKb > 0 ? (100.0 * usedKb / totalKb) : 0; @@ -351,21 +349,22 @@ string GetDcpProcesses() if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { - // Use wmic to find dcp processes on Windows - var (success, output) = RunCommand("wmic", "process where \"name like 'dcp%'\" get ProcessId,Name,WorkingSetSize /format:csv", timeoutMs: 5000); + // Use PowerShell to find dcp processes on Windows (wmic is deprecated) + // Use pipe delimiter to avoid issues with commas in process names + var (success, output) = RunCommand("powershell", "-NoProfile -NonInteractive -Command \"Get-Process -Name 'dcp*' -ErrorAction SilentlyContinue | ForEach-Object { '{0}|{1}|{2}' -f $_.ProcessName, $_.Id, $_.WorkingSet64 }\"", timeoutMs: 5000); if (success) { - var lines = output.Split('\n', StringSplitOptions.RemoveEmptyEntries).Skip(1); // Skip header + var lines = output.Split('\n', StringSplitOptions.RemoveEmptyEntries); foreach (var line in lines) { - var parts = line.Split(','); - if (parts.Length >= 4) + var parts = line.Split('|', StringSplitOptions.TrimEntries); + + if (parts.Length >= 3 && + int.TryParse(parts[1], out var pid) && + long.TryParse(parts[2], out var workingSet)) { - var name = parts[1]; - if (int.TryParse(parts[2], out var pid) && long.TryParse(parts[3], out var workingSet)) - { - dcpProcesses.Add((name, pid, 0, workingSet / 1024.0 / 1024.0)); - } + var name = parts[0]; + dcpProcesses.Add((name, pid, 0, workingSet / 1024.0 / 1024.0)); } } }