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


4 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