Skip to content

Commit eb53eea

Browse files
committed
Add json output to -Zdump-mono-stats
This allows analyzing the output programatically; for example, finding the item with the highest `total_estimate`. I also took the liberty of adding `untracked` tests to `rustc_session` and documentation to the unstable book for `dump-mono-items`.
1 parent ce85c98 commit eb53eea

File tree

11 files changed

+108
-23
lines changed

11 files changed

+108
-23
lines changed

Cargo.lock

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4226,6 +4226,8 @@ dependencies = [
42264226
"rustc_session",
42274227
"rustc_span",
42284228
"rustc_target",
4229+
"serde",
4230+
"serde_json",
42294231
"smallvec",
42304232
"tracing",
42314233
]

compiler/rustc_interface/src/tests.rs

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,17 +3,16 @@ use crate::interface::parse_cfgspecs;
33

44
use rustc_data_structures::fx::FxHashSet;
55
use rustc_errors::{emitter::HumanReadableErrorType, registry, ColorConfig};
6-
use rustc_session::config::InstrumentCoverage;
7-
use rustc_session::config::Strip;
6+
use rustc_session::config::rustc_optgroups;
87
use rustc_session::config::{build_configuration, build_session_options, to_crate_config};
9-
use rustc_session::config::{
10-
rustc_optgroups, ErrorOutputType, ExternLocation, LocationDetail, Options, Passes,
11-
};
128
use rustc_session::config::{
139
BranchProtection, Externs, OomStrategy, OutputType, OutputTypes, PAuthKey, PacRet,
1410
ProcMacroExecutionStrategy, SymbolManglingVersion, WasiExecModel,
1511
};
1612
use rustc_session::config::{CFGuard, ExternEntry, LinkerPluginLto, LtoCli, SwitchWithOptPath};
13+
use rustc_session::config::{DumpMonoStatsFormat, MirSpanview};
14+
use rustc_session::config::{ErrorOutputType, ExternLocation, LocationDetail, Options, Strip};
15+
use rustc_session::config::{InstrumentCoverage, Passes};
1716
use rustc_session::lint::Level;
1817
use rustc_session::search_paths::SearchPath;
1918
use rustc_session::utils::{CanonicalizedPath, NativeLib, NativeLibKind};
@@ -647,6 +646,9 @@ fn test_unstable_options_tracking_hash() {
647646
untracked!(dump_mir_dir, String::from("abc"));
648647
untracked!(dump_mir_exclude_pass_number, true);
649648
untracked!(dump_mir_graphviz, true);
649+
untracked!(dump_mir_spanview, Some(MirSpanview::Statement));
650+
untracked!(dump_mono_stats, SwitchWithOptPath::Enabled(Some("mono-items-dir/".into())));
651+
untracked!(dump_mono_stats_format, DumpMonoStatsFormat::Json);
650652
untracked!(dylib_lto, true);
651653
untracked!(emit_stack_sizes, true);
652654
untracked!(future_incompat_test, true);

compiler/rustc_monomorphize/Cargo.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ edition = "2021"
66
[lib]
77

88
[dependencies]
9+
serde = "1"
10+
serde_json = "1"
911
smallvec = { version = "1.8.1", features = [ "union", "may_dangle" ] }
1012
tracing = "0.1"
1113
rustc_data_structures = { path = "../rustc_data_structures" }

compiler/rustc_monomorphize/src/partitioning/mod.rs

Lines changed: 32 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,7 @@ use rustc_middle::mir::mono::{CodegenUnit, Linkage};
109109
use rustc_middle::ty::print::with_no_trimmed_paths;
110110
use rustc_middle::ty::query::Providers;
111111
use rustc_middle::ty::TyCtxt;
112-
use rustc_session::config::SwitchWithOptPath;
112+
use rustc_session::config::{DumpMonoStatsFormat, SwitchWithOptPath};
113113
use rustc_span::symbol::Symbol;
114114

115115
use crate::collector::InliningMap;
@@ -492,9 +492,11 @@ fn dump_mono_items_stats<'tcx>(
492492
Path::new(".")
493493
};
494494

495-
let filename = format!("{}.mono_items.md", crate_name.unwrap_or("unknown-crate"));
495+
let format = tcx.sess.opts.unstable_opts.dump_mono_stats_format;
496+
let ext = format.extension();
497+
let filename = format!("{}.mono_items.{ext}", crate_name.unwrap_or("unknown-crate"));
496498
let output_path = output_directory.join(&filename);
497-
let file = File::create(output_path)?;
499+
let file = File::create(&output_path)?;
498500
let mut file = BufWriter::new(file);
499501

