|
| 1 | +<# |
| 2 | +.SYNOPSIS |
| 3 | + Continuously runs `cargo clean` followed by `cargo build` in a loop. |
| 4 | + Logs comprehensive performance and battery statistics per iteration. |
| 5 | +
|
| 6 | +.DESCRIPTION |
| 7 | + - Runs a self-test before starting. If self-test fails, prints error and exits. |
| 8 | + - On start, prints initial line with date/time, battery, mode, etc. |
| 9 | + - For each iteration (called a "run"): |
| 10 | + * Executes cargo clean and cargo build |
| 11 | + * Measures times and increments counters |
| 12 | + * Sleeps a configured interval |
| 13 | + * Tracks stats per power-mode combination: |
| 14 | + - Total mode time |
| 15 | + - Total build/clean times |
| 16 | + - Build count |
| 17 | + * Calculates ratios: |
| 18 | + - Builds per % battery drop |
| 19 | + - Build time per % battery drop |
| 20 | + - Wall time per % battery drop |
| 21 | + * Prints a multi-line summary for each iteration: |
| 22 | + 1) Overall line (date/time, run #, mode, battery, total times) |
| 23 | + 2) Indented line with sleep info and last build output |
| 24 | + 3) One indented line per mode with detailed stats and ratios |
| 25 | + - On command failure, prints the error output and stops. |
| 26 | +
|
| 27 | + Time formats: |
| 28 | + - Display time: "yyyy-MM-dd HH:mm:sss" (with 's' appended at the end) |
| 29 | + - File names: "yyyy-MM-dd_HH-mm-ss" |
| 30 | + - Time spans: "HH:MM:SSs" for total times |
| 31 | + - Averages and ratios in seconds (with one decimal place if needed). |
| 32 | +
|
| 33 | + "Other" time = total script runtime - (build+clean+sleep). |
| 34 | +
|
| 35 | +.NOTES |
| 36 | + Requires PowerShell 5.1+ and `cargo` in PATH. |
| 37 | + Assumes the current directory has a Cargo project (Cargo.toml). |
| 38 | +
|
| 39 | +.EXAMPLE |
| 40 | + .\rust_perf_battery.ps1 |
| 41 | +#> |
| 42 | + |
| 43 | +param() |
| 44 | + |
| 45 | +Set-StrictMode -Version Latest |
| 46 | +$ErrorActionPreference = 'Stop' |
| 47 | + |
| 48 | +# Configuration |
| 49 | +$PauseBetweenRuns = 10 # seconds to sleep between each iteration |
| 50 | + |
| 51 | +# Formats |
| 52 | +$logTimeFormat = "yyyy-MM-dd_HH-mm-ss" # for file names |
| 53 | +$displayTimeFormat = "yyyy-MM-dd HH:mm:ss" # for printed lines |
| 54 | +[DateTime]$GlobalScriptStart = Get-Date |
| 55 | +[string]$ScriptStartTime = (Get-Date -Format $logTimeFormat) |
| 56 | +[string]$FullLogPath = Join-Path (Get-Location) ("rust_perf_battery_full_$ScriptStartTime.log") |
| 57 | +[string]$SummaryLogPath = Join-Path (Get-Location) ("rust_perf_battery_summary_$ScriptStartTime.log") |
| 58 | + |
| 59 | +# Track global stats |
| 60 | +$CompileCount = 0 |
| 61 | +$StopLoop = $false |
| 62 | +[TimeSpan]$TotalBuildTime = [TimeSpan]::Zero |
| 63 | +[TimeSpan]$TotalCleanTime = [TimeSpan]::Zero |
| 64 | +[TimeSpan]$TotalSleepTime = [TimeSpan]::Zero |
| 65 | + |
| 66 | +# Mode stats: Dictionary<string,PSObject> |
| 67 | +# Each value: { Mode, TotalModeTime, TotalBuildTime, TotalCleanTime, BuildCount } |
| 68 | +$ModeStats = [System.Collections.Generic.Dictionary[string, object]]::new() |
| 69 | + |
| 70 | +function Ensure-ModeStats($mode) { |
| 71 | + if (-not $ModeStats.ContainsKey($mode)) { |
| 72 | + $ModeStats[$mode] = [PSCustomObject]@{ |
| 73 | + Mode = $mode |
| 74 | + TotalModeTime = [TimeSpan]::Zero |
| 75 | + TotalBuildTime = [TimeSpan]::Zero |
| 76 | + TotalCleanTime = [TimeSpan]::Zero |
| 77 | + BuildCount = 0 |
| 78 | + } |
| 79 | + } |
| 80 | +} |
| 81 | + |
| 82 | +function Get-PowerSchemeName { |
| 83 | + $output = powercfg /getactivescheme 2>$null |
| 84 | + if ($LASTEXITCODE -ne 0 -or [string]::IsNullOrWhiteSpace($output)) { |
| 85 | + throw "Unable to retrieve power scheme name." |
| 86 | + } |
| 87 | + |
| 88 | + if ($output -match '\((?<SchemeName>[^)]+)\)') { |
| 89 | + return ($Matches['SchemeName'].Trim()) |
| 90 | + } else { |
| 91 | + throw "No scheme name found in powercfg output." |
| 92 | + } |
| 93 | +} |
| 94 | + |
| 95 | +function Get-BatteryStatus { |
| 96 | + $batt = Get-CimInstance Win32_Battery -ErrorAction SilentlyContinue |
| 97 | + if (-not $batt) { |
| 98 | + return [PSCustomObject]@{ |
| 99 | + HasBattery = $false |
| 100 | + BatteryPercent = 100 |
| 101 | + OnAC = $true |
| 102 | + BatteryStatus = "NoBattery" |
| 103 | + } |
| 104 | + } |
| 105 | + |
| 106 | + $statusCode = [int]$batt.BatteryStatus |
| 107 | + $percent = [int]$batt.EstimatedChargeRemaining |
| 108 | + $onAC = $true |
| 109 | + if ($statusCode -eq 1 -or $statusCode -eq 4 -or $statusCode -eq 5) { |
| 110 | + $onAC = $false |
| 111 | + } |
| 112 | + |
| 113 | + return [PSCustomObject]@{ |
| 114 | + HasBattery = $true |
| 115 | + BatteryPercent = $percent |
| 116 | + OnAC = $onAC |
| 117 | + BatteryStatus = $statusCode |
| 118 | + } |
| 119 | +} |
| 120 | + |
| 121 | +function Get-CurrentEnergyMode { |
| 122 | + $schemeName = Get-PowerSchemeName |
| 123 | + $battery = Get-BatteryStatus |
| 124 | + $powerSource = if ($battery.OnAC) { "ac" } else { "battery" } |
| 125 | + $modeName = ($schemeName -replace '\s+', '-').ToLower() |
| 126 | + return "$powerSource-$modeName" |
| 127 | +} |
| 128 | + |
| 129 | +function Format-ShortTime($ts) { |
| 130 | + # Format as HH:MM:SSs |
| 131 | + "{0:00}:{1:00}:{2:00}s" -f [int]$ts.TotalHours, $ts.Minutes, $ts.Seconds |
| 132 | +} |
| 133 | + |
| 134 | +function Self-Test { |
| 135 | + try { |
| 136 | + $mode = Get-CurrentEnergyMode |
| 137 | + # Just test we can add to mode stats without error |
| 138 | + Ensure-ModeStats($mode) |
| 139 | + $ModeStats[$mode].TotalModeTime += (New-TimeSpan -Seconds 5) |
| 140 | + } catch { |
| 141 | + Write-Host "Self-test failed: $($_.Exception.Message)" |
| 142 | + exit 1 |
| 143 | + } |
| 144 | +} |
| 145 | + |
| 146 | +function Run-CargoClean { |
| 147 | + $start = Get-Date |
| 148 | + $output = & cargo clean 2>&1 |
| 149 | + $exit = $LASTEXITCODE |
| 150 | + $elapsed = (Get-Date) - $start |
| 151 | + return [PSCustomObject]@{ |
| 152 | + Output = $output |
| 153 | + Success = ($exit -eq 0) |
| 154 | + Elapsed = $elapsed |
| 155 | + } |
| 156 | +} |
| 157 | + |
| 158 | +function Run-CargoBuild { |
| 159 | + $start = Get-Date |
| 160 | + $output = & cargo build 2>&1 |
| 161 | + $exit = $LASTEXITCODE |
| 162 | + $elapsed = (Get-Date) - $start |
| 163 | + return [PSCustomObject]@{ |
| 164 | + Output = $output |
| 165 | + Success = ($exit -eq 0) |
| 166 | + Elapsed = $elapsed |
| 167 | + } |
| 168 | +} |
| 169 | + |
| 170 | +function Write-Logs { |
| 171 | + param( |
| 172 | + [string]$FullOutput, |
| 173 | + [string]$SummaryLine |
| 174 | + ) |
| 175 | + Add-Content -Path $FullLogPath -Value $FullOutput |
| 176 | + Add-Content -Path $FullLogPath -Value $SummaryLine |
| 177 | + Add-Content -Path $SummaryLogPath -Value $SummaryLine |
| 178 | +} |
| 179 | + |
| 180 | +# Run self-test |
| 181 | +Self-Test |
| 182 | + |
| 183 | +# Clear mode stats since we tested adding time |
| 184 | +$ModeStats.Clear() |
| 185 | + |
| 186 | +# Now that functions are defined, we can read initial battery |
| 187 | +$InitialBatteryPercent = (Get-BatteryStatus).BatteryPercent |
| 188 | + |
| 189 | +# Print initial lines |
| 190 | +$battery = Get-BatteryStatus |
| 191 | +$initMode = Get-CurrentEnergyMode |
| 192 | +$currentTimestamp = (Get-Date -Format $displayTimeFormat) + "s" |
| 193 | + |
| 194 | +$initialLine = "$currentTimestamp battery=$($battery.BatteryPercent)% mode=$initMode" |
| 195 | +Write-Host $initialLine |
| 196 | + |
| 197 | +$logLine = "Logs: $(Split-Path $FullLogPath -Leaf), $(Split-Path $SummaryLogPath -Leaf)" |
| 198 | +Write-Host $logLine |
| 199 | + |
| 200 | +while (-not $StopLoop) { |
| 201 | + $iterationStart = Get-Date |
| 202 | + $CurrentEnergyMode = Get-CurrentEnergyMode |
| 203 | + |
| 204 | + # Clean |
| 205 | + $cleanResult = Run-CargoClean |
| 206 | + if (-not $cleanResult.Success) { |
| 207 | + Write-Host "cargo clean failed, output:" |
| 208 | + $cleanResult.Output | Write-Host |
| 209 | + $StopLoop = $true |
| 210 | + break |
| 211 | + } |
| 212 | + |
| 213 | + # Build |
| 214 | + $buildResult = Run-CargoBuild |
| 215 | + if (-not $buildResult.Success) { |
| 216 | + Write-Host "cargo build failed, output:" |
| 217 | + $buildResult.Output | Write-Host |
| 218 | + $StopLoop = $true |
| 219 | + break |
| 220 | + } |
| 221 | + |
| 222 | + $iterationElapsed = (Get-Date) - $iterationStart |
| 223 | + |
| 224 | + # Update global stats |
| 225 | + $TotalCleanTime += $cleanResult.Elapsed |
| 226 | + $TotalBuildTime += $buildResult.Elapsed |
| 227 | + |
| 228 | + # Update mode stats |
| 229 | + Ensure-ModeStats($CurrentEnergyMode) |
| 230 | + $ModeStats[$CurrentEnergyMode].TotalModeTime += $iterationElapsed |
| 231 | + $ModeStats[$CurrentEnergyMode].TotalBuildTime += $buildResult.Elapsed |
| 232 | + $ModeStats[$CurrentEnergyMode].TotalCleanTime += $cleanResult.Elapsed |
| 233 | + $ModeStats[$CurrentEnergyMode].BuildCount += 1 |
| 234 | + |
| 235 | + $CompileCount++ |
| 236 | + $batt = Get-BatteryStatus |
| 237 | + |
| 238 | + # Extract last build line safely |
| 239 | + $buildLineObj = $buildResult.Output | Select-Object -Last 1 |
| 240 | + $lastBuildLine = if ($buildLineObj) { [string]$buildLineObj } else { "" } |
| 241 | + $lastBuildLine = $lastBuildLine.Trim() |
| 242 | + |
| 243 | + # Sleep between runs |
| 244 | + $sleepStart = Get-Date |
| 245 | + Start-Sleep -Seconds $PauseBetweenRuns |
| 246 | + $sleepElapsed = (Get-Date) - $sleepStart |
| 247 | + $TotalSleepTime += $sleepElapsed |
| 248 | + |
| 249 | + # Compute metrics |
| 250 | + $totalElapsed = (Get-Date) - $GlobalScriptStart |
| 251 | + $otherTime = $totalElapsed - ($TotalBuildTime + $TotalCleanTime + $TotalSleepTime) |
| 252 | + |
| 253 | + $totalBuildStr = Format-ShortTime $TotalBuildTime |
| 254 | + $totalCleanStr = Format-ShortTime $TotalCleanTime |
| 255 | + $totalSleepStr = Format-ShortTime $TotalSleepTime |
| 256 | + $otherTimeStr = Format-ShortTime $otherTime |
| 257 | + |
| 258 | + $batteryDrop = $InitialBatteryPercent - $batt.BatteryPercent |
| 259 | + $batteryDropFloat = [double]$batteryDrop |
| 260 | + $currentTimestamp = (Get-Date -Format $displayTimeFormat) + "s" |
| 261 | + $runCountStr = "{0:000}" -f $CompileCount |
| 262 | + |
| 263 | + # Overall line |
| 264 | + $overallLine = "$currentTimestamp, run $runCountStr, $CurrentEnergyMode, battery $($batt.BatteryPercent)%, total build $totalBuildStr, clean $totalCleanStr, sleep $totalSleepStr" |
| 265 | + Write-Host $overallLine |
| 266 | + |
| 267 | + # Second line |
| 268 | + $secondLine = " Sleeping ${PauseBetweenRuns}s between builds. $lastBuildLine" |
| 269 | + Write-Host $secondLine |
| 270 | + |
| 271 | + # Mode lines with ratios |
| 272 | + foreach ($modeKey in $ModeStats.Keys) { |
| 273 | + $m = $ModeStats[$modeKey] |
| 274 | + # avg build/clean time |
| 275 | + $avgBuild = "N/A" |
| 276 | + $avgClean = "N/A" |
| 277 | + if ($m.BuildCount -gt 0) { |
| 278 | + $avgBuildSec = $m.TotalBuildTime.TotalSeconds / $m.BuildCount |
| 279 | + $avgBuild = ("{0:F1}s" -f $avgBuildSec) |
| 280 | + $avgCleanSec = $m.TotalCleanTime.TotalSeconds / $m.BuildCount |
| 281 | + $avgClean = ("{0:F1}s" -f $avgCleanSec) |
| 282 | + } |
| 283 | + |
| 284 | + $buildsPerDrop = "N/A" |
| 285 | + $buildTimePerDrop = "N/A" |
| 286 | + $wallPerDrop = "N/A" |
| 287 | + if ($batteryDropFloat -gt 0) { |
| 288 | + $buildsPerDropVal = $m.BuildCount / $batteryDropFloat |
| 289 | + $buildsPerDrop = ("{0:F2}" -f $buildsPerDropVal) |
| 290 | + |
| 291 | + $buildTimePerDropVal = $m.TotalBuildTime.TotalSeconds / $batteryDropFloat |
| 292 | + $buildTimePerDrop = ("{0:F0}s" -f $buildTimePerDropVal) |
| 293 | + |
| 294 | + $wallPerDropVal = $m.TotalModeTime.TotalSeconds / $batteryDropFloat |
| 295 | + $wallPerDrop = ("{0:F0}s" -f $wallPerDropVal) |
| 296 | + } |
| 297 | + |
| 298 | + $modeLine = " On $($m.Mode) for $(Format-ShortTime $m.TotalModeTime), avg build $avgBuild, avg clean $avgClean, builds per % drop: $buildsPerDrop, build time per %: $buildTimePerDrop, wall per %: $wallPerDrop" |
| 299 | + Write-Host $modeLine |
| 300 | + } |
| 301 | + |
| 302 | + $fullOutput = "=== Iteration $CompileCount ===`r`n" + |
| 303 | + "--- Cargo clean output ---`r`n" + ($cleanResult.Output -join "`r`n") + "`r`n" + |
| 304 | + "--- Cargo build output ---`r`n" + ($buildResult.Output -join "`r`n") + "`r`n" |
| 305 | + |
| 306 | + # For logs, just write the overall line as summary |
| 307 | + Write-Logs -FullOutput $fullOutput -SummaryLine $overallLine |
| 308 | +} |
| 309 | + |
| 310 | +Write-Host "Script ended." |
0 commit comments