use super::super::super::*;
pub(super) struct HashVerifyOutcome {
pub(super) hash_map: HashMap<NormalizedPath, ContentHash>,
pub(super) hash_source_ns: u64,
pub(super) hash_headers_ns: u64,
pub(super) depgraph_check_ns: u64,
pub(super) verdict: crate::depgraph::CacheVerdict,
pub(super) diag_reason: String,
}
pub(super) enum HashSourceOutcome {
Ready(HashVerifyOutcome),
Fallback,
}
pub(super) struct HashVerifyInput<'a> {
pub(super) state: &'a SharedState,
pub(super) sid: &'a SessionId,
pub(super) context_key: ContextKey,
pub(super) source_path: &'a NormalizedPath,
pub(super) ctx: &'a CompileContext,
pub(super) rustc_extern_paths: &'a [NormalizedPath],
pub(super) snap_clock: Clock,
}
pub(super) fn hash_and_verify(input: HashVerifyInput<'_>) -> HashSourceOutcome {
let HashVerifyInput {
state,
sid,
context_key,
source_path,
ctx,
rustc_extern_paths,
snap_clock,
} = input;
let context_is_cold = state.dep_graph.load().is_cold(&context_key);
let hit_trace = std::env::var_os("ZCCACHE_HIT_TRACE").is_some();
let t2 = std::time::Instant::now();
let mut hash_map: HashMap<NormalizedPath, ContentHash> = HashMap::new();
if !context_is_cold {
match hash_file(&state.cache_system, source_path, snap_clock) {
Ok(h) => {
hash_map.insert(source_path.clone(), h);
}
Err(e) => {
write_session_log(
&state.sessions,
sid,
&format!("cache key error: {e}, falling back to direct compile"),
);
return HashSourceOutcome::Fallback;
}
}
}
let hash_source_ns = t2.elapsed().as_nanos() as u64;
let t3 = std::time::Instant::now();
let hash_headers_ns;
let depgraph_check_ns;
let verdict;
let diag_reason;
if context_is_cold {
hash_headers_ns = 0;
depgraph_check_ns = 0;
verdict = crate::depgraph::CacheVerdict::Cold;
diag_reason = "cold_skip".to_string();
} else {
let headers_count: usize;
{
use rayon::prelude::*;
let includes = state.dep_graph.load().get_includes(&context_key);
let include_iter = includes
.iter()
.flat_map(|v| v.iter().map(|h| (h, "header_hash_fail")));
let force_iter = ctx
.force_includes
.iter()
.map(|h| (h, "force_include_hash_fail"));
let extern_iter = rustc_extern_paths
.iter()
.map(|h| (h, "rustc_extern_hash_fail"));
let all_paths: Vec<_> = include_iter.chain(force_iter).chain(extern_iter).collect();
headers_count = all_paths.len();
let results: Vec<_> = all_paths
.par_iter()
.map(|(header, label)| {
let hash_path = resolve_pch_source(header, &state.pch_source_map)
.unwrap_or_else(|| (*header).clone());
let result = hash_file(&state.cache_system, &hash_path, snap_clock);
((*header).clone(), hash_path, result, *label)
})
.collect();
for (header, hash_path, result, label) in results {
match result {
Ok(h) => {
hash_map.insert(header, h);
}
Err(e) => {
write_session_log(
&state.sessions,
sid,
&format!("[DIAG] {label}: {} error={e}", hash_path.display()),
);
}
}
}
}
hash_headers_ns = t3.elapsed().as_nanos() as u64;
if hit_trace {
let hdr_avg_us = if headers_count > 0 {
hash_headers_ns / headers_count as u64 / 1_000
} else {
0
};
eprintln!(
"ZCCACHE_HIT_TRACE source_us={} headers_count={} headers_us={} hdr_avg_us={} \
source_path={}",
hash_source_ns / 1_000,
headers_count,
hash_headers_ns / 1_000,
hdr_avg_us,
source_path.display()
);
}
if let Some(artifact_key) = state.dep_graph.load().try_fast_hit(&context_key, |p| {
let path = NormalizedPath::new(p);
hash_map.get(&path).copied()
}) {
depgraph_check_ns = 0;
verdict = crate::depgraph::CacheVerdict::Hit { artifact_key };
diag_reason = "fast_key_match".to_string();
} else {
let t4 = std::time::Instant::now();
let result = {
let is_fresh = |p: &Path| {
let path = NormalizedPath::new(p);
!state
.cache_system
.journal()
.changed_since(&path, snap_clock)
};
let get_hash = |p: &Path| {
let path = NormalizedPath::new(p);
hash_map.get(&path).copied()
};
state
.dep_graph
.load()
.check_diagnostic(&context_key, is_fresh, get_hash)
};
depgraph_check_ns = t4.elapsed().as_nanos() as u64;
verdict = result.0;
diag_reason = result.1;
}
}
HashSourceOutcome::Ready(HashVerifyOutcome {
hash_map,
hash_source_ns,
hash_headers_ns,
depgraph_check_ns,
verdict,
diag_reason,
})
}