Skip to content

Heartbeat.cs: Replace PowerShell process spawns with .NET APIs on Windows#15888

Draft
Copilot wants to merge 2 commits intomainfrom
copilot/replace-powershell-spawns-with-dotnet-apis
Draft

Heartbeat.cs: Replace PowerShell process spawns with .NET APIs on Windows#15888
Copilot wants to merge 2 commits intomainfrom
copilot/replace-powershell-spawns-with-dotnet-apis

Conversation

Copy link
Copy Markdown
Contributor

Copilot AI commented Apr 4, 2026

On Windows CI runners, the heartbeat monitor spawned 5 separate powershell.exe processes per cycle — each loading the full PS runtime (~100MB, high CPU on init) — contributing to runner hangs on 2-core windows-latest agents.

Changes

All 5 Windows metric collection paths now use zero-spawn .NET equivalents:

Metric Before After
CPU % Get-CimInstance Win32_Processor GetSystemTimes P/Invoke — idle/kernel/user time deltas (same approach as Linux /proc/stat)
Memory Get-CimInstance Win32_OperatingSystem GlobalMemoryStatusEx P/Invoke — single syscall
DCP processes Get-Process -Name 'dcp*' Process.GetProcesses() filtered by name prefix
Top 10 processes Get-Process | Sort-Object CPU Same Process.GetProcesses() call, sorted by CPU%
Disk Get-PSDrive -PSProvider FileSystem DriveInfo.GetDrives()

Key optimizations:

  • Process.GetProcesses() is called once per cycle and shared between DCP and Top metrics, with process handles disposed after both reads complete.
  • DllImport is used over LibraryImport (the script's auto-generated project doesn't have AllowUnsafeBlocks); SYSLIB1054 suppressed via #pragma warning disable.
  • FILETIME.ToLong() explicitly casts both fields to long before bitwise OR to prevent sign-extension overflow.
  • Linux and macOS code paths are unchanged.

Checklist

  • Is this feature complete?
    • Yes. Ready to ship.
    • No. Follow-up changes expected.
  • Are you including unit tests for the changes and scenario tests if relevant?
    • Yes
    • No
  • Did you add public API?
    • Yes
    • No
  • Does the change make any security assumptions or guarantees?
    • Yes
    • No
  • Does the change require an update in our Aspire docs?
    • Yes
    • No

…indows

- CPU: Use GetSystemTimes P/Invoke (kernel32.dll) instead of Get-CimInstance Win32_Processor
- Memory: Use GlobalMemoryStatusEx P/Invoke (kernel32.dll) instead of Get-CimInstance Win32_OperatingSystem
- DCP processes: Use Process.GetProcesses() filtered by name instead of PowerShell Get-Process
- Top processes: Use Process.GetProcesses() sorted by CPU instead of PowerShell Get-Process
- Disk: Use DriveInfo.GetDrives() instead of PowerShell Get-PSDrive
- Share single Process.GetProcesses() call per cycle between DCP and Top metrics
- Fix FILETIME.ToLong() to cast dwLowDateTime to long before bitwise OR (prevents overflow)
- Apply consistent Math.Round to CPU% in GetTopProcesses to match GetDcpProcesses behavior

Agent-Logs-Url: https://github.com/microsoft/aspire/sessions/980b8145-b029-436f-938b-b3f6da2078c7

Co-authored-by: radical <1472+radical@users.noreply.github.com>
Copilot AI changed the title [WIP] Replace PowerShell process spawns with .NET APIs on Windows Heartbeat.cs: Replace PowerShell process spawns with .NET APIs on Windows Apr 4, 2026
Copilot AI requested a review from radical April 4, 2026 02:16
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Heartbeat.cs: Replace PowerShell process spawns with .NET APIs on Windows

2 participants