How to Monitor CPU Usage and Processes on Windows Server 2012 as a Service for 24/7 Performance Analysis


28 views

When troubleshooting performance issues on Windows Server 2012, especially those occurring during unattended operations, traditional monitoring tools like Task Manager or Process Monitor fall short. The key requirements are:

  • Service-based operation (no RDP session dependency)
  • Long-duration monitoring (24+ hours)
  • Process-level CPU usage tracking
  • Historical data analysis capability

Windows Performance Monitor (PerfMon) is the built-in tool that meets all these requirements. Here's how to set it up:

# PowerShell script to create a data collector set
$DataCollectorSetName = "24h_CPU_Process_Monitor"
$OutputPath = "C:\PerfLogs\CPU_Monitoring"

# Create the data collector set
logman create counter $DataCollectorSetName -o $OutputPath -f bin -v mmddhhmm -max 1000 -c "\Process(*)\% Processor Time" "\Processor(_Total)\% Processor Time" -si 15

To capture both process names and CPU usage:

  1. Create a new Data Collector Set in Performance Monitor
  2. Add these counters:
    • \Process(*)\% Processor Time
    • \Process(*)\ID Process
  3. Set sample interval to 15-30 seconds
  4. Configure output as binary log (.blg) for efficient storage

To ensure monitoring continues after disconnection:

# Start the collector set and configure for service operation
logman start $DataCollectorSetName
logman update $DataCollectorSetName -b 01/01/2024 00:00:00 -e 01/02/2024 00:00:00
schtasks /create /tn "CPU_Monitor" /tr "logman start $DataCollectorSetName" /sc once /st 23:50 /ru "SYSTEM"

When performance issues occur, use PowerShell to parse the logs:

# PowerShell analysis script
$logPath = "C:\PerfLogs\CPU_Monitoring\*.blg"
$timeOfInterest = "03:15 AM"

$data = Import-Counter $logPath
$problemData = $data | Where-Object {$_.TimeStamp -like "*$timeOfInterest*"}

$processes = $problemData.CounterSamples | 
    Where-Object {$_.Path -like "*\Process(*)\% Processor Time*"} |
    Sort-Object -Property CookedValue -Descending |
    Select-Object -First 10

$processes | ForEach-Object {
    $processName = $_.Path.Split(')')[0].Split('(')[1]
    [PSCustomObject]@{
        Time = $_.TimeStamp
        Process = $processName
        CPU = [math]::Round($_.CookedValue, 2)
        PID = ($problemData.CounterSamples | 
            Where-Object {$_.Path -like "*\Process($processName)\ID Process*"}).CookedValue
    }
} | Format-Table -AutoSize

For more advanced monitoring, consider:

  • Windows Event Forwarding for centralized logging
  • PowerShell DSC for configuration management
  • Third-party tools like PRTG or Zabbix

When troubleshooting intermittent performance issues on Windows Server 2012, the main obstacles are:

  • Group Policy enforced session timeouts (10 minutes idle disconnect)
  • Need for 24/7 monitoring capability
  • Requirement for process-level CPU attribution
  • Historical data analysis capability

Windows Performance Monitor (PerfMon) can be configured as a service through Data Collector Sets. Here's how to set it up:

# PowerShell script to create and start a Data Collector Set
$DataCollectorSetName = "ProcessCPUUsage"
$OutputDirectory = "C:\\PerfLogs\\ProcessCPU"

# Create the data collector set
logman create counter $DataCollectorSetName -o $OutputDirectory -f csv -v mmddhhmm -c "\Process(*)\% Processor Time"

# Set sample interval to 15 seconds
logman update $DataCollectorSetName -si 15

# Start the collector
logman start $DataCollectorSetName

For more detailed analysis, WPR provides ETW-based tracing:

# Start recording with process CPU profile
wpr -start CPU -start GeneralProfile -filemode

# After 24 hours, stop and save
wpr -stop C:\\PerfLogs\\ProcessCPU.etl

For CSV output from PerfMon, use PowerShell to analyze:

# Find peak CPU usage periods
$data = Import-Csv "C:\\PerfLogs\\ProcessCPU\\*.csv" | 
        Where-Object { $_.'Process(*)\% Processor Time' -gt 80 }

# Group by process and show max CPU
$data | Group-Object "Process Name" | 
         Select-Object Name, 
                      @{Name="MaxCPU";Expression={($_.Group | Measure-Object -Property "Process(*)\% Processor Time" -Maximum).Maximum}}

To make this persistent across reboots:

# Create scheduled task to start monitoring at startup
$action = New-ScheduledTaskAction -Execute "logman.exe" -Argument "start ProcessCPUUsage"
$trigger = New-ScheduledTaskTrigger -AtStartup
Register-ScheduledTask -TaskName "Process CPU Monitor" -Action $action -Trigger $trigger -User "SYSTEM"

For graphical analysis of collected data:

  • Use Performance Monitor to load the CSV and create graphs
  • Import ETL files into Windows Performance Analyzer (WPA)
  • Use PowerShell's Out-GridView for quick filtering