Skip to content

Commit b0fcb24

Browse files
committed
unstable feature usage metrics
1 parent c82e0df commit b0fcb24

File tree

10 files changed

+163
-3
lines changed

10 files changed

+163
-3
lines changed

Cargo.lock

+2
Original file line numberDiff line numberDiff line change
@@ -3684,6 +3684,8 @@ version = "0.0.0"
36843684
dependencies = [
36853685
"rustc_data_structures",
36863686
"rustc_span",
3687+
"serde",
3688+
"serde_json",
36873689
]
36883690

36893691
[[package]]

compiler/rustc_driver_impl/messages.ftl

+1
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ driver_impl_ice_bug_report = we would appreciate a bug report: {$bug_report_url}
33
driver_impl_ice_bug_report_internal_feature = using internal features is not supported and expected to cause internal compiler errors when used incorrectly
44
driver_impl_ice_bug_report_update_note = please make sure that you have updated to the latest nightly
55
driver_impl_ice_exclude_cargo_defaults = some of the compiler flags provided by cargo are hidden
6+
driver_impl_unstable_feature_usage = cannot dump feature usage metrics: {$error}
67
78
driver_impl_ice_flags = compiler flags: {$flags}
89
driver_impl_ice_path = please attach the file at `{$path}` to your bug report

compiler/rustc_driver_impl/src/lib.rs

