Auto merge of #111495 - Kobzol:dist-tests, r=Mark-Simulacrum
Run tests on PGO/LTO/BOLT optimized dist artifacts This PR adds baisc tests for the optimized dist builds on x64 Linux and Windows. A subset of the test suite is run, so it's not perfect, but it's better than the status quo (which is basically no testing at all, apart from the perf bot on Linux). r? `@ghost`
This commit is contained in:
commit
7b28a6b08a
@ -222,6 +222,7 @@ pub struct Build {
|
||||
initial_cargo: PathBuf,
|
||||
initial_lld: PathBuf,
|
||||
initial_libdir: PathBuf,
|
||||
initial_sysroot: PathBuf,
|
||||
|
||||
// Runtime state filled in later on
|
||||
// C/C++ compilers and archiver for all targets
|
||||
@ -389,13 +390,16 @@ pub fn new(mut config: Config) -> Build {
|
||||
"/dummy".to_string()
|
||||
} else {
|
||||
output(Command::new(&config.initial_rustc).arg("--print").arg("sysroot"))
|
||||
};
|
||||
}
|
||||
.trim()
|
||||
.to_string();
|
||||
|
||||
let initial_libdir = initial_target_dir
|
||||
.parent()
|
||||
.unwrap()
|
||||
.parent()
|
||||
.unwrap()
|
||||
.strip_prefix(initial_sysroot.trim())
|
||||
.strip_prefix(&initial_sysroot)
|
||||
.unwrap()
|
||||
.to_path_buf();
|
||||
|
||||
@ -425,6 +429,7 @@ pub fn new(mut config: Config) -> Build {
|
||||
initial_cargo: config.initial_cargo.clone(),
|
||||
initial_lld,
|
||||
initial_libdir,
|
||||
initial_sysroot: initial_sysroot.into(),
|
||||
local_rebuild: config.local_rebuild,
|
||||
fail_fast: config.cmd.fail_fast(),
|
||||
doc_tests: config.cmd.doc_tests(),
|
||||
|
@ -1424,7 +1424,15 @@ fn run(self, builder: &Builder<'_>) {
|
||||
|
||||
cmd.arg("--src-base").arg(builder.src.join("tests").join(suite));
|
||||
cmd.arg("--build-base").arg(testdir(builder, compiler.host).join(suite));
|
||||
cmd.arg("--sysroot-base").arg(builder.sysroot(compiler));
|
||||
|
||||
// When top stage is 0, that means that we're testing an externally provided compiler.
|
||||
// In that case we need to use its specific sysroot for tests to pass.
|
||||
let sysroot = if builder.top_stage == 0 {
|
||||
builder.initial_sysroot.clone()
|
||||
} else {
|
||||
builder.sysroot(compiler).to_path_buf()
|
||||
};
|
||||
cmd.arg("--sysroot-base").arg(sysroot);
|
||||
cmd.arg("--stage-id").arg(stage_id);
|
||||
cmd.arg("--suite").arg(suite);
|
||||
cmd.arg("--mode").arg(mode);
|
||||
|
@ -124,6 +124,12 @@ class Pipeline:
|
||||
def metrics_path(self) -> Path:
|
||||
return self.build_root() / "build" / "metrics.json"
|
||||
|
||||
def executable_extension(self) -> str:
|
||||
raise NotImplementedError
|
||||
|
||||
def skipped_tests(self) -> Iterable[str]:
|
||||
return ()
|
||||
|
||||
|
||||
class LinuxPipeline(Pipeline):
|
||||
def checkout_path(self) -> Path:
|
||||
@ -152,6 +158,13 @@ class LinuxPipeline(Pipeline):
|
||||
def supports_bolt(self) -> bool:
|
||||
return True
|
||||
|
||||
def executable_extension(self) -> str:
|
||||
return ""
|
||||
|
||||
def skipped_tests(self) -> Iterable[str]:
|
||||
# This test fails because of linker errors, as of June 2023.
|
||||
yield "tests/ui/process/nofile-limit.rs"
|
||||
|
||||
|
||||
class WindowsPipeline(Pipeline):
|
||||
def __init__(self):
|
||||
@ -211,6 +224,13 @@ class WindowsPipeline(Pipeline):
|
||||
def supports_bolt(self) -> bool:
|
||||
return False
|
||||
|
||||
def executable_extension(self) -> str:
|
||||
return ".exe"
|
||||
|
||||
def skipped_tests(self) -> Iterable[str]:
|
||||
# This test fails as of June 2023
|
||||
yield "tests\\codegen\\vec-shrink-panik.rs"
|
||||
|
||||
|
||||
def get_timestamp() -> float:
|
||||
return time.time()
|
||||
@ -403,9 +423,9 @@ def delete_directory(path: Path):
|
||||
shutil.rmtree(path)
|
||||
|
||||
|
||||
def unpack_archive(archive: Path):
|
||||
def unpack_archive(archive: Path, target_dir: Optional[Path] = None):
|
||||
LOGGER.info(f"Unpacking archive `{archive}`")
|
||||
shutil.unpack_archive(archive)
|
||||
shutil.unpack_archive(str(archive), extract_dir=str(target_dir) if target_dir is not None else None)
|
||||
|
||||
|
||||
def download_file(src: str, target: Path):
|
||||
@ -455,6 +475,7 @@ def cmd(
|
||||
)
|
||||
return subprocess.run(args, env=environment, check=True)
|
||||
|
||||
|
||||
class BenchmarkRunner:
|
||||
def run_rustc(self, pipeline: Pipeline):
|
||||
raise NotImplementedError
|
||||
@ -465,6 +486,7 @@ class BenchmarkRunner:
|
||||
def run_bolt(self, pipeline: Pipeline):
|
||||
raise NotImplementedError
|
||||
|
||||
|
||||
class DefaultBenchmarkRunner(BenchmarkRunner):
|
||||
def run_rustc(self, pipeline: Pipeline):
|
||||
# Here we're profiling the `rustc` frontend, so we also include `Check`.
|
||||
@ -478,6 +500,7 @@ class DefaultBenchmarkRunner(BenchmarkRunner):
|
||||
LLVM_PROFILE_FILE=str(pipeline.rustc_profile_template_path())
|
||||
)
|
||||
)
|
||||
|
||||
def run_llvm(self, pipeline: Pipeline):
|
||||
run_compiler_benchmarks(
|
||||
pipeline,
|
||||
@ -494,6 +517,7 @@ class DefaultBenchmarkRunner(BenchmarkRunner):
|
||||
crates=LLVM_BOLT_CRATES
|
||||
)
|
||||
|
||||
|
||||
def run_compiler_benchmarks(
|
||||
pipeline: Pipeline,
|
||||
profiles: List[str],
|
||||
@ -650,10 +674,8 @@ def gather_llvm_profiles(pipeline: Pipeline, runner: BenchmarkRunner):
|
||||
def gather_rustc_profiles(pipeline: Pipeline, runner: BenchmarkRunner):
|
||||
LOGGER.info("Running benchmarks with PGO instrumented rustc")
|
||||
|
||||
|
||||
runner.run_rustc(pipeline)
|
||||
|
||||
|
||||
profile_path = pipeline.rustc_profile_merged_file()
|
||||
LOGGER.info(f"Merging Rustc PGO profiles to {profile_path}")
|
||||
cmd([
|
||||
@ -770,6 +792,86 @@ def record_metrics(pipeline: Pipeline, timer: Timer):
|
||||
log_metrics(metrics)
|
||||
|
||||
|
||||
def run_tests(pipeline: Pipeline):
|
||||
"""
|
||||
After `dist` is executed, we extract its archived components into a sysroot directory,
|
||||
and then use that extracted rustc as a stage0 compiler.
|
||||
Then we run a subset of tests using that compiler, to have a basic smoke test which checks
|
||||
whether the optimization pipeline hasn't broken something.
|
||||
"""
|
||||
build_dir = pipeline.build_root() / "build"
|
||||
dist_dir = build_dir / "dist"
|
||||
|
||||
def extract_dist_dir(name: str) -> Path:
|
||||
target_dir = build_dir / "optimized-dist"
|
||||
target_dir.mkdir(parents=True, exist_ok=True)
|
||||
unpack_archive(dist_dir / f"{name}.tar.xz", target_dir=target_dir)
|
||||
extracted_path = target_dir / name
|
||||
assert extracted_path.is_dir()
|
||||
return extracted_path
|
||||
|
||||
# Extract rustc, libstd, cargo and src archives to create the optimized sysroot
|
||||
rustc_dir = extract_dist_dir(f"rustc-nightly-{PGO_HOST}") / "rustc"
|
||||
libstd_dir = extract_dist_dir(f"rust-std-nightly-{PGO_HOST}") / f"rust-std-{PGO_HOST}"
|
||||
cargo_dir = extract_dist_dir(f"cargo-nightly-{PGO_HOST}") / f"cargo"
|
||||
extracted_src_dir = extract_dist_dir("rust-src-nightly") / "rust-src"
|
||||
|
||||
# We need to manually copy libstd to the extracted rustc sysroot
|
||||
shutil.copytree(
|
||||
libstd_dir / "lib" / "rustlib" / PGO_HOST / "lib",
|
||||
rustc_dir / "lib" / "rustlib" / PGO_HOST / "lib"
|
||||
)
|
||||
|
||||
# Extract sources - they aren't in the `rustc-nightly-{host}` tarball, so we need to manually copy libstd
|
||||
# sources to the extracted sysroot. We need sources available so that `-Zsimulate-remapped-rust-src-base`
|
||||
# works correctly.
|
||||
shutil.copytree(
|
||||
extracted_src_dir / "lib" / "rustlib" / "src",
|
||||
rustc_dir / "lib" / "rustlib" / "src"
|
||||
)
|
||||
|
||||
rustc_path = rustc_dir / "bin" / f"rustc{pipeline.executable_extension()}"
|
||||
assert rustc_path.is_file()
|
||||
cargo_path = cargo_dir / "bin" / f"cargo{pipeline.executable_extension()}"
|
||||
assert cargo_path.is_file()
|
||||
|
||||
config_content = f"""profile = "user"
|
||||
changelog-seen = 2
|
||||
|
||||
[build]
|
||||
rustc = "{rustc_path.as_posix()}"
|
||||
cargo = "{cargo_path.as_posix()}"
|
||||
|
||||
[llvm]
|
||||
download-ci-llvm = true
|
||||
"""
|
||||
logging.info(f"Using following `config.toml` for running tests:\n{config_content}")
|
||||
|
||||
# Simulate a stage 0 compiler with the extracted optimized dist artifacts.
|
||||
with open("config.toml", "w") as f:
|
||||
f.write(config_content)
|
||||
|
||||
args = [
|
||||
sys.executable,
|
||||
pipeline.checkout_path() / "x.py",
|
||||
"test",
|
||||
"--stage", "0",
|
||||
"tests/assembly",
|
||||
"tests/codegen",
|
||||
"tests/codegen-units",
|
||||
"tests/incremental",
|
||||
"tests/mir-opt",
|
||||
"tests/pretty",
|
||||
"tests/run-pass-valgrind",
|
||||
"tests/ui",
|
||||
]
|
||||
for test_path in pipeline.skipped_tests():
|
||||
args.extend(["--exclude", test_path])
|
||||
cmd(args=args, env=dict(
|
||||
COMPILETEST_FORCE_STAGE0="1"
|
||||
))
|
||||
|
||||
|
||||
def execute_build_pipeline(timer: Timer, pipeline: Pipeline, runner: BenchmarkRunner, final_build_args: List[str]):
|
||||
# Clear and prepare tmp directory
|
||||
shutil.rmtree(pipeline.opt_artifacts(), ignore_errors=True)
|
||||
@ -844,6 +946,11 @@ def execute_build_pipeline(timer: Timer, pipeline: Pipeline, runner: BenchmarkRu
|
||||
cmd(final_build_args)
|
||||
record_metrics(pipeline, stage4)
|
||||
|
||||
# Try builds can be in various broken states, so we don't want to gatekeep them with tests
|
||||
if not is_try_build():
|
||||
with timer.section("Run tests"):
|
||||
run_tests(pipeline)
|
||||
|
||||
|
||||
def run(runner: BenchmarkRunner):
|
||||
logging.basicConfig(
|
||||
|
Loading…
Reference in New Issue
Block a user