diff --git a/collector/src/bin/rustc-fake.rs b/collector/src/bin/rustc-fake.rs index e72b2f9c0..8a2298ff8 100644 --- a/collector/src/bin/rustc-fake.rs +++ b/collector/src/bin/rustc-fake.rs @@ -4,8 +4,18 @@ use std::process::Command; use std::time::{Duration, Instant}; fn main() { - let mut args = env::args_os().skip(1).collect::>(); + let mut args_os = env::args_os(); + let name = args_os.next().unwrap().into_string().unwrap(); + + let mut args = args_os.collect::>(); let rustc = env::var_os("RUSTC_REAL").unwrap(); + let rustdoc = env::var_os("RUSTDOC_REAL").unwrap(); + let actually_rustdoc = name.ends_with("rustdoc-fake"); + let tool = if actually_rustdoc { + rustdoc + } else { + rustc + }; if let Some(count) = env::var("RUSTC_THREAD_COUNT") .ok() @@ -36,7 +46,7 @@ fn main() { .arg("instructions:u,cycles:u,task-clock,cpu-clock,faults") .arg("--log-fd") .arg("1") - .arg(&rustc) + .arg(&tool) .args(&args); let prof_out_dir = std::env::current_dir().unwrap().join("self-profile-output"); @@ -91,14 +101,14 @@ fn main() { } "self-profile" => { - let mut cmd = Command::new(&rustc); + let mut cmd = Command::new(&tool); cmd.arg("-Zself-profile=Zsp").args(&args); assert!(cmd.status().expect("failed to spawn").success()); } "time-passes" => { - let mut cmd = Command::new(&rustc); + let mut cmd = Command::new(&tool); cmd.arg("-Ztime-passes").args(&args); assert!(cmd.status().expect("failed to spawn").success()); @@ -113,7 +123,7 @@ fn main() { .arg("--output=perf") .arg("--freq=299") .arg("--event=cycles:u,instructions:u") - .arg(&rustc) + .arg(&tool) .args(&args); assert!(cmd.status().expect("failed to spawn").success()); @@ -124,7 +134,7 @@ fn main() { let has_oprofile = cmd.output().is_ok(); assert!(has_oprofile); // Other possibly useful args: --callgraph, --separate-thread - cmd.arg("operf").arg(&rustc).args(&args); + cmd.arg("operf").arg(&tool).args(&args); assert!(cmd.status().expect("failed to spawn").success()); } @@ -140,7 +150,7 @@ fn main() { .arg("--cache-sim=no") .arg("--branch-sim=no") .arg("--cachegrind-out-file=cgout") - .arg(&rustc) + .arg(&tool) .args(&args); assert!(cmd.status().expect("failed to spawn").success()); @@ -157,7 +167,7 @@ fn main() { .arg("--cache-sim=no") .arg("--branch-sim=no") .arg("--callgrind-out-file=clgout") - .arg(&rustc) + .arg(&tool) .args(&args); assert!(cmd.status().expect("failed to spawn").success()); @@ -170,7 +180,7 @@ fn main() { cmd.arg("--tool=dhat") .arg("--num-callers=4") .arg("--dhat-out-file=dhout") - .arg(&rustc) + .arg(&tool) .args(&args); assert!(cmd.status().expect("failed to spawn").success()); @@ -186,14 +196,14 @@ fn main() { .arg("--threshold=0.2") .arg("--massif-out-file=msout") .arg("--alloc-fn=__rdl_alloc") - .arg(&rustc) + .arg(&tool) .args(&args); assert!(cmd.status().expect("failed to spawn").success()); } "eprintln" | "llvm-lines" => { - let mut cmd = Command::new(&rustc); + let mut cmd = Command::new(&tool); cmd.args(&args); assert!(cmd.status().expect("failed to spawn").success()); @@ -204,7 +214,7 @@ fn main() { } } } else { - let mut cmd = Command::new(&rustc); + let mut cmd = Command::new(&tool); cmd.args(&args); exec(&mut cmd); } diff --git a/collector/src/bin/rustc-perf-collector/execute.rs b/collector/src/bin/rustc-perf-collector/execute.rs index 3e9dc48ae..91af94367 100644 --- a/collector/src/bin/rustc-perf-collector/execute.rs +++ b/collector/src/bin/rustc-perf-collector/execute.rs @@ -146,7 +146,7 @@ impl Profiler { // What cargo subcommand do we need to run for this profiler? If not // `rustc`, must be a subcommand that itself invokes `rustc`. - fn subcommand(&self) -> &'static str { + fn subcommand(&self, build_kind: BuildKind) -> Option<&'static str> { match self { Profiler::PerfStat | Profiler::PerfStatSelfProfile @@ -158,25 +158,17 @@ impl Profiler { | Profiler::Callgrind | Profiler::DHAT | Profiler::Massif - | Profiler::Eprintln => "rustc", - Profiler::LlvmLines => "llvm-lines", - } - } - - fn is_build_kind_allowed(&self, build_kind: BuildKind) -> bool { - match self { - Profiler::PerfStat - | Profiler::PerfStatSelfProfile - | Profiler::SelfProfile - | Profiler::TimePasses - | Profiler::PerfRecord - | Profiler::OProfile - | Profiler::Cachegrind - | Profiler::Callgrind - | Profiler::DHAT - | Profiler::Massif - | Profiler::Eprintln => true, - Profiler::LlvmLines => build_kind != BuildKind::Check, + | Profiler::Eprintln => { + if build_kind == BuildKind::Doc { + Some("rustdoc") + } else { + Some("rustc") + } + } + Profiler::LlvmLines => match build_kind { + BuildKind::Debug | BuildKind::Opt => Some("rustc"), + BuildKind::Check | BuildKind::Doc => None, + }, } } @@ -242,6 +234,10 @@ impl<'a> CargoProcess<'a> { .arg(subcommand) .arg("--manifest-path") .arg(&self.manifest_path); + + if let Some(r) = &self.compiler.rustdoc { + cmd.env("RUSTDOC", &*FAKE_RUSTDOC).env("RUSTDOC_REAL", r); + } cmd } @@ -263,12 +259,6 @@ impl<'a> CargoProcess<'a> { // machinery works). let subcommand = if let Some((ref mut processor, run_kind, ..)) = self.processor_etc { let profiler = processor.profiler(); - if !profiler.is_build_kind_allowed(self.build_kind) { - return Err(anyhow::anyhow!( - "this profiler doesn't support {:?} builds", - self.build_kind - )); - } if !profiler.is_run_kind_allowed(run_kind) { return Err(anyhow::anyhow!( "this profiler doesn't support {:?} runs", @@ -276,7 +266,15 @@ impl<'a> CargoProcess<'a> { )); } - profiler.subcommand() + match profiler.subcommand(self.build_kind) { + None => { + return Err(anyhow::anyhow!( + "this profiler doesn't support {:?} builds", + self.build_kind + )) + } + Some(sub) => sub, + } } else { "rustc" }; @@ -288,6 +286,7 @@ impl<'a> CargoProcess<'a> { cmd.arg("--profile").arg("check"); } BuildKind::Debug => {} + BuildKind::Doc => {} BuildKind::Opt => { cmd.arg("--release"); } @@ -301,8 +300,9 @@ impl<'a> CargoProcess<'a> { // onto rustc for the final crate, which is exactly the crate for which // we want to wrap rustc. if let Some((ref mut processor, ..)) = self.processor_etc { + let profiler = processor.profiler().name(); cmd.arg("--wrap-rustc-with"); - cmd.arg(processor.profiler().name()); + cmd.arg(profiler); cmd.args(&self.rustc_args); } @@ -339,6 +339,21 @@ lazy_static::lazy_static! { fake_rustc.push("rustc-fake"); fake_rustc }; + static ref FAKE_RUSTDOC: PathBuf = { + let mut fake_rustdoc = env::current_exe().unwrap(); + fake_rustdoc.pop(); + fake_rustdoc.push("rustdoc-fake"); + // link from rustc-fake to rustdoc-fake + if !fake_rustdoc.exists() { + #[cfg(unix)] + use std::os::unix::fs::symlink; + #[cfg(windows)] + use std::os::windows::fs::symlink_file as symlink; + + symlink(&*FAKE_RUSTC, &fake_rustdoc).expect("failed to make symbolic link"); + } + fake_rustdoc + }; } /// Used to indicate if we need to retry a run. @@ -438,6 +453,7 @@ impl<'a> MeasureProcessor<'a> { let profile = match build_kind { BuildKind::Check => database::Profile::Check, BuildKind::Debug => database::Profile::Debug, + BuildKind::Doc => database::Profile::Doc, BuildKind::Opt => database::Profile::Opt, }; let mut buf = FuturesUnordered::new(); diff --git a/collector/src/bin/rustc-perf-collector/main.rs b/collector/src/bin/rustc-perf-collector/main.rs index 9c2ebffd4..a0d29f2e6 100644 --- a/collector/src/bin/rustc-perf-collector/main.rs +++ b/collector/src/bin/rustc-perf-collector/main.rs @@ -29,6 +29,7 @@ use sysroot::Sysroot; #[derive(Debug, Copy, Clone)] pub struct Compiler<'a> { pub rustc: &'a Path, + pub rustdoc: Option<&'a Path>, pub cargo: &'a Path, pub triple: &'a str, pub is_nightly: bool, @@ -38,6 +39,7 @@ impl<'a> Compiler<'a> { fn from_sysroot(sysroot: &'a Sysroot) -> Compiler<'a> { Compiler { rustc: &sysroot.rustc, + rustdoc: Some(&sysroot.rustdoc), cargo: &sysroot.cargo, triple: &sysroot.triple, is_nightly: true, @@ -49,9 +51,21 @@ impl<'a> Compiler<'a> { pub enum BuildKind { Check, Debug, + Doc, Opt, } +impl BuildKind { + fn all() -> Vec { + vec![ + BuildKind::Check, + BuildKind::Debug, + BuildKind::Doc, + BuildKind::Opt, + ] + } +} + #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] pub enum RunKind { Full, @@ -85,6 +99,7 @@ pub enum KindError { const STRINGS_AND_BUILD_KINDS: &[(&str, BuildKind)] = &[ ("Check", BuildKind::Check), ("Debug", BuildKind::Debug), + ("Doc", BuildKind::Doc), ("Opt", BuildKind::Opt), ]; @@ -100,7 +115,7 @@ pub fn build_kinds_from_arg(arg: &Option<&str>) -> Result, KindEr if let Some(arg) = arg { kinds_from_arg(STRINGS_AND_BUILD_KINDS, arg) } else { - Ok(vec![BuildKind::Check, BuildKind::Debug, BuildKind::Opt]) + Ok(BuildKind::all()) } } @@ -172,7 +187,7 @@ fn process_commits( rt, conn, &ArtifactId::Commit(commit), - &[BuildKind::Check, BuildKind::Debug, BuildKind::Opt], + &BuildKind::all(), &RunKind::all(), Compiler::from_sysroot(&sysroot), &benchmarks, @@ -224,6 +239,11 @@ fn bench_commit( call_home: bool, self_profile: bool, ) -> BenchmarkErrors { + if compiler.rustdoc.is_none() && build_kinds.iter().any(|b| *b == BuildKind::Doc) { + eprintln!("Rustdoc build specified but rustdoc path not provided"); + std::process::exit(1); + } + let mut errors_recorded = 0; eprintln!("Benchmarking {} for triple {}", cid, compiler.triple); @@ -316,7 +336,9 @@ fn get_benchmarks( exclude: Option<&str>, ) -> anyhow::Result> { let mut benchmarks = Vec::new(); - 'outer: for entry in fs::read_dir(benchmark_dir).context("failed to list benchmarks")? { + 'outer: for entry in fs::read_dir(benchmark_dir) + .with_context(|| format!("failed to list benchmark dir {}", benchmark_dir.display()))? + { let entry = entry?; let path = entry.path(); let name = match entry.file_name().into_string() { @@ -368,7 +390,7 @@ fn main() { match main_result() { Ok(code) => process::exit(code), Err(err) => { - eprintln!("{}", err); + eprintln!("{:#}\n{}", err, err.backtrace()); process::exit(1); } } @@ -394,10 +416,11 @@ fn main_result() -> anyhow::Result { (@subcommand bench_local => (about: "benchmark a local rustc") (@arg RUSTC: --rustc +required +takes_value "The path to the local rustc to benchmark") + (@arg RUSTDOC: --rustdoc +takes_value "The path to the local rustdoc to benchmark") (@arg CARGO: --cargo +required +takes_value "The path to the local Cargo to use") (@arg BUILDS: --builds +takes_value "One or more (comma-separated) of: 'Check', 'Debug',\n\ - 'Opt', 'All'") + 'Doc', 'Opt', 'All'") (@arg RUNS: --runs +takes_value "One or more (comma-separated) of: 'Full',\n\ 'IncrFull', 'IncrUnchanged', 'IncrPatched', 'All'") @@ -414,6 +437,7 @@ fn main_result() -> anyhow::Result { (about: "profile a local rustc") (@arg output_dir: --("output") +required +takes_value "Output directory") (@arg RUSTC: --rustc +required +takes_value "The path to the local rustc to benchmark") + (@arg RUSTDOC: --rustdoc +takes_value "The path to the local rustdoc to benchmark") (@arg CARGO: --cargo +required +takes_value "The path to the local Cargo to use") (@arg BUILDS: --builds +takes_value "One or more (comma-separated) of: 'Check', 'Debug',\n\ @@ -461,14 +485,14 @@ fn main_result() -> anyhow::Result { let commit = sub_m.value_of("COMMIT").unwrap(); let commit = get_commit_or_fake_it(&commit)?; let sysroot = Sysroot::install(commit.sha.to_string(), "x86_64-unknown-linux-gnu")?; - let build_kinds = &[BuildKind::Check, BuildKind::Debug, BuildKind::Opt]; + let build_kinds = BuildKind::all(); let run_kinds = RunKind::all(); let conn = rt.block_on(pool.expect("--db passed").connection()); bench_commit( &mut rt, conn, &ArtifactId::Commit(commit), - build_kinds, + &build_kinds, &run_kinds, Compiler::from_sysroot(&sysroot), &benchmarks, @@ -481,12 +505,18 @@ fn main_result() -> anyhow::Result { ("bench_local", Some(sub_m)) => { let rustc = sub_m.value_of("RUSTC").unwrap(); + let rustdoc = sub_m.value_of("RUSTDOC"); let cargo = sub_m.value_of("CARGO").unwrap(); let build_kinds = build_kinds_from_arg(&sub_m.value_of("BUILDS"))?; let run_kinds = run_kinds_from_arg(&sub_m.value_of("RUNS"))?; let id = sub_m.value_of("ID").unwrap(); let rustc_path = PathBuf::from(rustc).canonicalize()?; + let rustdoc_path = if let Some(r) = rustdoc { + Some(PathBuf::from(r).canonicalize()?) + } else { + None + }; let cargo_path = PathBuf::from(cargo).canonicalize()?; let conn = rt.block_on(pool.expect("--db passed").connection()); bench_commit( @@ -497,6 +527,7 @@ fn main_result() -> anyhow::Result { &run_kinds, Compiler { rustc: &rustc_path, + rustdoc: rustdoc_path.as_deref(), cargo: &cargo_path, triple: "x86_64-unknown-linux-gnu", is_nightly: true, @@ -519,28 +550,22 @@ fn main_result() -> anyhow::Result { anyhow::bail!("failed to install toolchain for {}", id); } - let rustc = String::from_utf8( - Command::new("rustup") - .arg("which") - .arg("--toolchain") - .arg(&id) - .arg("rustc") - .output() - .context("rustup which rustc")? - .stdout, - ) - .context("utf8")?; - let cargo = String::from_utf8( - Command::new("rustup") - .arg("which") - .arg("--toolchain") - .arg(&id) - .arg("cargo") - .output() - .context("rustup which cargo")? - .stdout, - ) - .context("utf8")?; + let which = |tool| { + String::from_utf8( + Command::new("rustup") + .arg("which") + .arg("--toolchain") + .arg(&id) + .arg(tool) + .output() + .context(format!("rustup which {}", tool))? + .stdout, + ) + .context("utf8") + }; + let rustc = which("rustc")?; + let rustdoc = which("rustdoc")?; + let cargo = which("cargo")?; // Remove benchmarks that don't work with a stable compiler. benchmarks.retain(|b| b.supports_stable()); @@ -555,10 +580,11 @@ fn main_result() -> anyhow::Result { &mut rt, conn, &ArtifactId::Artifact(id.to_string()), - &[BuildKind::Check, BuildKind::Debug, BuildKind::Opt], + &BuildKind::all(), &run_kinds, Compiler { rustc: Path::new(rustc.trim()), + rustdoc: Some(Path::new(rustdoc.trim())), cargo: Path::new(cargo.trim()), is_nightly: false, triple: "x86_64-unknown-linux-gnu", @@ -583,6 +609,7 @@ fn main_result() -> anyhow::Result { ("profile", Some(sub_m)) => { let rustc = sub_m.value_of("RUSTC").unwrap(); + let rustdoc = sub_m.value_of("RUSTDOC"); let cargo = sub_m.value_of("CARGO").unwrap(); let build_kinds = build_kinds_from_arg(&sub_m.value_of("BUILDS"))?; let run_kinds = run_kinds_from_arg(&sub_m.value_of("RUNS"))?; @@ -593,9 +620,15 @@ fn main_result() -> anyhow::Result { eprintln!("Profiling with {:?}", profiler); let rustc_path = PathBuf::from(rustc).canonicalize()?; + let rustdoc_path = if let Some(r) = rustdoc { + Some(PathBuf::from(r).canonicalize()?) + } else { + None + }; let cargo_path = PathBuf::from(cargo).canonicalize()?; let compiler = Compiler { rustc: &rustc_path, + rustdoc: rustdoc_path.as_deref(), cargo: &cargo_path, is_nightly: true, triple: "x86_64-unknown-linux-gnu", // XXX: Technically not necessarily true @@ -633,7 +666,7 @@ fn main_result() -> anyhow::Result { &mut rt, conn, &ArtifactId::Commit(commit), - &[BuildKind::Check], // no Debug or Opt builds + &[BuildKind::Check, BuildKind::Doc], // no Debug or Opt builds &RunKind::all(), Compiler::from_sysroot(&sysroot), &benchmarks, diff --git a/database/src/bin/ingest-json.rs b/database/src/bin/ingest-json.rs index 827845f20..7425fe9c5 100644 --- a/database/src/bin/ingest-json.rs +++ b/database/src/bin/ingest-json.rs @@ -934,6 +934,7 @@ async fn ingest(conn: &T, caches: &mut IdCache, path: &Path) { let profile_str = match profile { Profile::Check => "check", Profile::Debug => "debug", + Profile::Doc => "doc", Profile::Opt => "opt", }; let state = match &run.state { diff --git a/database/src/lib.rs b/database/src/lib.rs index 744455625..272a1fa87 100644 --- a/database/src/lib.rs +++ b/database/src/lib.rs @@ -284,6 +284,7 @@ impl Ord for Commit { pub enum Profile { Check, Debug, + Doc, Opt, } @@ -293,6 +294,7 @@ impl std::str::FromStr for Profile { Ok(match s.to_ascii_lowercase().as_str() { "check" => Profile::Check, "debug" => Profile::Debug, + "doc" => Profile::Doc, "opt" => Profile::Opt, _ => return Err(format!("{} is not a profile", s)), }) @@ -308,6 +310,7 @@ impl fmt::Display for Profile { Profile::Check => "check", Profile::Opt => "opt", Profile::Debug => "debug", + Profile::Doc => "doc", } ) } diff --git a/database/src/pool/sqlite.rs b/database/src/pool/sqlite.rs index 7c9b1ac41..edffb8686 100644 --- a/database/src/pool/sqlite.rs +++ b/database/src/pool/sqlite.rs @@ -298,6 +298,7 @@ impl Connection for SqliteConnection { "check" => Profile::Check, "opt" => Profile::Opt, "debug" => Profile::Debug, + "doc" => Profile::Doc, o => unreachable!("{}: not a profile", o), }, row.get::<_, String>(3)?.as_str().parse().unwrap(), @@ -330,7 +331,6 @@ impl Connection for SqliteConnection { cid.and_then(|cid| { query .query_row(params![&sid, &cid.0], |row| row.get(0)) - .optional() .unwrap_or_else(|e| { panic!("{:?}: series={:?}, aid={:?}", e, sid, cid); }) diff --git a/site/src/server.rs b/site/src/server.rs index 9de3494d0..b562a2946 100644 --- a/site/src/server.rs +++ b/site/src/server.rs @@ -64,6 +64,7 @@ pub fn handle_info(data: &InputData) -> info::Response { pub struct ByProfile { pub check: T, pub debug: T, + pub doc: T, pub opt: T, } @@ -76,6 +77,7 @@ impl ByProfile { Ok(ByProfile { check: f(Profile::Check).await?, debug: f(Profile::Debug).await?, + doc: f(Profile::Doc).await?, opt: f(Profile::Opt).await?, }) } @@ -87,6 +89,7 @@ impl std::ops::Index for ByProfile { match index { Profile::Check => &self.check, Profile::Debug => &self.debug, + Profile::Doc => &self.doc, Profile::Opt => &self.opt, } } @@ -106,14 +109,18 @@ pub async fn handle_dashboard(data: Arc) -> ServerResult a.cmp(&b), (_, _) => { + use std::cmp::Ordering; + if a.starts_with("beta") && b.starts_with("beta") { a.cmp(b) } else if a.starts_with("beta") { - std::cmp::Ordering::Greater + Ordering::Greater } else if b.starts_with("beta") { - std::cmp::Ordering::Less + Ordering::Less } else { - panic!("unexpected version") + // These are both local ids, not a commit. + // There's no way to tell which version they are, so just pretend they're the same. + Ordering::Equal } } }