+20-1
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,7 @@ mod signal_handler {
102102

103103
use crate::session_diagnostics::{
104104
RLinkEmptyVersionNumber, RLinkEncodingVersionMismatch, RLinkRustcVersionMismatch,
105-
RLinkWrongFileType, RlinkCorruptFile, RlinkNotAFile, RlinkUnableToRead,
105+
RLinkWrongFileType, RlinkCorruptFile, RlinkNotAFile, RlinkUnableToRead, UnstableFeatureUsage
106106
};
107107

108108
rustc_fluent_macro::fluent_messages! { "../messages.ftl" }
@@ -430,6 +430,25 @@ fn run_compiler(
430430
// Make sure name resolution and macro expansion is run.
431431
queries.global_ctxt()?.enter(|tcx| tcx.resolver_for_lowering());
432432

433+
if let Some(metrics_dir) = &sess.opts.unstable_opts.metrics_dir {
434+
queries.global_ctxt()?.enter(|tcxt| {
435+
let output_filenames = tcxt.output_filenames(());
436+
let mut metrics_file_name =
437+
std::ffi::OsString::from("unstable_feature_usage_metrics-");
438+
let mut metrics_path =
439+
output_filenames.with_directory_and_extension(metrics_dir, "json");
440+
let metrics_file_stem = metrics_path
441+
.file_name()
442+
.expect("there should be a valid default output filename");
443+
metrics_file_name.push(metrics_file_stem);
444+
metrics_path.pop();
445+
metrics_path.push(metrics_file_name);
446+
if let Err(error) = tcxt.features().dump_feature_usage_metrics(metrics_path) {
447+
tcxt.dcx().emit_err(UnstableFeatureUsage { error });
448+
}
449+
});
450+
}
451+
433452
if callbacks.after_expansion(compiler, queries) == Compilation::Stop {
434453
return early_exit();
435454
}

compiler/rustc_driver_impl/src/session_diagnostics.rs

+8
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
use std::error::Error;
2+
13
use rustc_macros::{Diagnostic, Subdiagnostic};
24

35
#[derive(Diagnostic)]
@@ -93,3 +95,9 @@ pub(crate) struct IceFlags {
9395
#[derive(Diagnostic)]
9496
#[diag(driver_impl_ice_exclude_cargo_defaults)]
9597
pub(crate) struct IceExcludeCargoDefaults;
98+
99+
#[derive(Diagnostic)]
100+
#[diag(driver_impl_unstable_feature_usage)]
101+
pub(crate) struct UnstableFeatureUsage{
102+
pub error: Box<dyn Error>,
103+
}

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

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

654+
655+
impl Features {
656+
pub fn dump_feature_usage_metrics(&self, metrics_path: PathBuf) -> Result<(), Box<dyn std::error::Error>> {
657+
#[derive(serde::Serialize)]
658+
struct LibFeature {
659+
symbol: String,
660+
}
661+
662+
#[derive(serde::Serialize)]
663+
struct LangFeature {
664+
symbol: String,
665+
since: Option<String>,
666+
}
667+
668+
#[derive(serde::Serialize)]
669+
struct FeatureUsage {
670+
lib_features: Vec<LibFeature>,
671+
lang_features: Vec<LangFeature>,
672+
}
673+
674+
// FIXME(yaahc): once metrics can be enabled by default we will want "failure to emit
675+
// default metrics" to only produce a warning when metrics are enabled by default and emit
676+
// an error only when the user manually enables metrics
677+
let metrics_file = std::fs::File::create(metrics_path)?;
678+
let metrics_file = std::io::BufWriter::new(metrics_file);
679+
680+
let lib_features = self
681+
.enabled_lib_features
682+
.iter()
683+
.map(|EnabledLibFeature { gate_name, .. }| LibFeature { symbol: gate_name.to_string() })
684+
.collect();
685+
686+
let lang_features = self
687+
.enabled_lang_features
688+
.iter()
689+
.map(|EnabledLangFeature { gate_name, stable_since, .. }| LangFeature {
690+
symbol: gate_name.to_string(),
691+
since: stable_since.map(|since| since.to_string()),
692+
})
693+
.collect();
694+
695+
let feature_usage = FeatureUsage { lib_features, lang_features };
696+
697+
serde_json::to_writer(metrics_file, &feature_usage)?;
698+
699+
Ok(())
700+
}
701+
}
702+
652703
/// Some features are not allowed to be used together at the same time, if
653704
/// the two are present, produce an error.
654705
///

compiler/rustc_session/src/config.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -1068,7 +1068,7 @@ impl OutputFilenames {
10681068
self.with_directory_and_extension(&self.out_directory, extension)
10691069
}
10701070

1071-
fn with_directory_and_extension(&self, directory: &PathBuf, extension: &str) -> PathBuf {
1071+
pub fn with_directory_and_extension(&self, directory: &PathBuf, extension: &str) -> PathBuf {
10721072
let mut path = directory.join(&self.filestem);
10731073
path.set_extension(extension);
10741074
path

compiler/rustc_session/src/options.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -1889,7 +1889,7 @@ options! {
18891889
meta_stats: bool = (false, parse_bool, [UNTRACKED],
18901890
"gather metadata statistics (default: no)"),
18911891
metrics_dir: Option<PathBuf> = (None, parse_opt_pathbuf, [UNTRACKED],
1892-
"stores metrics about the errors being emitted by rustc to disk"),
1892+
"the directory metrics emitted by rustc are dumped into (implicitly enables default set of metrics)"),
18931893
mir_emit_retag: bool = (false, parse_bool, [TRACKED],
18941894
"emit Retagging MIR statements, interpreted e.g., by miri; implies -Zmir-opt-level=0 \
18951895
(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,68 @@
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, has_extension, filename_contains, rfs, run_in_tmpdir, rustc, serde_json, shallow_find_files,
20+
};
21+
22+
fn find_feature_usage_metrics<P: AsRef<Path>>(dir: P) -> Vec<PathBuf> {
23+
shallow_find_files(dir, |path| {
24+
if filename_contains(path, "unstable_feature_usage") && has_extension(path, "json") {
25+
true
26+
} else {
27+
dbg!(path);
28+
false
29+
}
30+
})
31+
}
32+
33+
fn main() {
34+
test_metrics_dump();
35+
}
36+
37+
#[track_caller]
38+
fn test_metrics_dump() {
39+
run_in_tmpdir(|| {
40+
let metrics_dir = cwd().join("metrics");
41+
rustc()
42+
.input("lib.rs")
43+
.env("RUST_BACKTRACE", "short")
44+
.arg(format!("-Zmetrics-dir={}", metrics_dir.display()))
45+
.run();
46+
let mut metrics = find_feature_usage_metrics(&metrics_dir);
47+
let json_path =
48+
metrics.pop().expect("there should be one metrics file in the output directory");
49+
50+
assert_eq!(
51+
0,
52+
metrics.len(),
53+
"there should be no more than one metrics file in the output directory"
54+
);
55+
56+
let message = rfs::read_to_string(json_path);
57+
let parsed: serde_json::Value =
58+
serde_json::from_str(&message).expect("metrics should be dumped as json");
59+
let expected = serde_json::json!(
60+
{
61+
"lib_features":[{"symbol":"ascii_char"}],
62+
"lang_features":[{"symbol":"box_patterns","since":null}]
63+
}
64+
);
65+
66+
assert_eq!(expected, parsed);
67+
});
68+
}

0 commit comments

Comments
 (0)