Skip to content

Commit db0eeca

Browse files
author
vp
committed
Add speedscope view task for diagnostics
1 parent dc875c5 commit db0eeca

2 files changed

Lines changed: 131 additions & 1 deletion

File tree

.vscode/tasks-diagnostics.ps1

Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,135 @@ switch($Command.ToLowerInvariant()){
120120
if($LASTEXITCODE -ne 0){ throw 'Trace conversion failed' }
121121
Write-Host "Trace complete: $traceFile" -ForegroundColor Green
122122
Write-Host "Speedscope file: $speedFile" -ForegroundColor Green
123+
# Auto-open speedscope view
124+
try {
125+
if(Get-Command npx -ErrorAction SilentlyContinue){
126+
Write-Host 'Opening speedscope (npx)...' -ForegroundColor Cyan
127+
& npx speedscope $speedFile
128+
} else {
129+
Write-Host 'npx not available; opening speedscope.app and folder.' -ForegroundColor Yellow
130+
Start-Process 'https://www.speedscope.app'
131+
Start-Process explorer.exe (Split-Path $speedFile -Parent)
132+
}
133+
} catch { Write-Host "Speedscope auto-open failed: $($_.Exception.Message)" -ForegroundColor Yellow }
134+
}
135+
'trace-cpu' {
136+
Ensure-Tool 'dotnet-trace' 'dotnet-trace'
137+
$script:DotNetOnly = $true
138+
# Duration selection (interactive)
139+
$durationsMap = [ordered]@{ '10 sec'='00:00:10'; '30 sec'='00:00:30'; '1 min'='00:01:00'; '5 min'='00:05:00' }
140+
$duration = $durationsMap['10 sec']
141+
try {
142+
Import-Module PwshSpectreConsole -ErrorAction Stop
143+
$choice = Read-SpectreSelection -Title 'Select trace duration' -Choices ($durationsMap.Keys + 'Cancel') -EnableSearch -PageSize 10
144+
if($choice -and $choice -ne 'Cancel'){ $duration = $durationsMap[$choice] }
145+
if($choice -eq 'Cancel'){ Write-Host 'CPU trace cancelled.' -ForegroundColor Yellow; break }
146+
} catch { Write-Host 'Spectre selection unavailable; using default 10 sec.' -ForegroundColor Yellow }
147+
$procId = Select-Pid 'Select process for CPU trace'
148+
if(-not $procId){ Write-Host 'No PID selected.' -ForegroundColor Yellow; break }
149+
$outDir = Join-Path $PSScriptRoot '..' '.tmp' 'diagnostics'
150+
New-Item -ItemType Directory -Force -Path $outDir | Out-Null
151+
$fileBase = "cpu_${procId}_$(Get-Date -Format 'yyyyMMdd_HHmmss')"
152+
$traceFile = Join-Path $outDir "$fileBase.nettrace"
153+
$speedFile = Join-Path $outDir "$fileBase.speedscope.json"
154+
Write-Host "Collecting CPU trace (SampleProfiler, $duration) for PID $procId ..." -ForegroundColor Cyan
155+
& dotnet-trace collect --process-id $procId --providers Microsoft-DotNETCore-SampleProfiler:1 --duration $duration -o $traceFile
156+
if($LASTEXITCODE -ne 0){ throw 'CPU trace collection failed' }
157+
Write-Host 'Converting to speedscope...' -ForegroundColor Cyan
158+
& dotnet-trace convert --format SpeedScope $traceFile -o $speedFile
159+
if($LASTEXITCODE -ne 0){ throw 'CPU trace conversion failed' }
160+
Write-Host "CPU trace complete: $traceFile" -ForegroundColor Green
161+
Write-Host "Speedscope file: $speedFile" -ForegroundColor Green
162+
try {
163+
if(Get-Command npx -ErrorAction SilentlyContinue){
164+
Write-Host 'Opening speedscope (npx)...' -ForegroundColor Cyan
165+
& npx speedscope $speedFile
166+
} else {
167+
Write-Host 'npx not available; opening speedscope.app and folder.' -ForegroundColor Yellow
168+
Start-Process 'https://www.speedscope.app'
169+
Start-Process explorer.exe (Split-Path $speedFile -Parent)
170+
}
171+
} catch { Write-Host "Speedscope auto-open failed: $($_.Exception.Message)" -ForegroundColor Yellow }
172+
}
173+
'trace-gc' {
174+
Ensure-Tool 'dotnet-trace' 'dotnet-trace'
175+
$script:DotNetOnly = $true
176+
# Duration selection (interactive)
177+
$durationsMap = [ordered]@{ '10 sec'='00:00:10'; '30 sec'='00:00:30'; '1 min'='00:01:00'; '5 min'='00:05:00' }
178+
$duration = $durationsMap['10 sec']
179+
try {
180+
Import-Module PwshSpectreConsole -ErrorAction Stop
181+
$choice = Read-SpectreSelection -Title 'Select GC trace duration' -Choices ($durationsMap.Keys + 'Cancel') -EnableSearch -PageSize 10
182+
if($choice -and $choice -ne 'Cancel'){ $duration = $durationsMap[$choice] }
183+
if($choice -eq 'Cancel'){ Write-Host 'GC trace cancelled.' -ForegroundColor Yellow; break }
184+
} catch { Write-Host 'Spectre selection unavailable; using default 10 sec.' -ForegroundColor Yellow }
185+
$procId = Select-Pid 'Select process for GC-focused trace'
186+
if(-not $procId){ Write-Host 'No PID selected.' -ForegroundColor Yellow; break }
187+
$outDir = Join-Path $PSScriptRoot '..' '.tmp' 'diagnostics'
188+
New-Item -ItemType Directory -Force -Path $outDir | Out-Null
189+
$fileBase = "gc_${procId}_$(Get-Date -Format 'yyyyMMdd_HHmmss')"
190+
$traceFile = Join-Path $outDir "$fileBase.nettrace"
191+
$speedFile = Join-Path $outDir "$fileBase.speedscope.json"
192+
Write-Host "Collecting GC-focused trace (SampleProfiler + System.Runtime, $duration) for PID $procId ..." -ForegroundColor Cyan
193+
& dotnet-trace collect --process-id $procId --providers Microsoft-DotNETCore-SampleProfiler:1,System.Runtime:4 --duration $duration -o $traceFile
194+
if($LASTEXITCODE -ne 0){ throw 'GC trace collection failed' }
195+
Write-Host 'Converting to speedscope...' -ForegroundColor Cyan
196+
& dotnet-trace convert --format SpeedScope $traceFile -o $speedFile
197+
if($LASTEXITCODE -ne 0){ throw 'GC trace conversion failed' }
198+
Write-Host "GC trace complete: $traceFile" -ForegroundColor Green
199+
Write-Host "Speedscope file: $speedFile" -ForegroundColor Green
200+
try {
201+
if(Get-Command npx -ErrorAction SilentlyContinue){
202+
Write-Host 'Opening speedscope (npx)...' -ForegroundColor Cyan
203+
& npx speedscope $speedFile
204+
} else {
205+
Write-Host 'npx not available; opening speedscope.app and folder.' -ForegroundColor Yellow
206+
Start-Process 'https://www.speedscope.app'
207+
Start-Process explorer.exe (Split-Path $speedFile -Parent)
208+
}
209+
} catch { Write-Host "Speedscope auto-open failed: $($_.Exception.Message)" -ForegroundColor Yellow }
210+
}
211+
'speedscope-view' {
212+
# Select a speedscope json file and open visualization
213+
$diagDir = Join-Path $PSScriptRoot '..' '.tmp' 'diagnostics'
214+
if(-not (Test-Path $diagDir)){ Write-Host "Diagnostics directory not found: $diagDir" -ForegroundColor Yellow; break }
215+
$profiles = Get-ChildItem $diagDir -Recurse -Filter '*.speedscope.json'
216+
if(-not $profiles){ Write-Host 'No speedscope profiles found.' -ForegroundColor Yellow; break }
217+
Import-Module PwshSpectreConsole -ErrorAction Stop
218+
$solutionRoot = (Resolve-Path (Join-Path $PSScriptRoot '..')).Path
219+
$map = [ordered]@{}
220+
$labels = @()
221+
foreach($p in ($profiles | Sort-Object LastWriteTime -Descending)){
222+
$full = (Resolve-Path $p.FullName).Path
223+
$rel = $full.Substring($solutionRoot.Length).TrimStart('\\')
224+
$label = "$($p.LastWriteTime.ToString('HH:mm:ss')) | $($p.Name)"
225+
$map[$label] = $full
226+
$labels += $label
227+
}
228+
$sel = Read-SpectreSelection -Title 'Select Speedscope Profile' -Choices ($labels + 'Cancel') -EnableSearch -PageSize 20
229+
if(-not $sel -or $sel -eq 'Cancel'){ Write-Host 'Speedscope view cancelled.' -ForegroundColor Yellow; break }
230+
if(-not $map.Contains($sel)){ Write-Host 'Invalid selection.' -ForegroundColor Red; break }
231+
$profile = $map[$sel]
232+
Write-Host "Selected profile: $profile" -ForegroundColor Cyan
233+
# Try npx speedscope first (requires node + speedscope)
234+
$opened = $false
235+
try {
236+
if(Get-Command npx -ErrorAction SilentlyContinue){
237+
Write-Host 'Launching speedscope via npx (local web UI)...' -ForegroundColor Cyan
238+
& npx speedscope $profile
239+
if($LASTEXITCODE -eq 0){ $opened = $true }
240+
}
241+
} catch { Write-Host "npx speedscope failed: $($_.Exception.Message)" -ForegroundColor Yellow }
242+
if(-not $opened){
243+
Write-Host 'Opening speedscope profile in default browser (https://www.speedscope.app)...' -ForegroundColor Cyan
244+
try {
245+
Start-Process 'https://www.speedscope.app' # user can manually load file
246+
# Also open folder in Explorer for convenience
247+
$folder = Split-Path $profile -Parent
248+
Start-Process explorer.exe $folder
249+
Write-Host 'Speedscope site and folder opened.' -ForegroundColor Green
250+
} catch { Write-Host "Browser/folder open failed: $($_.Exception.Message)" -ForegroundColor Yellow }
251+
}
123252
}
124253
'dump-heap' {
125254
Ensure-Tool 'dotnet-dump' 'dotnet-dump'

tasks.ps1

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,7 @@ $tasks = [ordered]@{
131131
'trace-flame' = @{ Label='Diagnostics Trace (Flame)'; Script={ Invoke-Diagnostics 'trace-flame' } }
132132
'trace-cpu' = @{ Label='Diagnostics Trace (CPU SampleProfiler)'; Script={ Invoke-Diagnostics 'trace-cpu' } }
133133
'trace-gc' = @{ Label='Diagnostics Trace (GC Focus)'; Script={ Invoke-Diagnostics 'trace-gc' } }
134+
'speedscope-view' = @{ Label='Diagnostics Speedscope View'; Script={ Invoke-Diagnostics 'speedscope-view' } }
134135
'dump-heap' = @{ Label='Diagnostics Heap Dump'; Script={ Invoke-Diagnostics 'dump-heap' } }
135136
'gc-stats' = @{ Label='Diagnostics GC Stats'; Script={ Invoke-Diagnostics 'gc-stats' } }
136137
'aspnet-metrics' = @{ Label='Diagnostics ASP.NET Core Metrics'; Script={ Invoke-Diagnostics 'aspnet-metrics' } }
@@ -148,7 +149,7 @@ $categories = [ordered]@{
148149
'Security & Compliance' = @('vulnerabilities','vulnerabilities-deep','outdated','outdated-json','licenses')
149150
'API & Spec' = @('openapi-lint')
150151
'Utilities' = @('misc-clean','misc-digest','misc-repl')
151-
'Performance & Diagnostics' = @('bench','bench-select','trace-flame','trace-cpu','trace-gc','dump-heap','gc-stats','aspnet-metrics','diag-quick')
152+
'Performance & Diagnostics' = @('bench','bench-select','trace-flame','trace-cpu','trace-gc','dump-heap','gc-stats','aspnet-metrics','diag-quick','speedscope-view')
152153
}
153154

154155
function Run-Task([string]$key){

0 commit comments

Comments
 (0)