172 lines
5.4 KiB
PowerShell
172 lines
5.4 KiB
PowerShell
param(
|
|
[string]$ProcessName = "QtDesktopPet",
|
|
[Alias("Pid")]
|
|
[int]$ProcessId = 0,
|
|
[ValidateRange(1, 86400)]
|
|
[int]$IntervalSeconds = 5,
|
|
[ValidateRange(1, 604800)]
|
|
[int]$DurationSeconds = 300,
|
|
[string]$OutputPath = "reports/perf"
|
|
)
|
|
|
|
Set-StrictMode -Version Latest
|
|
$ErrorActionPreference = "Stop"
|
|
|
|
function Resolve-TargetProcess {
|
|
param(
|
|
[string]$Name,
|
|
[int]$Id
|
|
)
|
|
|
|
if ($Id -gt 0) {
|
|
return Get-Process -Id $Id -ErrorAction SilentlyContinue
|
|
}
|
|
|
|
$processes = @(Get-Process -Name $Name -ErrorAction SilentlyContinue)
|
|
if ($processes.Count -eq 0) {
|
|
return $null
|
|
}
|
|
|
|
if ($processes.Count -gt 1) {
|
|
Write-Warning "Multiple processes named '$Name' were found. Sampling PID $($processes[0].Id). Use -Pid to target a specific process."
|
|
}
|
|
|
|
return $processes[0]
|
|
}
|
|
|
|
function Read-ProcessSnapshot {
|
|
param(
|
|
[System.Diagnostics.Process]$Process
|
|
)
|
|
|
|
$path = ""
|
|
try {
|
|
$path = $Process.Path
|
|
}
|
|
catch {
|
|
$path = ""
|
|
}
|
|
|
|
$responding = ""
|
|
try {
|
|
$responding = $Process.Responding
|
|
}
|
|
catch {
|
|
$responding = ""
|
|
}
|
|
|
|
return [pscustomobject]@{
|
|
TimestampUtc = (Get-Date).ToUniversalTime().ToString("o")
|
|
Pid = $Process.Id
|
|
CpuSeconds = [double]($Process.CPU)
|
|
WorkingSetMB = [math]::Round($Process.WorkingSet64 / 1MB, 2)
|
|
PrivateMemoryMB = [math]::Round($Process.PrivateMemorySize64 / 1MB, 2)
|
|
HandleCount = $Process.HandleCount
|
|
ThreadCount = $Process.Threads.Count
|
|
Responding = $responding
|
|
Path = $path
|
|
}
|
|
}
|
|
|
|
function New-OutputFilePath {
|
|
param(
|
|
[string]$Directory,
|
|
[string]$Name
|
|
)
|
|
|
|
if (-not (Test-Path -LiteralPath $Directory)) {
|
|
New-Item -ItemType Directory -Force -Path $Directory | Out-Null
|
|
}
|
|
|
|
$safeName = $Name -replace '[^a-zA-Z0-9._-]', '_'
|
|
$timestamp = Get-Date -Format "yyyyMMdd-HHmmss"
|
|
return Join-Path $Directory "$timestamp-$safeName.csv"
|
|
}
|
|
|
|
$target = Resolve-TargetProcess -Name $ProcessName -Id $ProcessId
|
|
if ($null -eq $target) {
|
|
if ($ProcessId -gt 0) {
|
|
Write-Error "Process with PID $ProcessId was not found."
|
|
}
|
|
else {
|
|
Write-Error "Process '$ProcessName' was not found. Start the app first or pass -Pid."
|
|
}
|
|
exit 1
|
|
}
|
|
|
|
$logicalProcessorCount = [Environment]::ProcessorCount
|
|
if ($logicalProcessorCount -lt 1) {
|
|
$logicalProcessorCount = 1
|
|
}
|
|
|
|
$outputFile = New-OutputFilePath -Directory $OutputPath -Name $target.ProcessName
|
|
$sampleCount = [math]::Max(1, [math]::Ceiling($DurationSeconds / $IntervalSeconds))
|
|
|
|
Write-Host "Sampling PID $($target.Id) ($($target.ProcessName)) every $IntervalSeconds seconds for up to $DurationSeconds seconds."
|
|
Write-Host "Output: $outputFile"
|
|
|
|
$previousSnapshot = Read-ProcessSnapshot -Process $target
|
|
$previousTime = Get-Date
|
|
$firstRow = [pscustomobject]@{
|
|
TimestampUtc = $previousSnapshot.TimestampUtc
|
|
Pid = $previousSnapshot.Pid
|
|
CpuPercent = 0
|
|
WorkingSetMB = $previousSnapshot.WorkingSetMB
|
|
PrivateMemoryMB = $previousSnapshot.PrivateMemoryMB
|
|
HandleCount = $previousSnapshot.HandleCount
|
|
ThreadCount = $previousSnapshot.ThreadCount
|
|
Responding = $previousSnapshot.Responding
|
|
Path = $previousSnapshot.Path
|
|
Status = "running"
|
|
}
|
|
$firstRow | Export-Csv -Path $outputFile -NoTypeInformation -Encoding UTF8
|
|
|
|
for ($index = 1; $index -le $sampleCount; $index++) {
|
|
Start-Sleep -Seconds $IntervalSeconds
|
|
|
|
$currentProcess = Get-Process -Id $target.Id -ErrorAction SilentlyContinue
|
|
if ($null -eq $currentProcess) {
|
|
[pscustomobject]@{
|
|
TimestampUtc = (Get-Date).ToUniversalTime().ToString("o")
|
|
Pid = $target.Id
|
|
CpuPercent = ""
|
|
WorkingSetMB = ""
|
|
PrivateMemoryMB = ""
|
|
HandleCount = ""
|
|
ThreadCount = ""
|
|
Responding = ""
|
|
Path = $previousSnapshot.Path
|
|
Status = "exited"
|
|
} | Export-Csv -Path $outputFile -NoTypeInformation -Encoding UTF8 -Append
|
|
|
|
Write-Warning "Process $($target.Id) exited. Sampling stopped."
|
|
break
|
|
}
|
|
|
|
$currentSnapshot = Read-ProcessSnapshot -Process $currentProcess
|
|
$currentTime = Get-Date
|
|
$elapsedSeconds = [math]::Max(0.001, ($currentTime - $previousTime).TotalSeconds)
|
|
$cpuPercent = (($currentSnapshot.CpuSeconds - $previousSnapshot.CpuSeconds) / $elapsedSeconds) * 100 / $logicalProcessorCount
|
|
if ($cpuPercent -lt 0) {
|
|
$cpuPercent = 0
|
|
}
|
|
|
|
[pscustomobject]@{
|
|
TimestampUtc = $currentSnapshot.TimestampUtc
|
|
Pid = $currentSnapshot.Pid
|
|
CpuPercent = [math]::Round($cpuPercent, 2)
|
|
WorkingSetMB = $currentSnapshot.WorkingSetMB
|
|
PrivateMemoryMB = $currentSnapshot.PrivateMemoryMB
|
|
HandleCount = $currentSnapshot.HandleCount
|
|
ThreadCount = $currentSnapshot.ThreadCount
|
|
Responding = $currentSnapshot.Responding
|
|
Path = $currentSnapshot.Path
|
|
Status = "running"
|
|
} | Export-Csv -Path $outputFile -NoTypeInformation -Encoding UTF8 -Append
|
|
|
|
$previousSnapshot = $currentSnapshot
|
|
$previousTime = $currentTime
|
|
}
|
|
|
|
Write-Host "Sampling finished."
|