|
| 1 | +use crate::config::{Profile, Scenario}; |
| 2 | +use clap::Parser; |
| 3 | +use std::path::PathBuf; |
| 4 | +use std::process::Command; |
| 5 | + |
| 6 | +mod config; |
| 7 | + |
| 8 | +/// Performs profiling or benchmarking with [`rustc-perf`](https://github.com/rust-lang/rustc-perf) |
| 9 | +/// using a locally built compiler. |
| 10 | +#[derive(Debug, clap::Parser)] |
| 11 | +// Hide arguments from BuildContext in the default usage string. |
| 12 | +// Clap does not seem to have a way of disabling the usage of these arguments. |
| 13 | +#[clap(override_usage = "rustc-perf-wrapper [OPTIONS] <COMMAND>")] |
| 14 | +pub struct Args { |
| 15 | + #[clap(subcommand)] |
| 16 | + cmd: PerfCommand, |
| 17 | + |
| 18 | + #[clap(flatten)] |
| 19 | + opts: SharedOpts, |
| 20 | + |
| 21 | + #[clap(flatten)] |
| 22 | + ctx: BuildContext, |
| 23 | +} |
| 24 | + |
| 25 | +#[derive(Debug, clap::Parser)] |
| 26 | +enum PerfCommand { |
| 27 | + /// Run `profile_local eprintln`. |
| 28 | + /// This executes the compiler on the given benchmarks and stores its stderr output. |
| 29 | + Eprintln, |
| 30 | + /// Run `profile_local samply` |
| 31 | + /// This executes the compiler on the given benchmarks and profiles it with `samply`. |
| 32 | + /// You need to install `samply`, e.g. using `cargo install samply`. |
| 33 | + Samply, |
| 34 | + /// Run `profile_local cachegrind`. |
| 35 | + /// This executes the compiler on the given benchmarks under `Cachegrind`. |
| 36 | + Cachegrind, |
| 37 | +} |
| 38 | + |
| 39 | +impl PerfCommand { |
| 40 | + fn is_profiling(&self) -> bool { |
| 41 | + match self { |
| 42 | + PerfCommand::Eprintln | PerfCommand::Samply | PerfCommand::Cachegrind => true, |
| 43 | + } |
| 44 | + } |
| 45 | +} |
| 46 | + |
| 47 | +#[derive(Debug, clap::Parser)] |
| 48 | +struct SharedOpts { |
| 49 | + /// Select the benchmarks that you want to run (separated by commas). |
| 50 | + /// If unspecified, all benchmarks will be executed. |
| 51 | + #[clap(long, global = true, value_delimiter = ',')] |
| 52 | + include: Vec<String>, |
| 53 | + /// Select the scenarios that should be benchmarked. |
| 54 | + #[clap( |
| 55 | + long, |
| 56 | + global = true, |
| 57 | + value_delimiter = ',', |
| 58 | + default_value = "Full,IncrFull,IncrUnchanged,IncrPatched" |
| 59 | + )] |
| 60 | + scenarios: Vec<Scenario>, |
| 61 | + /// Select the profiles that should be benchmarked. |
| 62 | + #[clap(long, global = true, value_delimiter = ',', default_value = "Check,Debug,Opt")] |
| 63 | + profiles: Vec<Profile>, |
| 64 | +} |
| 65 | + |
| 66 | +/// These arguments are mostly designed to be passed from bootstrap, not by users |
| 67 | +/// directly. |
| 68 | +#[derive(Debug, clap::Parser)] |
| 69 | +struct BuildContext { |
| 70 | + /// Compiler binary that will be benchmarked/profiled. |
| 71 | + #[clap(long, hide = true, env = "PERF_RUSTC")] |
| 72 | + compiler: PathBuf, |
| 73 | + /// rustc-perf collector binary that will be used for running benchmarks/profilers. |
| 74 | + #[clap(long, hide = true, env = "PERF_COLLECTOR")] |
| 75 | + collector: PathBuf, |
| 76 | + /// Directory where to store results. |
| 77 | + #[clap(long, hide = true, env = "PERF_RESULT_DIR")] |
| 78 | + results_dir: PathBuf, |
| 79 | +} |
| 80 | + |
| 81 | +fn main() { |
| 82 | + let args = Args::parse(); |
| 83 | + run(args); |
| 84 | +} |
| 85 | + |
| 86 | +fn run(args: Args) { |
| 87 | + let mut cmd = Command::new(args.ctx.collector); |
| 88 | + match &args.cmd { |
| 89 | + PerfCommand::Eprintln => { |
| 90 | + cmd.arg("profile_local").arg("eprintln"); |
| 91 | + } |
| 92 | + PerfCommand::Samply => { |
| 93 | + cmd.arg("profile_local").arg("samply"); |
| 94 | + } |
| 95 | + PerfCommand::Cachegrind => { |
| 96 | + cmd.arg("profile_local").arg("cachegrind"); |
| 97 | + } |
| 98 | + } |
| 99 | + if args.cmd.is_profiling() { |
| 100 | + cmd.arg("--out-dir").arg(&args.ctx.results_dir); |
| 101 | + } |
| 102 | + |
| 103 | + if !args.opts.include.is_empty() { |
| 104 | + cmd.arg("--include").arg(args.opts.include.join(",")); |
| 105 | + } |
| 106 | + if !args.opts.profiles.is_empty() { |
| 107 | + cmd.arg("--profiles") |
| 108 | + .arg(args.opts.profiles.iter().map(|p| p.to_string()).collect::<Vec<_>>().join(",")); |
| 109 | + } |
| 110 | + if !args.opts.scenarios.is_empty() { |
| 111 | + cmd.arg("--scenarios") |
| 112 | + .arg(args.opts.scenarios.iter().map(|p| p.to_string()).collect::<Vec<_>>().join(",")); |
| 113 | + } |
| 114 | + cmd.arg(&args.ctx.compiler); |
| 115 | + |
| 116 | + println!("Running `rustc-perf` using `{}`", args.ctx.compiler.display()); |
| 117 | + |
| 118 | + const MANIFEST_DIR: &str = env!("CARGO_MANIFEST_DIR"); |
| 119 | + |
| 120 | + let rustc_perf_dir = PathBuf::from(MANIFEST_DIR).join("../rustc-perf"); |
| 121 | + |
| 122 | + // We need to set the working directory to `src/tools/perf`, so that it can find the directory |
| 123 | + // with compile-time benchmarks. |
| 124 | + let cmd = cmd.current_dir(rustc_perf_dir); |
| 125 | + cmd.status().expect("error while running rustc-perf collector"); |
| 126 | + |
| 127 | + if args.cmd.is_profiling() { |
| 128 | + println!("You can find the results at `{}`", args.ctx.results_dir.display()); |
| 129 | + } |
| 130 | +} |
0 commit comments