500502
// Gather instantiated mono items grouped by def_id
@@ -508,30 +510,44 @@ fn dump_mono_items_stats<'tcx>(
508510
}
509511
}
510512

513+
#[derive(serde::Serialize)]
514+
struct MonoItem {
515+
name: String,
516+
instantiation_count: usize,
517+
size_estimate: usize,
518+
total_estimate: usize,
519+
}
520+
511521
// Output stats sorted by total instantiated size, from heaviest to lightest
512522
let mut stats: Vec<_> = items_per_def_id
513523
.into_iter()
514524
.map(|(def_id, items)| {
525+
let name = with_no_trimmed_paths!(tcx.def_path_str(def_id));
515526
let instantiation_count = items.len();
516527
let size_estimate = items[0].size_estimate(tcx);
517528
let total_estimate = instantiation_count * size_estimate;
518-
(def_id, instantiation_count, size_estimate, total_estimate)
529+
MonoItem { name, instantiation_count, size_estimate, total_estimate }
519530
})
520531
.collect();
521-
stats.sort_unstable_by_key(|(_, _, _, total_estimate)| cmp::Reverse(*total_estimate));
532+
stats.sort_unstable_by_key(|item| cmp::Reverse(item.total_estimate));
522533

523534
if !stats.is_empty() {
524-
writeln!(
525-
file,
526-
"| Item | Instantiation count | Estimated Cost Per Instantiation | Total Estimated Cost |"
527-
)?;
528-
writeln!(file, "| --- | ---: | ---: | ---: |")?;
529-
for (def_id, instantiation_count, size_estimate, total_estimate) in stats {
530-
let item = with_no_trimmed_paths!(tcx.def_path_str(def_id));
531-
writeln!(
532-
file,
533-
"| {item} | {instantiation_count} | {size_estimate} | {total_estimate} |"
534-
)?;
535+
match format {
536+
DumpMonoStatsFormat::Json => serde_json::to_writer(file, &stats)?,
537+
DumpMonoStatsFormat::Markdown => {
538+
writeln!(
539+
file,
540+
"| Item | Instantiation count | Estimated Cost Per Instantiation | Total Estimated Cost |"
541+
)?;
542+
writeln!(file, "| --- | ---: | ---: | ---: |")?;
543+
544+
for MonoItem { name, instantiation_count, size_estimate, total_estimate } in stats {
545+
writeln!(
546+
file,
547+
"| `{name}` | {instantiation_count} | {size_estimate} | {total_estimate} |"
548+
)?;
549+
}
550+
}
535551
}
536552
}
537553

compiler/rustc_session/src/config.rs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2975,3 +2975,21 @@ pub enum ProcMacroExecutionStrategy {
29752975
/// Run the proc-macro code on a different thread.
29762976
CrossThread,
29772977
}
2978+
2979+
/// Which format to use for `-Z dump-mono-stats`
2980+
#[derive(Clone, Copy, PartialEq, Hash, Debug)]
2981+
pub enum DumpMonoStatsFormat {
2982+
/// Pretty-print a markdown table
2983+
Markdown,
2984+
/// Emit structured JSON
2985+
Json,
2986+
}
2987+
2988+
impl DumpMonoStatsFormat {
2989+
pub fn extension(self) -> &'static str {
2990+
match self {
2991+
Self::Markdown => "md",
2992+
Self::Json => "json",
2993+
}
2994+
}
2995+
}

