Skip to content

Commit

Permalink
unstable feature usage metrics
Browse files Browse the repository at this point in the history
  • Loading branch information
yaahc committed Nov 12, 2024
1 parent 6503543 commit 3addcf4
Show file tree
Hide file tree
Showing 7 changed files with 145 additions and 1 deletion.
2 changes: 2 additions & 0 deletions Cargo.lock
Original file line number Diff line number Diff line change
Expand Up @@ -3684,6 +3684,8 @@ version = "0.0.0"
dependencies = [
"rustc_data_structures",
"rustc_span",
"serde",
"serde_json",
]

[[package]]
Expand Down
11 changes: 11 additions & 0 deletions compiler/rustc_driver_impl/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ use rustc_session::lint::{Lint, LintId};
use rustc_session::output::collect_crate_types;
use rustc_session::{EarlyDiagCtxt, Session, config, filesearch};
use rustc_span::FileName;
use rustc_span::def_id::LOCAL_CRATE;
use rustc_span::source_map::FileLoader;
use rustc_target::json::ToJson;
use rustc_target::spec::{Target, TargetTuple};
Expand Down Expand Up @@ -430,6 +431,16 @@ fn run_compiler(
// Make sure name resolution and macro expansion is run.
queries.global_ctxt()?.enter(|tcx| tcx.resolver_for_lowering());

if let Some(metrics_dir) = &sess.opts.unstable_opts.metrics_dir {
compiler.enter(|queries| -> Result<(), ErrorGuaranteed> {
queries.global_ctxt()?.enter(|tcxt| {
let crate_id = tcxt.stable_crate_id(LOCAL_CRATE);
tcxt.features().dump_feature_usage_metrics(metrics_dir, crate_id)
});
Ok(())
})?;
}

if callbacks.after_expansion(compiler, queries) == Compilation::Stop {
return early_exit();
}
Expand Down
2 changes: 2 additions & 0 deletions compiler/rustc_feature/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,6 @@ edition = "2021"
# tidy-alphabetical-start
rustc_data_structures = { path = "../rustc_data_structures" }
rustc_span = { path = "../rustc_span" }
serde = { version = "1.0.125", features = [ "derive" ] }
serde_json = "1.0.59"
# tidy-alphabetical-end
57 changes: 57 additions & 0 deletions compiler/rustc_feature/src/unstable.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
//! List of the unstable feature gates.
use std::path::Path;

use rustc_data_structures::fx::FxHashSet;
use rustc_span::Span;
use rustc_span::def_id::StableCrateId;
use rustc_span::symbol::{Symbol, sym};

use super::{Feature, to_nonzero};
Expand Down Expand Up @@ -649,6 +652,60 @@ declare_features! (
// -------------------------------------------------------------------------
);

impl Features {
pub fn dump_feature_usage_metrics(&self, metrics_dir: &Path, _crate_id: StableCrateId) {
#[derive(serde::Serialize)]
struct LibFeature {
symbol: String,
}

#[derive(serde::Serialize)]
struct LangFeature {
symbol: String,
since: Option<String>,
}

#[derive(serde::Serialize)]
struct FeatureUsage {
lib_features: Vec<LibFeature>,
lang_features: Vec<LangFeature>,
}

// TODO (DECIDE): How fine grained do we want to track feature usage?

Check failure on line 674 in compiler/rustc_feature/src/unstable.rs

View workflow job for this annotation

GitHub Actions / PR - mingw-check-tidy

TODO is used for tasks that should be done before merging a PR; If you want to leave a message in the codebase use FIXME
// Jane Preference: i want to track usage for code that gets used, not code
// that is in development, but I don't know how we'd hook into this for code
// that doesn't participate in the crates.io ecosystem, those crates won't even
// necessarily have releases or versioning norms that match other crates, so we
// may have to just track per compilation and aggressively collapse metrics to
// avoid unnecessary disk usage.
// TODO avoid filename collisions between runs

Check failure on line 681 in compiler/rustc_feature/src/unstable.rs

View workflow job for this annotation

GitHub Actions / PR - mingw-check-tidy

TODO is used for tasks that should be done before merging a PR; If you want to leave a message in the codebase use FIXME
// let path = format!("unstable_feature_usage-{crate_id:?}.json");
let feature_metrics_file = metrics_dir.join("unstable_feature_usage.json");
println!("{}", feature_metrics_file.display());
let feature_metrics_file = std::fs::File::create(feature_metrics_file).unwrap();
let feature_metrics_file = std::io::BufWriter::new(feature_metrics_file);

let lib_features = self
.enabled_lib_features
.iter()
.map(|EnabledLibFeature { gate_name, .. }| LibFeature { symbol: gate_name.to_string() })
.collect();

let lang_features = self
.enabled_lang_features
.iter()
.map(|EnabledLangFeature { gate_name, stable_since, .. }| LangFeature {
symbol: gate_name.to_string(),
since: stable_since.map(|since| since.to_string()),
})
.collect();

let feature_usage = FeatureUsage { lib_features, lang_features };

let _ = serde_json::to_writer(feature_metrics_file, &feature_usage);
}
}

/// Some features are not allowed to be used together at the same time, if
/// the two are present, produce an error.
///
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_session/src/options.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1889,7 +1889,7 @@ options! {
meta_stats: bool = (false, parse_bool, [UNTRACKED],
"gather metadata statistics (default: no)"),
metrics_dir: Option<PathBuf> = (None, parse_opt_pathbuf, [UNTRACKED],
"stores metrics about the errors being emitted by rustc to disk"),
"the directory metrics emitted by rustc are dumped into (implicitly enables default set of metrics)"),
mir_emit_retag: bool = (false, parse_bool, [TRACKED],
"emit Retagging MIR statements, interpreted e.g., by miri; implies -Zmir-opt-level=0 \
(default: no)"),
Expand Down
9 changes: 9 additions & 0 deletions tests/run-make/unstable-feature-usage-metrics/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
#![feature(ascii_char)] // random lib feature
#![feature(box_patterns)] // random lang feature

// picked arbitrary unstable features, just need a random lib and lang feature, ideally ones that
// won't be stabilized any time soon so we don't have to update this test

fn main() {
println!("foobar");
}
63 changes: 63 additions & 0 deletions tests/run-make/unstable-feature-usage-metrics/rmake.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
//! This test checks if unstable feature usage metric dump files `unstable-feature-usage*.json` work
//! as expected.
//!
//! - Basic sanity checks on a default ICE dump.
//!
//! See <https://github.com/rust-lang/rust/issues/129485>.
//!
//! # Test history
//!
//! - forked from dump-ice-to-disk test, which has flakeyness issues on i686-mingw, I'm assuming
//! those will be present in this test as well on the same platform
//@ ignore-windows
//FIXME(#128911): still flakey on i686-mingw.

use std::path::{Path, PathBuf};

use run_make_support::{
cwd, has_extension, has_prefix, rfs, run_in_tmpdir, rustc, serde_json, shallow_find_files,
};

fn find_feature_usage_metrics<P: AsRef<Path>>(dir: P) -> Vec<PathBuf> {
shallow_find_files(dir, |path| {
has_prefix(path, "unstable_feature_usage") && has_extension(path, "json")
})
}

fn main() {
test_metrics_dump();
}

#[track_caller]
fn test_metrics_dump() {
run_in_tmpdir(|| {
let metrics_dir = cwd().join("metrics");
rustc()
.input("lib.rs")
.env("RUST_BACKTRACE", "short")
.arg(format!("-Zmetrics-dir={}", metrics_dir.display()))
.run();
let mut metrics = find_feature_usage_metrics(&metrics_dir);
let json_path =
metrics.pop().expect("there should be exactly metrics file in the output directory");

assert_eq!(
0,
metrics.len(),
"there should be exactly one metrics file in the output directory"
);

let message = rfs::read_to_string(json_path);
let parsed: serde_json::Value =
serde_json::from_str(&message).expect("metrics should be dumped as json");
let expected = serde_json::json!(
{
"lib_features":[{"symbol":"ascii_char"}],
"lang_features":[{"symbol":"box_patterns","since":null}]
}
);

assert_eq!(expected, parsed);
});
}

0 comments on commit 3addcf4

Please sign in to comment.