Skip to content

Commit 29fcb25

Browse files
committed
unstable feature usage metrics
1 parent 17b322f commit 29fcb25

File tree

7 files changed

+134
-1
lines changed

7 files changed

+134
-1
lines changed

Cargo.lock

+2
Original file line numberDiff line numberDiff line change
@@ -3681,6 +3681,8 @@ version = "0.0.0"
36813681
dependencies = [
36823682
"rustc_data_structures",
36833683
"rustc_span",
3684+
"serde",
3685+
"serde_json",
36843686
]
36853687

36863688
[[package]]

compiler/rustc_driver_impl/src/lib.rs

+10
Original file line numberDiff line numberDiff line change
@@ -432,6 +432,16 @@ fn run_compiler(
432432
// Make sure name resolution and macro expansion is run.
433433
queries.global_ctxt()?.enter(|tcx| tcx.resolver_for_lowering());
434434

435+
// TODO: DECIDE where to put metrics in dump
436+
if let Some(metrics_dir) = &sess.opts.unstable_opts.metrics_dir {
437+
compiler.enter(|queries| -> Result<(), ErrorGuaranteed> {
438+
queries
439+
.global_ctxt()?
440+
.enter(|tcxt| tcxt.features().dump_feature_usage_metrics(metrics_dir));
441+
Ok(())
442+
})?;
443+
}
444+
435445
if callbacks.after_expansion(compiler, queries) == Compilation::Stop {
436446
return early_exit();
437447
}

compiler/rustc_feature/Cargo.toml

+2
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,6 @@ edition = "2021"
77
# tidy-alphabetical-start
88
rustc_data_structures = { path = "../rustc_data_structures" }
99
rustc_span = { path = "../rustc_span" }
10+
serde = { version = "1.0.125", features = [ "derive" ] }
11+
serde_json = "1.0.59"
1012
# tidy-alphabetical-end

compiler/rustc_feature/src/unstable.rs

+55
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
//! List of the unstable feature gates.
22
3+
use std::path::Path;
4+
35
use rustc_data_structures::fx::FxHashSet;
46
use rustc_span::symbol::{sym, Symbol};
57
use rustc_span::Span;
@@ -649,6 +651,59 @@ declare_features! (
649651
// -------------------------------------------------------------------------
650652
);
651653

654+
impl Features {
655+
pub fn dump_feature_usage_metrics(&self, metrics_dir: &Path) {
656+
#[derive(serde::Serialize)]
657+
struct LibFeature {
658+
symbol: String,
659+
}
660+
661+
#[derive(serde::Serialize)]
662+
struct LangFeature {
663+
symbol: String,
664+
since: Option<String>,
665+
}
666+
667+
#[derive(serde::Serialize)]
668+
struct FeatureUsage {
669+
lib_features: Vec<LibFeature>,
670+
lang_features: Vec<LangFeature>,
671+
}
672+
673+
// TODO (DECIDE): How fine grained do we want to track feature usage?
674+
// Jane Preference: i want to track usage for code that gets used, not code
675+
// that is in development, but I don't know how we'd hook into this for code
676+
// that doesn't participate in the crates.io ecosystem, those crates won't even
677+
// necessarily have releases or versioning norms that match other crates, so we
678+
// may have to just track per compilation and aggressively collapse metrics to
679+
// avoid unnecessary disk usage.
680+
// TODO avoid filename collisions between runs
681+
let feature_metrics_file = metrics_dir.join("unstable_feature_usage.json");
682+
println!("{}", feature_metrics_file.display());
683+
let feature_metrics_file = std::fs::File::create(feature_metrics_file).unwrap();
684+
let feature_metrics_file = std::io::BufWriter::new(feature_metrics_file);
685+
686+
let lib_features = self
687+
.declared_lib_features
688+
.iter()
689+
.map(|(symbol, _)| LibFeature { symbol: symbol.to_string() })
690+
.collect();
691+
692+
let lang_features = self
693+
.declared_lang_features
694+
.iter()
695+
.map(|(symbol, _, since)| LangFeature {
696+
symbol: symbol.to_string(),
697+
since: since.map(|since| since.to_string()),
698+
})
699+
.collect();
700+
701+
let feature_usage = FeatureUsage { lib_features, lang_features };
702+
703+
let _ = serde_json::to_writer(feature_metrics_file, &feature_usage);
704+
}
705+
}
706+
652707
/// Some features are not allowed to be used together at the same time, if
653708
/// the two are present, produce an error.
654709
///

compiler/rustc_session/src/options.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -1845,7 +1845,7 @@ options! {
18451845
meta_stats: bool = (false, parse_bool, [UNTRACKED],
18461846
"gather metadata statistics (default: no)"),
18471847
metrics_dir: Option<PathBuf> = (None, parse_opt_pathbuf, [UNTRACKED],
1848-
"stores metrics about the errors being emitted by rustc to disk"),
1848+
"the directory metrics emitted by rustc are dumped into (implicitly enables default set of metrics)"),
18491849
mir_emit_retag: bool = (false, parse_bool, [TRACKED],
18501850
"emit Retagging MIR statements, interpreted e.g., by miri; implies -Zmir-opt-level=0 \
18511851
(default: no)"),
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
#![feature(ascii_char)] // random lib feature
2+
#![feature(box_patterns)] // random lang feature
3+
4+
// picked arbitrary unstable features, just need a random lib and lang feature, ideally ones that
5+
// won't be stabilized any time soon so we don't have to update this test
6+
7+
fn main() {
8+
println!("foobar");
9+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
//! This test checks if unstable feature usage metric dump files `unstable-feature-usage*.json` work
2+
//! as expected.
3+
//!
4+
//! - Basic sanity checks on a default ICE dump.
5+
//!
6+
//! See <https://github.com/rust-lang/rust/issues/129485>.
7+
//!
8+
//! # Test history
9+
//!
10+
//! - forked from dump-ice-to-disk test, which has flakeyness issues on i686-mingw, I'm assuming
11+
//! those will be present in this test as well on the same platform
12+
13+
//@ ignore-windows
14+
//FIXME(#128911): still flakey on i686-mingw.
15+
16+
use std::path::{Path, PathBuf};
17+
18+
use run_make_support::{
19+
cwd, filename_contains, has_extension, has_prefix, rfs, run_in_tmpdir, rustc,
20+
shallow_find_files,
21+
};
22+
23+
fn find_feature_usage_metrics<P: AsRef<Path>>(dir: P) -> Vec<PathBuf> {
24+
shallow_find_files(dir, |path| {
25+
has_prefix(path, "unstable_feature_usage") && has_extension(path, "json")
26+
})
27+
}
28+
29+
fn main() {
30+
test_metrics_dump();
31+
}
32+
33+
#[track_caller]
34+
fn test_metrics_dump() {
35+
run_in_tmpdir(|| {
36+
let metrics_dir = cwd().join("metrics");
37+
rustc()
38+
.input("lib.rs")
39+
.env("RUST_BACKTRACE", "short")
40+
.arg(format!("-Zmetrics-dir={}", metrics_dir.display()))
41+
.run();
42+
let mut metrics = find_feature_usage_metrics(&metrics_dir);
43+
let json_path =
44+
metrics.pop().expect("there should be exactly metrics file in the output directory");
45+
assert_eq!(
46+
0,
47+
metrics.len(),
48+
"there should be exactly one metrics file in the output directory"
49+
);
50+
51+
let message = rfs::read_to_string(json_path);
52+
println!("{message}");
53+
panic!();
54+
});
55+
}

0 commit comments

Comments
 (0)