compiler/rustc_session/src/options.rs

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -377,6 +377,7 @@ mod desc {
377377
pub const parse_linker_flavor: &str = ::rustc_target::spec::LinkerFlavorCli::one_of();
378378
pub const parse_optimization_fuel: &str = "crate=integer";
379379
pub const parse_mir_spanview: &str = "`statement` (default), `terminator`, or `block`";
380+
pub const parse_dump_mono_stats: &str = "`markdown` (default) or `json`";
380381
pub const parse_instrument_coverage: &str =
381382
"`all` (default), `except-unused-generics`, `except-unused-functions`, or `off`";
382383
pub const parse_unpretty: &str = "`string` or `string=string`";
@@ -820,6 +821,21 @@ mod parse {
820821
true
821822
}
822823

824+
pub(crate) fn parse_dump_mono_stats(slot: &mut DumpMonoStatsFormat, v: Option<&str>) -> bool {
825+
match v {
826+
None => true,
827+
Some("json") => {
828+
*slot = DumpMonoStatsFormat::Json;
829+
true
830+
}
831+
Some("markdown") => {
832+
*slot = DumpMonoStatsFormat::Markdown;
833+
true
834+
}
835+
Some(_) => false,
836+
}
837+
}
838+
823839
pub(crate) fn parse_instrument_coverage(
824840
slot: &mut Option<InstrumentCoverage>,
825841
v: Option<&str>,
@@ -1295,7 +1311,9 @@ options! {
12951311
an additional `.html` file showing the computed coverage spans."),
12961312
dump_mono_stats: SwitchWithOptPath = (SwitchWithOptPath::Disabled,
12971313
parse_switch_with_opt_path, [UNTRACKED],
1298-
"output statistics about monomorphization collection (format: markdown)"),
1314+
"output statistics about monomorphization collection"),
1315+
dump_mono_stats_format: DumpMonoStatsFormat = (DumpMonoStatsFormat::Markdown, parse_dump_mono_stats, [UNTRACKED],
1316+
"the format to use for -Z dump-mono-stats (`markdown` (default) or `json`)"),
12991317
dwarf_version: Option<u32> = (None, parse_opt_number, [TRACKED],
13001318
"version of DWARF debug information to emit (default: 2 or 4, depending on platform)"),
13011319
dylib_lto: bool = (false, parse_bool, [UNTRACKED],
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
# `dump-mono-stats-format`
2+
3+
--------------------
4+
5+
The `-Z dump-mono-stats-format` compiler flag controls what file format to use for `-Z dump-mono-stats`.
6+
The default is markdown; currently JSON is also supported. JSON can be useful for programatically manipulating the results (e.g. to find the item that took the longest to compile).
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
# `dump-mono-stats`
2+
3+
--------------------
4+
5+
The `-Z dump-mono-stats` compiler flag generates a file with a list of the monomorphized items in the current crate.
6+
It is useful for investigating compile times.
7+
8+
It accepts an optional directory where the file will be located. If no directory is specified, the file will be placed in the current directory.
9+
10+
See also `-Z dump-mono-stats-format` and `-Z print-mono-items`. Unlike `print-mono-items`,
11+
`dump-mono-stats` aggregates monomorphized items by definition and includes a size estimate of how
12+
large the item is when codegened.
13+
14+
See <https://rustc-dev-guide.rust-lang.org/backend/monomorph.html> for an overview of monomorphized items.
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
include ../../run-make-fulldeps/tools.mk
2+
3+
all:
4+
$(RUSTC) --crate-type lib foo.rs -Z dump-mono-stats=$(TMPDIR) -Zdump-mono-stats-format=json
5+
cat $(TMPDIR)/foo.mono_items.json | $(CGREP) '"name":"bar"'
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
pub fn bar() {}

src/test/rustdoc-ui/z-help.stdout

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,8 @@
3535
-Z dump-mir-exclude-pass-number=val -- exclude the pass number when dumping MIR (used in tests) (default: no)
3636
-Z dump-mir-graphviz=val -- in addition to `.mir` files, create graphviz `.dot` files (and with `-Z instrument-coverage`, also create a `.dot` file for the MIR-derived coverage graph) (default: no)
3737
-Z dump-mir-spanview=val -- in addition to `.mir` files, create `.html` files to view spans for all `statement`s (including terminators), only `terminator` spans, or computed `block` spans (one span encompassing a block's terminator and all statements). If `-Z instrument-coverage` is also enabled, create an additional `.html` file showing the computed coverage spans.
38-
-Z dump-mono-stats=val -- output statistics about monomorphization collection (format: markdown)
38+
-Z dump-mono-stats=val -- output statistics about monomorphization collection
39+
-Z dump-mono-stats-format=val -- the format to use for -Z dump-mono-stats (`markdown` (default) or `json`)
3940
-Z dwarf-version=val -- version of DWARF debug information to emit (default: 2 or 4, depending on platform)
4041
-Z dylib-lto=val -- enables LTO for dylib crate type
4142
-Z emit-stack-sizes=val -- emit a section containing stack size metadata (default: no)

0 commit comments

Comments
 (0)