Skip to content

Commit 54e0a00

Browse files
Copilotdavidfowl
andauthored
Fix heartbeat monitoring on Windows - replace deprecated wmic with PowerShell (dotnet#13555)
* Initial plan * Fix heartbeat monitoring on Windows for CPU and DCP detection Co-authored-by: davidfowl <[email protected]> * Remove redundant Trim calls since StringSplitOptions.TrimEntries already handles trimming Co-authored-by: davidfowl <[email protected]> * Remove redundant IsNullOrWhiteSpace check for CSV parsing Co-authored-by: davidfowl <[email protected]> * Remove redundant dcp name check since wmic query already filters by name Co-authored-by: davidfowl <[email protected]> * Replace wmic with PowerShell commands for Windows compatibility Co-authored-by: davidfowl <[email protected]> * Improve PowerShell commands for multi-processor CPU and robust CSV parsing Co-authored-by: davidfowl <[email protected]> * Optimize PowerShell commands for better performance Co-authored-by: davidfowl <[email protected]> --------- Co-authored-by: copilot-swe-agent[bot] <[email protected]> Co-authored-by: davidfowl <[email protected]>
1 parent 5743f3c commit 54e0a00

File tree

1 file changed

+23
-24
lines changed

1 file changed

+23
-24
lines changed

tools/scripts/Heartbeat.cs

Lines changed: 23 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -167,14 +167,15 @@ string GetCpuUsage(ref long prevIdle, ref long prevTotal, ref TimeSpan prevCpu,
167167
}
168168
else if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
169169
{
170-
// Use wmic for Windows
171-
var (success, output) = RunCommand("wmic", "cpu get loadpercentage /value");
170+
// Use PowerShell for Windows (wmic is deprecated)
171+
// Get average CPU load across all processors
172+
var (success, output) = RunCommand("powershell", "-NoProfile -NonInteractive -Command \"(Get-CimInstance Win32_Processor | Measure-Object -Property LoadPercentage -Average).Average\"");
172173
if (success)
173174
{
174-
var match = System.Text.RegularExpressions.Regex.Match(output, @"LoadPercentage=(\d+)");
175-
if (match.Success)
175+
var trimmed = output.Trim();
176+
if (double.TryParse(trimmed, out var loadPercentage))
176177
{
177-
return $"{match.Groups[1].Value}%";
178+
return $"{loadPercentage:F1}%";
178179
}
179180
}
180181
}
@@ -254,19 +255,16 @@ long GetPages(string key) =>
254255
}
255256
else if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
256257
{
257-
// Use GC memory info as a baseline, plus wmic for system memory
258-
var (success, output) = RunCommand("wmic", "OS get FreePhysicalMemory,TotalVisibleMemorySize /value");
258+
// Use PowerShell for Windows (wmic is deprecated)
259+
var (success, output) = RunCommand("powershell", "-NoProfile -NonInteractive -Command \"$os = Get-CimInstance Win32_OperatingSystem; Write-Host \\\"$($os.FreePhysicalMemory),$($os.TotalVisibleMemorySize)\\\"\"");
259260
if (success)
260261
{
261-
var freeMatch = System.Text.RegularExpressions.Regex.Match(output, @"FreePhysicalMemory=(\d+)");
262-
var totalMatch = System.Text.RegularExpressions.Regex.Match(output, @"TotalVisibleMemorySize=(\d+)");
263-
264-
if (freeMatch.Success && totalMatch.Success)
262+
var parts = output.Trim().Split(',');
263+
if (parts.Length == 2 &&
264+
long.TryParse(parts[0].Trim(), out var freeKb) &&
265+
long.TryParse(parts[1].Trim(), out var totalKb))
265266
{
266-
var freeKb = long.Parse(freeMatch.Groups[1].Value, CultureInfo.InvariantCulture);
267-
var totalKb = long.Parse(totalMatch.Groups[1].Value, CultureInfo.InvariantCulture);
268267
var usedKb = totalKb - freeKb;
269-
270268
var totalGb = totalKb / 1024.0 / 1024.0;
271269
var usedGb = usedKb / 1024.0 / 1024.0;
272270
var pct = totalKb > 0 ? (100.0 * usedKb / totalKb) : 0;
@@ -351,21 +349,22 @@ string GetDcpProcesses()
351349

352350
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
353351
{
354-
// Use wmic to find dcp processes on Windows
355-
var (success, output) = RunCommand("wmic", "process where \"name like 'dcp%'\" get ProcessId,Name,WorkingSetSize /format:csv", timeoutMs: 5000);
352+
// Use PowerShell to find dcp processes on Windows (wmic is deprecated)
353+
// Use pipe delimiter to avoid issues with commas in process names
354+
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);
356355
if (success)
357356
{
358-
var lines = output.Split('\n', StringSplitOptions.RemoveEmptyEntries).Skip(1); // Skip header
357+
var lines = output.Split('\n', StringSplitOptions.RemoveEmptyEntries);
359358
foreach (var line in lines)
360359
{
361-
var parts = line.Split(',');
362-
if (parts.Length >= 4)
360+
var parts = line.Split('|', StringSplitOptions.TrimEntries);
361+
362+
if (parts.Length >= 3 &&
363+
int.TryParse(parts[1], out var pid) &&
364+
long.TryParse(parts[2], out var workingSet))
363365
{
364-
var name = parts[1];
365-
if (int.TryParse(parts[2], out var pid) && long.TryParse(parts[3], out var workingSet))
366-
{
367-
dcpProcesses.Add((name, pid, 0, workingSet / 1024.0 / 1024.0));
368-
}
366+
var name = parts[0];
367+
dcpProcesses.Add((name, pid, 0, workingSet / 1024.0 / 1024.0));
369368
}
370369
}
371370
}

0 commit comments

Comments
 (0)