Skip to content

Commit 827bc7d

Browse files
committed
Track -Cprofile-use and -Cprofile-sample-use values by file hash, not file path
1 parent ef9810a commit 827bc7d

File tree

8 files changed

+127
-13
lines changed

8 files changed

+127
-13
lines changed

Cargo.lock

+1
Original file line numberDiff line numberDiff line change
@@ -4032,6 +4032,7 @@ dependencies = [
40324032
"rustc_ty_utils",
40334033
"rustc_typeck",
40344034
"smallvec",
4035+
"tempfile",
40354036
"tracing",
40364037
"winapi",
40374038
]

compiler/rustc_codegen_ssa/src/back/linker.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -304,7 +304,7 @@ impl<'a> GccLinker<'a> {
304304
};
305305

306306
if let Some(path) = &self.sess.opts.unstable_opts.profile_sample_use {
307-
self.linker_arg(&format!("-plugin-opt=sample-profile={}", path.display()));
307+
self.linker_arg(&format!("-plugin-opt=sample-profile={}", path.as_path().display()));
308308
};
309309
self.linker_args(&[
310310
&format!("-plugin-opt={}", opt_level),

compiler/rustc_codegen_ssa/src/back/write.rs

+5-2
Original file line numberDiff line numberDiff line change
@@ -180,8 +180,11 @@ impl ModuleConfig {
180180
sess.opts.cg.profile_generate.clone(),
181181
SwitchWithOptPath::Disabled
182182
),
183-
pgo_use: if_regular!(sess.opts.cg.profile_use.clone(), None),
184-
pgo_sample_use: if_regular!(sess.opts.unstable_opts.profile_sample_use.clone(), None),
183+
pgo_use: if_regular!(sess.opts.cg.profile_use.clone().map(|p| p.into()), None),
184+
pgo_sample_use: if_regular!(
185+
sess.opts.unstable_opts.profile_sample_use.clone().map(|p| p.into()),
186+
None
187+
),
185188
debug_info_for_profiling: sess.opts.unstable_opts.debug_info_for_profiling,
186189
instrument_coverage: if_regular!(sess.instrument_coverage(), false),
187190
instrument_gcov: if_regular!(

compiler/rustc_interface/Cargo.toml

+1
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ winapi = { version = "0.3", features = ["libloaderapi"] }
5555

5656
[dev-dependencies]
5757
rustc_target = { path = "../rustc_target" }
58+
tempfile = "3.2"
5859

5960
[features]
6061
llvm = ['rustc_codegen_llvm']

compiler/rustc_interface/src/tests.rs

+36-3
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ use rustc_session::config::{
1616
use rustc_session::config::{CFGuard, ExternEntry, LinkerPluginLto, LtoCli, SwitchWithOptPath};
1717
use rustc_session::lint::Level;
1818
use rustc_session::search_paths::SearchPath;
19-
use rustc_session::utils::{CanonicalizedPath, NativeLib, NativeLibKind};
19+
use rustc_session::utils::{CanonicalizedPath, ContentHashedFilePath, NativeLib, NativeLibKind};
2020
use rustc_session::{build_session, getopts, DiagnosticOutput, Session};
2121
use rustc_span::edition::{Edition, DEFAULT_EDITION};
2222
use rustc_span::symbol::sym;
@@ -594,7 +594,6 @@ fn test_codegen_options_tracking_hash() {
594594
tracked!(passes, vec![String::from("1"), String::from("2")]);
595595
tracked!(prefer_dynamic, true);
596596
tracked!(profile_generate, SwitchWithOptPath::Enabled(None));
597-
tracked!(profile_use, Some(PathBuf::from("abc")));
598597
tracked!(relocation_model, Some(RelocModel::Pic));
599598
tracked!(soft_float, true);
600599
tracked!(split_debuginfo, Some(SplitDebuginfo::Packed));
@@ -777,7 +776,6 @@ fn test_unstable_options_tracking_hash() {
777776
tracked!(profile, true);
778777
tracked!(profile_emit, Some(PathBuf::from("abc")));
779778
tracked!(profiler_runtime, "abc".to_string());
780-
tracked!(profile_sample_use, Some(PathBuf::from("abc")));
781779
tracked!(relax_elf_relocations, Some(true));
782780
tracked!(relro_level, Some(RelroLevel::Full));
783781
tracked!(remap_cwd_prefix, Some(PathBuf::from("abc")));
@@ -818,6 +816,41 @@ fn test_unstable_options_tracking_hash() {
818816
tracked_no_crate_hash!(no_codegen, true);
819817
}
820818

819+
#[test]
820+
fn test_hashed_file_different_hash() {
821+
let tempdir = tempfile::TempDir::new().unwrap();
822+
823+
let mut options = Options::default();
824+
825+
macro_rules! check {
826+
($opt: expr, $file: expr) => {
827+
let path = tempdir.path().join($file);
828+
829+
// Write some data into the file
830+
std::fs::write(&path, &[1, 2, 3]).unwrap();
831+
832+
// The hash is calculated now
833+
*$opt = Some(ContentHashedFilePath::new(path.clone()));
834+
835+
let hash_no_crate = options.dep_tracking_hash(false);
836+
let hash_crate = options.dep_tracking_hash(true);
837+
838+
// Write different data into the file
839+
std::fs::write(&path, &[1, 2, 3, 4]).unwrap();
840+
841+
// The hash is re-calculated now
842+
*$opt = Some(ContentHashedFilePath::new(path));
843+
844+
// Check that the hash has changed
845+
assert_ne!(options.dep_tracking_hash(true), hash_crate);
846+
assert_ne!(options.dep_tracking_hash(false), hash_no_crate);
847+
};
848+
}
849+
850+
check!(&mut options.cg.profile_use, "profile-instr.profdata");
851+
check!(&mut options.unstable_opts.profile_sample_use, "profile-sample.profdata");
852+
}
853+
821854
#[test]
822855
fn test_edition_parsing() {
823856
// test default edition

compiler/rustc_session/src/options.rs

+17-3
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ use crate::config::*;
33
use crate::early_error;
44
use crate::lint;
55
use crate::search_paths::SearchPath;
6-
use crate::utils::NativeLib;
6+
use crate::utils::{ContentHashedFilePath, NativeLib};
77
use rustc_errors::LanguageIdentifier;
88
use rustc_target::spec::{CodeModel, LinkerFlavor, MergeFunctions, PanicStrategy, SanitizerSet};
99
use rustc_target::spec::{
@@ -364,6 +364,7 @@ mod desc {
364364
pub const parse_string_push: &str = parse_string;
365365
pub const parse_opt_langid: &str = "a language identifier";
366366
pub const parse_opt_pathbuf: &str = "a path";
367+
pub const parse_opt_hashed_filepath: &str = "a path";
367368
pub const parse_list: &str = "a space-separated list of strings";
368369
pub const parse_list_with_polarity: &str =
369370
"a comma-separated list of strings, with elements beginning with + or -";
@@ -509,6 +510,19 @@ mod parse {
509510
}
510511
}
511512

513+
pub(crate) fn parse_opt_hashed_filepath(
514+
slot: &mut Option<ContentHashedFilePath>,
515+
v: Option<&str>,
516+
) -> bool {
517+
match v {
518+
Some(s) => {
519+
*slot = Some(ContentHashedFilePath::new(PathBuf::from(s)));
520+
true
521+
}
522+
None => false,
523+
}
524+
}
525+
512526
pub(crate) fn parse_string_push(slot: &mut Vec<String>, v: Option<&str>) -> bool {
513527
match v {
514528
Some(s) => {
@@ -1176,7 +1190,7 @@ options! {
11761190
profile_generate: SwitchWithOptPath = (SwitchWithOptPath::Disabled,
11771191
parse_switch_with_opt_path, [TRACKED],
11781192
"compile the program with profiling instrumentation"),
1179-
profile_use: Option<PathBuf> = (None, parse_opt_pathbuf, [TRACKED],
1193+
profile_use: Option<ContentHashedFilePath> = (None, parse_opt_hashed_filepath, [TRACKED],
11801194
"use the given `.profdata` file for profile-guided optimization"),
11811195
#[rustc_lint_opt_deny_field_access("use `Session::relocation_model` instead of this field")]
11821196
relocation_model: Option<RelocModel> = (None, parse_relocation_model, [TRACKED],
@@ -1488,7 +1502,7 @@ options! {
14881502
(default based on relative source path)"),
14891503
profiler_runtime: String = (String::from("profiler_builtins"), parse_string, [TRACKED],
14901504
"name of the profiler runtime crate to automatically inject (default: `profiler_builtins`)"),
1491-
profile_sample_use: Option<PathBuf> = (None, parse_opt_pathbuf, [TRACKED],
1505+
profile_sample_use: Option<ContentHashedFilePath> = (None, parse_opt_hashed_filepath, [TRACKED],
14921506
"use the given `.prof` file for sampled profile-guided optimization (also known as AutoFDO)"),
14931507
query_dep_graph: bool = (false, parse_bool, [UNTRACKED],
14941508
"enable queries of the dependency graph for regression testing (default: no)"),

compiler/rustc_session/src/session.rs

+4-4
Original file line numberDiff line numberDiff line change
@@ -1459,20 +1459,20 @@ fn validate_commandline_args_with_session_available(sess: &Session) {
14591459
// Make sure that any given profiling data actually exists so LLVM can't
14601460
// decide to silently skip PGO.
14611461
if let Some(ref path) = sess.opts.cg.profile_use {
1462-
if !path.exists() {
1462+
if !path.as_path().exists() {
14631463
sess.err(&format!(
14641464
"File `{}` passed to `-C profile-use` does not exist.",
1465-
path.display()
1465+
path.as_path().display()
14661466
));
14671467
}
14681468
}
14691469

14701470
// Do the same for sample profile data.
14711471
if let Some(ref path) = sess.opts.unstable_opts.profile_sample_use {
1472-
if !path.exists() {
1472+
if !path.as_path().exists() {
14731473
sess.err(&format!(
14741474
"File `{}` passed to `-C profile-sample-use` does not exist.",
1475-
path.display()
1475+
path.as_path().display()
14761476
));
14771477
}
14781478
}

compiler/rustc_session/src/utils.rs

+62
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
1+
use crate::config::dep_tracking::DepTrackingHash;
2+
use crate::config::ErrorOutputType;
13
use crate::session::Session;
24
use rustc_data_structures::profiling::VerboseTimingGuard;
5+
use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
6+
use std::collections::hash_map::DefaultHasher;
7+
use std::fs::File;
8+
use std::io::Read;
39
use std::path::{Path, PathBuf};
410

511
impl Session {
@@ -91,3 +97,59 @@ impl CanonicalizedPath {
9197
&self.original
9298
}
9399
}
100+
101+
/// A path that should be invalidated when the file that it points to has changed.
102+
/// `ContentHashedFilePath` is identified by its contents only, so even if the filepath itself
103+
/// changes, but the contents stay the same, it will contain the same hash.
104+
#[derive(Clone, Debug)]
105+
pub struct ContentHashedFilePath {
106+
path: PathBuf,
107+
hash: (u64, u64),
108+
}
109+
110+
impl ContentHashedFilePath {
111+
pub fn new(path: PathBuf) -> Self {
112+
// If the file does not exist or couldn't be hashed, just use a placeholder hash
113+
let hash = hash_file(&path).unwrap_or((0, 0));
114+
Self { path, hash }
115+
}
116+
117+
pub fn as_path(&self) -> &Path {
118+
self.path.as_path()
119+
}
120+
}
121+
122+
impl From<ContentHashedFilePath> for PathBuf {
123+
fn from(path: ContentHashedFilePath) -> Self {
124+
path.path
125+
}
126+
}
127+
128+
impl DepTrackingHash for ContentHashedFilePath {
129+
fn hash(
130+
&self,
131+
hasher: &mut DefaultHasher,
132+
_error_format: ErrorOutputType,
133+
_for_crate_hash: bool,
134+
) {
135+
std::hash::Hash::hash(&self.hash, hasher);
136+
}
137+
}
138+
139+
fn hash_file(path: &Path) -> std::io::Result<(u64, u64)> {
140+
let mut hasher = StableHasher::new();
141+
142+
let mut file = File::open(path)?;
143+
let mut buffer = [0; 128 * 1024];
144+
145+
loop {
146+
let count = file.read(&mut buffer)?;
147+
if count == 0 {
148+
break;
149+
}
150+
151+
buffer[..count].hash_stable(&mut (), &mut hasher);
152+
}
153+
154+
Ok(hasher.finalize())
155+
}

0 commit comments

Comments
 (0)