Skip to content

Commit 917e646

Browse files
authored
test(pgo): ensure PGO works (#14859)
### What does this PR try to resolve? This is a regression test to prevent issues like #7416. The test only run on Linux, as other platforms have different requirements for PGO, or emit different PGO function missing warnings. ### How should we test and review this PR? Not sure how brittle it is. We can optionally run it only on Cargo's CI? cc #14830
2 parents 7aa570a + 02e25d5 commit 917e646

File tree

2 files changed

+112
-0
lines changed

2 files changed

+112
-0
lines changed

tests/testsuite/main.rs

+1
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,7 @@ mod package_features;
136136
mod patch;
137137
mod path;
138138
mod paths;
139+
mod pgo;
139140
mod pkgid;
140141
mod precise_pre_release;
141142
mod proc_macro;

tests/testsuite/pgo.rs

+111
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
//! Test if PGO works.
2+
3+
use std::path::PathBuf;
4+
use std::process::Command;
5+
6+
use cargo_test_support::prelude::*;
7+
use cargo_test_support::project;
8+
use cargo_test_support::str;
9+
10+
fn llvm_profdata() -> Option<PathBuf> {
11+
let output = Command::new("rustc")
12+
.arg("--print=target-libdir")
13+
.output()
14+
.expect("rustc to run");
15+
assert!(output.status.success());
16+
let mut libdir = PathBuf::from(String::from_utf8(output.stdout).unwrap());
17+
assert!(libdir.pop());
18+
let mut bin = libdir.join("bin").join("llvm-profdata");
19+
bin.exists().then(|| bin.clone()).or_else(|| {
20+
bin.set_extension("exe");
21+
bin.exists().then_some(bin)
22+
})
23+
}
24+
25+
#[cargo_test]
26+
fn pgo_works() {
27+
if cfg!(not(target_os = "linux")) {
28+
// macOS may emit different LLVM PGO warnings.
29+
// Windows LLVM has different requirements.
30+
return;
31+
}
32+
33+
let Some(llvm_profdata) = llvm_profdata() else {
34+
return;
35+
};
36+
37+
let p = project()
38+
.file(
39+
"Cargo.toml",
40+
r#"
41+
[package]
42+
name = "foo"
43+
edition = "2021"
44+
"#,
45+
)
46+
.file(
47+
"src/main.rs",
48+
r#"
49+
fn fibonacci(n: u64) -> u64 {
50+
match n {
51+
0 => 0,
52+
1 => 1,
53+
_ => fibonacci(n - 1) + fibonacci(n - 2),
54+
}
55+
}
56+
57+
fn main() {
58+
for i in [15, 20, 25] {
59+
let _ = fibonacci(i);
60+
}
61+
}
62+
"#,
63+
)
64+
.build();
65+
66+
let target_dir = p.build_dir();
67+
let release_bin = target_dir.join("release").join("foo");
68+
let pgo_data_dir = target_dir.join("pgo-data");
69+
let profdata_path = target_dir.join("merged.profdata");
70+
71+
// Build the instrumented binary
72+
p.cargo("build --release")
73+
.env(
74+
"RUSTFLAGS",
75+
format!("-Cprofile-generate={}", pgo_data_dir.display()),
76+
)
77+
.run();
78+
// Run the instrumented binary
79+
cargo_test_support::execs()
80+
.with_process_builder(cargo_test_support::process(release_bin))
81+
.run();
82+
83+
cargo_test_support::process(llvm_profdata)
84+
.arg("merge")
85+
.arg("-o")
86+
.arg(&profdata_path)
87+
.arg(pgo_data_dir)
88+
.status()
89+
.unwrap();
90+
91+
// Use merged profdata during optimization.
92+
//
93+
// -Cllvm-args=-pgo-warn-missing-function is essential.
94+
// If there are LLVM warnings, there might be something wrong.
95+
p.cargo("build --release -v")
96+
.env(
97+
"RUSTFLAGS",
98+
format!(
99+
"-Cprofile-use={} -Cllvm-args=-pgo-warn-missing-function",
100+
profdata_path.display()
101+
),
102+
)
103+
.with_stderr_data(str![[r#"
104+
[DIRTY] foo v0.0.0 ([ROOT]/foo): the rustflags changed
105+
[COMPILING] foo v0.0.0 ([ROOT]/foo)
106+
[RUNNING] `rustc [..]-Cprofile-use=[ROOT]/foo/target/merged.profdata -Cllvm-args=-pgo-warn-missing-function`
107+
[FINISHED] `release` profile [optimized] target(s) in [ELAPSED]s
108+
109+
"#]])
110+
.run();
111+
}

0 commit comments

Comments
 (0)