diff --git a/tools/profile.sh b/tools/profile.sh deleted file mode 100755 index d08bbad059..0000000000 --- a/tools/profile.sh +++ /dev/null @@ -1,83 +0,0 @@ -#!/usr/bin/env bash -# Profiling helper — installs dotnet diagnostic tools (idempotent) and runs -# a common set of captures against a long-running DBSP circuit. -# -# Usage: -# ./tools/profile.sh install # install/update all tools -# ./tools/profile.sh counters # live runtime counters -# ./tools/profile.sh trace # session trace for speedscope -# ./tools/profile.sh gcdump # heap dump for PerfView -# ./tools/profile.sh bench # run BDN with DisassemblyDiagnoser - -set -eu - -case "${1:-}" in - install) - dotnet tool update --global dotnet-counters || dotnet tool install --global dotnet-counters - dotnet tool update --global dotnet-trace || dotnet tool install --global dotnet-trace - dotnet tool update --global dotnet-dump || dotnet tool install --global dotnet-dump - dotnet tool update --global dotnet-gcdump || dotnet tool install --global dotnet-gcdump - dotnet tool update --global dotnet-symbol || dotnet tool install --global dotnet-symbol - dotnet tool update --global dotnet-sos || dotnet tool install --global dotnet-sos - dotnet tool update --global dotnet-reportgenerator-globaltool \ - || dotnet tool install --global dotnet-reportgenerator-globaltool - echo "All tools installed." - ;; - - counters) - pid="${2:?usage: $0 counters }" - dotnet-counters monitor --process-id "$pid" \ - --counters System.Runtime,System.Runtime.Gc,System.Threading.ThreadPool - ;; - - trace) - pid="${2:?usage: $0 trace }" - dotnet-trace collect --process-id "$pid" \ - --providers 'Microsoft-DotNETCore-SampleProfiler,Microsoft-Windows-DotNETRuntime:0x1CCBDCC:4' \ - --duration 00:00:30 - dotnet-trace convert --format speedscope trace.nettrace - echo "Open trace.speedscope.json at https://www.speedscope.app" - ;; - - gcdump) - pid="${2:?usage: $0 gcdump }" - dotnet-gcdump collect -p "$pid" - ;; - - bench) - cd "$(dirname "$0")/.." - dotnet run --project bench/Benchmarks -c Release -- \ - --filter "${2:-*}" --memoryRandomization --runOncePerIteration - ;; - - coverage) - cd "$(dirname "$0")/.." - dotnet test Zeta.sln -c Release \ - /p:CollectCoverage=true \ - /p:CoverletOutputFormat=cobertura \ - /p:CoverletOutput=./TestResults/ \ - /p:Exclude="[Dbsp.Tests.*]*" - reportgenerator -reports:'tests/**/TestResults/coverage.cobertura.xml' \ - -targetdir:./coverage-report -reporttypes:Html - echo "Coverage HTML at ./coverage-report/index.html" - ;; - - *) - cat < # live runtime + GC + thread-pool counters - $0 trace # session trace for speedscope.app - $0 gcdump # heap dump for PerfView - $0 bench [filter] # BenchmarkDotNet run with memory diagnoser - $0 coverage # run tests with coverage, emit HTML - -Typical flow for finding a hot spot: - dotnet run --project samples/Demo -c Release & - PID=\$! - $0 counters \$PID # watch live — confirm alloc-rate trend - $0 trace \$PID # capture a 30-s snapshot, view in speedscope -EOF - ;; -esac diff --git a/tools/profile.ts b/tools/profile.ts new file mode 100644 index 0000000000..d295ead2ce --- /dev/null +++ b/tools/profile.ts @@ -0,0 +1,144 @@ +#!/usr/bin/env bun +import { spawnSync } from "node:child_process"; +import { dirname, resolve } from "node:path"; +import { fileURLToPath } from "node:url"; + +function repoRoot(): string { + return resolve(dirname(fileURLToPath(import.meta.url)), ".."); +} + +function run(cmd: string, args: string[], cwd?: string): void { + const result = spawnSync(cmd, args, { stdio: "inherit", cwd }); + if (result.status !== 0) process.exit(result.status ?? 1); +} + +function installTools(): void { + const tools = [ + "dotnet-counters", + "dotnet-trace", + "dotnet-dump", + "dotnet-gcdump", + "dotnet-symbol", + "dotnet-sos", + "dotnet-reportgenerator-globaltool", + ]; + for (const tool of tools) { + const update = spawnSync("dotnet", ["tool", "update", "--global", tool], { + stdio: "inherit", + }); + if (update.status !== 0) { + run("dotnet", ["tool", "install", "--global", tool]); + } + } + process.stdout.write("All tools installed.\n"); +} + +function requirePid(argv: string[]): string { + const pid = argv[1]; + if (!pid) { + process.stderr.write(`usage: profile.ts ${argv[0]} \n`); + process.exit(64); + } + return pid; +} + +const command = process.argv[2] ?? ""; +const rest = process.argv.slice(2); + +switch (command) { + case "install": + installTools(); + break; + + case "counters": + run("dotnet-counters", [ + "monitor", + "--process-id", + requirePid(rest), + "--counters", + "System.Runtime,System.Runtime.Gc,System.Threading.ThreadPool", + ]); + break; + + case "trace": { + const pid = requirePid(rest); + run("dotnet-trace", [ + "collect", + "--process-id", + pid, + "--providers", + "Microsoft-DotNETCore-SampleProfiler,Microsoft-Windows-DotNETRuntime:0x1CCBDCC:4", + "--duration", + "00:00:30", + ]); + run("dotnet-trace", ["convert", "--format", "speedscope", "trace.nettrace"]); + process.stdout.write( + "Open trace.speedscope.json at https://www.speedscope.app\n", + ); + break; + } + + case "gcdump": + run("dotnet-gcdump", ["collect", "-p", requirePid(rest)]); + break; + + case "bench": + run( + "dotnet", + [ + "run", + "--project", + "bench/Benchmarks", + "-c", + "Release", + "--", + "--filter", + rest[1] ?? "*", + "--memoryRandomization", + "--runOncePerIteration", + ], + repoRoot(), + ); + break; + + case "coverage": + run( + "dotnet", + [ + "test", + "Zeta.sln", + "-c", + "Release", + "/p:CollectCoverage=true", + "/p:CoverletOutputFormat=cobertura", + "/p:CoverletOutput=./TestResults/", + '/p:Exclude=[Dbsp.Tests.*]*', + ], + repoRoot(), + ); + run( + "reportgenerator", + [ + "-reports:tests/**/TestResults/coverage.cobertura.xml", + "-targetdir:./coverage-report", + "-reporttypes:Html", + ], + repoRoot(), + ); + process.stdout.write("Coverage HTML at ./coverage-report/index.html\n"); + break; + + default: + process.stdout.write(`DBSP profiling helper. + + bun tools/profile.ts install # install all dotnet diagnostic tools + bun tools/profile.ts counters # live runtime + GC + thread-pool counters + bun tools/profile.ts trace # session trace for speedscope.app + bun tools/profile.ts gcdump # heap dump for PerfView + bun tools/profile.ts bench [filter] # BenchmarkDotNet run with memory diagnoser + bun tools/profile.ts coverage # run tests with coverage, emit HTML +`); + if (command !== "" && command !== "-h" && command !== "--help") + process.exit(64); + break; +}