收敛稳定性风险
This commit is contained in:
@@ -0,0 +1,171 @@
|
||||
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."
|
||||
Reference in New Issue
Block a user