From 9ca273b902cf94b78fb56159d83c466f829b1c5f Mon Sep 17 00:00:00 2001 From: Jane Losare-Lusby Date: Fri, 6 Sep 2024 13:05:01 -0700 Subject: [PATCH] unstable feature usage metrics --- Cargo.lock | 2 + compiler/rustc_driver_impl/src/lib.rs | 17 +++++ compiler/rustc_feature/Cargo.toml | 2 + compiler/rustc_feature/src/unstable.rs | 46 +++++++++++++ compiler/rustc_session/src/config.rs | 6 +- compiler/rustc_session/src/options.rs | 2 +- .../unstable-feature-usage-metrics/lib.rs | 9 +++ .../unstable-feature-usage-metrics/rmake.rs | 68 +++++++++++++++++++ 8 files changed, 150 insertions(+), 2 deletions(-) create mode 100644 tests/run-make/unstable-feature-usage-metrics/lib.rs create mode 100644 tests/run-make/unstable-feature-usage-metrics/rmake.rs diff --git a/Cargo.lock b/Cargo.lock index 97d07cd6ca8ac..2f15361460bb9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3684,6 +3684,8 @@ version = "0.0.0" dependencies = [ "rustc_data_structures", "rustc_span", + "serde", + "serde_json", ] [[package]] diff --git a/compiler/rustc_driver_impl/src/lib.rs b/compiler/rustc_driver_impl/src/lib.rs index b6f7abed6f3c9..20685114b4ba3 100644 --- a/compiler/rustc_driver_impl/src/lib.rs +++ b/compiler/rustc_driver_impl/src/lib.rs @@ -430,6 +430,23 @@ 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 { + queries.global_ctxt()?.enter(|tcxt| { + let output_filenames = tcxt.output_filenames(()); + let mut metrics_file_name = + std::ffi::OsString::from("unstable_feature_usage_metrics-"); + let mut metrics_path = + output_filenames.with_directory_and_extension(metrics_dir, "json"); + let metrics_file_stem = metrics_path + .file_name() + .expect("there should be a valid default output filename"); + metrics_file_name.push(metrics_file_stem); + metrics_path.pop(); + metrics_path.push(metrics_file_name); + tcxt.features().dump_feature_usage_metrics(metrics_path) + }); + } + if callbacks.after_expansion(compiler, queries) == Compilation::Stop { return early_exit(); } diff --git a/compiler/rustc_feature/Cargo.toml b/compiler/rustc_feature/Cargo.toml index 9df320e1279ed..77de7fabd4f92 100644 --- a/compiler/rustc_feature/Cargo.toml +++ b/compiler/rustc_feature/Cargo.toml @@ -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 diff --git a/compiler/rustc_feature/src/unstable.rs b/compiler/rustc_feature/src/unstable.rs index 8326d0031ea60..81fcab2cb5547 100644 --- a/compiler/rustc_feature/src/unstable.rs +++ b/compiler/rustc_feature/src/unstable.rs @@ -1,5 +1,7 @@ //! List of the unstable feature gates. +use std::path::PathBuf; + use rustc_data_structures::fx::FxHashSet; use rustc_span::Span; use rustc_span::symbol::{Symbol, sym}; @@ -649,6 +651,50 @@ declare_features! ( // ------------------------------------------------------------------------- ); +impl Features { + pub fn dump_feature_usage_metrics(&self, metrics_path: PathBuf) { + #[derive(serde::Serialize)] + struct LibFeature { + symbol: String, + } + + #[derive(serde::Serialize)] + struct LangFeature { + symbol: String, + since: Option, + } + + #[derive(serde::Serialize)] + struct FeatureUsage { + lib_features: Vec, + lang_features: Vec, + } + + println!("{}", metrics_path.display()); + let metrics_file = std::fs::File::create(metrics_path).unwrap(); + let metrics_file = std::io::BufWriter::new(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(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. /// diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs index 44721bd889a0c..687f6de70fa53 100644 --- a/compiler/rustc_session/src/config.rs +++ b/compiler/rustc_session/src/config.rs @@ -1068,8 +1068,12 @@ impl OutputFilenames { self.with_directory_and_extension(&self.out_directory, extension) } - fn with_directory_and_extension(&self, directory: &PathBuf, extension: &str) -> PathBuf { + pub fn with_directory_and_extension(&self, directory: &PathBuf, extension: &str) -> PathBuf { let mut path = directory.join(&self.filestem); + // if extension == "unstable_feature_usage_metrics.json" { + // dbg!(&self.filestem); + // panic!() + // } path.set_extension(extension); path } diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs index f485e8cace54d..5d2375759919b 100644 --- a/compiler/rustc_session/src/options.rs +++ b/compiler/rustc_session/src/options.rs @@ -1889,7 +1889,7 @@ options! { meta_stats: bool = (false, parse_bool, [UNTRACKED], "gather metadata statistics (default: no)"), metrics_dir: Option = (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)"), diff --git a/tests/run-make/unstable-feature-usage-metrics/lib.rs b/tests/run-make/unstable-feature-usage-metrics/lib.rs new file mode 100644 index 0000000000000..2202d722c497e --- /dev/null +++ b/tests/run-make/unstable-feature-usage-metrics/lib.rs @@ -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"); +} diff --git a/tests/run-make/unstable-feature-usage-metrics/rmake.rs b/tests/run-make/unstable-feature-usage-metrics/rmake.rs new file mode 100644 index 0000000000000..4372085c1a6fa --- /dev/null +++ b/tests/run-make/unstable-feature-usage-metrics/rmake.rs @@ -0,0 +1,68 @@ +//! 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 . +//! +//! # 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, filename_contains, rfs, run_in_tmpdir, rustc, serde_json, shallow_find_files, +}; + +fn find_feature_usage_metrics>(dir: P) -> Vec { + shallow_find_files(dir, |path| { + if filename_contains(path, "unstable_feature_usage") && has_extension(path, "json") { + true + } else { + dbg!(path); + false + } + }) +} + +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 one metrics file in the output directory"); + + assert_eq!( + 0, + metrics.len(), + "there should be no more than 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); + }); +}