Skip to content

Commit fe5403f

Browse files
authored
Rollup merge of #130236 - yaahc:unstable-feature-usage, r=estebank
unstable feature usage metrics example output ``` test-lib on  master [?] is 📦 v0.1.0 via 🦀 v1.80.1 ❯ cat src/lib.rs ───────┬─────────────────────────────────────────────────────── │ File: src/lib.rs ───────┼─────────────────────────────────────────────────────── 1 │ #![feature(unix_set_mark)] 2 │ pub fn add(left: u64, right: u64) -> u64 { 3 │ left + right 4 │ } 5 │ 6 │ #[cfg(test)] 7 │ mod tests { 8 │ use super::*; 9 │ 10 │ #[test] 11 │ fn it_works() { 12 │ let result = add(2, 2); 13 │ assert_eq!(result, 4); 14 │ } 15 │ } ───────┴─────────────────────────────────────────────────────── test-lib on  master [?] is 📦 v0.1.0 via 🦀 v1.80.1 ❯ cargo +stage1 rustc -- -Zmetrics-dir=$PWD/metrics Compiling test-lib v0.1.0 (/home/yaahc/tmp/test-lib) Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.08s test-lib on  master [?] is 📦 v0.1.0 via 🦀 v1.80.1 ❯ cat metrics/unstable_feature_usage.json ───────┬───────────────────────────────────────────────────────────────────── │ File: metrics/unstable_feature_usage.json ───────┼───────────────────────────────────────────────────────────────────── 1 │ {"lib_features":[{"symbol":"unix_set_mark"}],"lang_features":[]} ``` related to #129485
2 parents 0b1bf71 + 0a14f71 commit fe5403f

File tree

10 files changed

+185
-3
lines changed

10 files changed

+185
-3
lines changed

Cargo.lock

+2
Original file line numberDiff line numberDiff line change
@@ -3685,6 +3685,8 @@ version = "0.0.0"
36853685
dependencies = [
36863686
"rustc_data_structures",
36873687
"rustc_span",
3688+
"serde",
3689+
"serde_json",
36883690
]
36893691

36903692
[[package]]

compiler/rustc_driver_impl/messages.ftl

+2
Original file line numberDiff line numberDiff line change
@@ -23,3 +23,5 @@ driver_impl_rlink_rustc_version_mismatch = .rlink file was produced by rustc ver
2323
driver_impl_rlink_unable_to_read = failed to read rlink file: `{$err}`
2424
2525
driver_impl_rlink_wrong_file_type = The input does not look like a .rlink file
26+
27+
driver_impl_unstable_feature_usage = cannot dump feature usage metrics: {$error}

compiler/rustc_driver_impl/src/lib.rs

