Skip to content
47 changes: 23 additions & 24 deletions tools/scripts/Heartbeat.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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))
Copy link

Copilot AI Dec 15, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The double.TryParse call should specify CultureInfo.InvariantCulture to ensure consistent parsing across different system locales. PowerShell may output decimal numbers with culture-specific separators (e.g., comma vs. period), which could cause parsing to fail on systems with non-English locales. Change to double.TryParse(trimmed, NumberStyles.Float, CultureInfo.InvariantCulture, out var loadPercentage) for consistency with other parsing operations in this file (see lines 130, 194, 222).

Suggested change
if (double.TryParse(trimmed, out var loadPercentage))
if (double.TryParse(trimmed, NumberStyles.Float, CultureInfo.InvariantCulture, out var loadPercentage))

Copilot uses AI. Check for mistakes.
{
return $"{match.Groups[1].Value}%";
return $"{loadPercentage:F1}%";
}
}
}
Expand Down Expand Up @@ -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))
Comment on lines +264 to +265
Copy link

Copilot AI Dec 15, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The long.TryParse calls should specify NumberStyles.Integer and CultureInfo.InvariantCulture to ensure consistent parsing across different system locales. This prevents potential parsing failures on systems with non-English regional settings. Change to long.TryParse(parts[0].Trim(), NumberStyles.Integer, CultureInfo.InvariantCulture, out var freeKb) and long.TryParse(parts[1].Trim(), NumberStyles.Integer, CultureInfo.InvariantCulture, out var totalKb) for consistency with other parsing operations in this file.

Suggested change
long.TryParse(parts[0].Trim(), out var freeKb) &&
long.TryParse(parts[1].Trim(), out var totalKb))
long.TryParse(parts[0].Trim(), NumberStyles.Integer, CultureInfo.InvariantCulture, out var freeKb) &&
long.TryParse(parts[1].Trim(), NumberStyles.Integer, CultureInfo.InvariantCulture, out var totalKb))

Copilot uses AI. Check for mistakes.
{
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;
Expand Down Expand Up @@ -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))
Comment on lines +363 to +364
Copy link

Copilot AI Dec 15, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The int.TryParse and long.TryParse calls should specify NumberStyles and CultureInfo.InvariantCulture to ensure consistent parsing across different system locales. This prevents potential parsing failures on systems with non-English regional settings. Change to int.TryParse(parts[1], NumberStyles.Integer, CultureInfo.InvariantCulture, out var pid) and long.TryParse(parts[2], NumberStyles.Integer, CultureInfo.InvariantCulture, out var workingSet) for consistency with other parsing operations in this file.

Suggested change
int.TryParse(parts[1], out var pid) &&
long.TryParse(parts[2], out var workingSet))
int.TryParse(parts[1], NumberStyles.Integer, CultureInfo.InvariantCulture, out var pid) &&
long.TryParse(parts[2], NumberStyles.Integer, CultureInfo.InvariantCulture, out var workingSet))

Copilot uses AI. Check for mistakes.
{
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));
}
}
}
Expand Down
Loading