+23-1
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ use rustc_interface::{Linker, Queries, interface, passes};
5151
use rustc_lint::unerased_lint_store;
5252
use rustc_metadata::creader::MetadataLoader;
5353
use rustc_metadata::locator;
54+
use rustc_middle::ty::TyCtxt;
5455
use rustc_parse::{new_parser_from_file, new_parser_from_source_str, unwrap_or_emit_fatal};
5556
use rustc_session::config::{
5657
CG_OPTIONS, ErrorOutputType, Input, OutFileName, OutputType, UnstableOptions, Z_OPTIONS,
@@ -103,7 +104,7 @@ mod signal_handler {
103104

104105
use crate::session_diagnostics::{
105106
RLinkEmptyVersionNumber, RLinkEncodingVersionMismatch, RLinkRustcVersionMismatch,
106-
RLinkWrongFileType, RlinkCorruptFile, RlinkNotAFile, RlinkUnableToRead,
107+
RLinkWrongFileType, RlinkCorruptFile, RlinkNotAFile, RlinkUnableToRead, UnstableFeatureUsage,
107108
};
108109

109110
rustc_fluent_macro::fluent_messages! { "../messages.ftl" }
@@ -431,6 +432,10 @@ fn run_compiler(
431432
// Make sure name resolution and macro expansion is run.
432433
queries.global_ctxt()?.enter(|tcx| tcx.resolver_for_lowering());
433434

435+
if let Some(metrics_dir) = &sess.opts.unstable_opts.metrics_dir {
436+
queries.global_ctxt()?.enter(|tcxt| dump_feature_usage_metrics(tcxt, metrics_dir));
437+
}
438+
434439
if callbacks.after_expansion(compiler, queries) == Compilation::Stop {
435440
return early_exit();
436441
}
@@ -475,6 +480,23 @@ fn run_compiler(
475480
})
476481
}
477482

483+
fn dump_feature_usage_metrics(tcxt: TyCtxt<'_>, metrics_dir: &PathBuf) {
484+
let output_filenames = tcxt.output_filenames(());
485+
let mut metrics_file_name = std::ffi::OsString::from("unstable_feature_usage_metrics-");
486+
let mut metrics_path = output_filenames.with_directory_and_extension(metrics_dir, "json");
487+
let metrics_file_stem =
488+
metrics_path.file_name().expect("there should be a valid default output filename");
489+
metrics_file_name.push(metrics_file_stem);
490+
metrics_path.pop();
491+
metrics_path.push(metrics_file_name);
492+
if let Err(error) = tcxt.features().dump_feature_usage_metrics(metrics_path) {
493+
// FIXME(yaahc): once metrics can be enabled by default we will want "failure to emit
494+
// default metrics" to only produce a warning when metrics are enabled by default and emit
495+
// an error only when the user manually enables metrics
496+
tcxt.dcx().emit_err(UnstableFeatureUsage { error });
497+
}
498+
}
499+
478500
// Extract output directory and file from matches.
479501
fn make_output(matches: &getopts::Matches) -> (Option<PathBuf>, Option<OutFileName>) {
480502
let odir = matches.opt_str("out-dir").map(|o| PathBuf::from(&o));

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

+50
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};
@@ -651,6 +653,54 @@ declare_features! (
651653
// -------------------------------------------------------------------------
652654
);
653655

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

compiler/rustc_session/src/config.rs

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

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

compiler/rustc_session/src/options.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -1887,7 +1887,7 @@ options! {
18871887
meta_stats: bool = (false, parse_bool, [UNTRACKED],
18881888
"gather metadata statistics (default: no)"),
18891889
metrics_dir: Option<PathBuf> = (None, parse_opt_pathbuf, [UNTRACKED],
1890-
"stores metrics about the errors being emitted by rustc to disk"),
1890+
"the directory metrics emitted by rustc are dumped into (implicitly enables default set of metrics)"),
18911891
mir_emit_retag: bool = (false, parse_bool, [TRACKED],
18921892
"emit Retagging MIR statements, interpreted e.g., by miri; implies -Zmir-opt-level=0 \
18931893
(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,87 @@
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::rfs::create_dir_all;
19+
use run_make_support::{
20+
cwd, filename_contains, has_extension, rfs, run_in_tmpdir, rustc, serde_json,
21+
shallow_find_files,
22+
};
23+
24+
fn find_feature_usage_metrics<P: AsRef<Path>>(dir: P) -> Vec<PathBuf> {
25+
shallow_find_files(dir, |path| {
26+
if filename_contains(path, "unstable_feature_usage") && has_extension(path, "json") {
27+
true
28+
} else {
29+
dbg!(path);
30+
false
31+
}
32+
})
33+
}
34+
35+
fn main() {
36+
test_metrics_dump();
37+
test_metrics_errors();
38+
}
39+
40+
#[track_caller]
41+
fn test_metrics_dump() {
42+
run_in_tmpdir(|| {
43+
let metrics_dir = cwd().join("metrics");
44+
create_dir_all(&metrics_dir);
45+
rustc()
46+
.input("lib.rs")
47+
.env("RUST_BACKTRACE", "short")
48+
.arg(format!("-Zmetrics-dir={}", metrics_dir.display()))
49+
.run();
50+
let mut metrics = find_feature_usage_metrics(&metrics_dir);
51+
let json_path =
52+
metrics.pop().expect("there should be one metrics file in the output directory");
53+
54+
// After the `pop` above, there should be no files left.
55+
assert!(
56+
metrics.is_empty(),
57+
"there should be no more than one metrics file in the output directory"
58+
);
59+
60+
let message = rfs::read_to_string(json_path);
61+
let parsed: serde_json::Value =
62+
serde_json::from_str(&message).expect("metrics should be dumped as json");
63+
let expected = serde_json::json!(
64+
{
65+
"lib_features":[{"symbol":"ascii_char"}],
66+
"lang_features":[{"symbol":"box_patterns","since":null}]
67+
}
68+
);
69+
70+
assert_eq!(expected, parsed);
71+
});
72+
}
73+
74+
#[track_caller]
75+
fn test_metrics_errors() {
76+
run_in_tmpdir(|| {
77+
rustc()
78+
.input("lib.rs")
79+
.env("RUST_BACKTRACE", "short")
80+
.arg("-Zmetrics-dir=invaliddirectorythatdefinitelydoesntexist")
81+
.run_fail()
82+
.assert_stderr_contains(
83+
"error: cannot dump feature usage metrics: No such file or directory",
84+
)
85+
.assert_stdout_not_contains("internal compiler error");
86+
});
87+
}

0 commit comments

Comments
 (0)