about summary refs log tree commit diff
diff options
context:
space:
mode:
authorJoshua Nelson <github@jyn.dev>2022-12-29 21:08:09 +0000
committerJoshua Nelson <github@jyn.dev>2023-01-02 23:02:58 +0000
commiteb53eea60920e28d3c3e053b170d015be7493a74 (patch)
tree8c55e3081e63532aa2313ec407cd3a24c9f26fca
parentce85c98575e3016cf2007d90a85be321e592aa96 (diff)
downloadrust-eb53eea60920e28d3c3e053b170d015be7493a74.tar.gz
rust-eb53eea60920e28d3c3e053b170d015be7493a74.zip
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`.
-rw-r--r--Cargo.lock2
-rw-r--r--compiler/rustc_interface/src/tests.rs12
-rw-r--r--compiler/rustc_monomorphize/Cargo.toml2
-rw-r--r--compiler/rustc_monomorphize/src/partitioning/mod.rs48
-rw-r--r--compiler/rustc_session/src/config.rs18
-rw-r--r--compiler/rustc_session/src/options.rs20
-rw-r--r--src/doc/unstable-book/src/compiler-flags/dump-mono-stats-format.md6
-rw-r--r--src/doc/unstable-book/src/compiler-flags/dump-mono-stats.md14
-rw-r--r--src/test/run-make/dump-mono-stats/Makefile5
-rw-r--r--src/test/run-make/dump-mono-stats/foo.rs1
-rw-r--r--src/test/rustdoc-ui/z-help.stdout3
11 files changed, 108 insertions, 23 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 9581099c210..9037feec79e 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -4226,6 +4226,8 @@ dependencies = [
  "rustc_session",
  "rustc_span",
  "rustc_target",
+ "serde",
+ "serde_json",
  "smallvec",
  "tracing",
 ]
diff --git a/compiler/rustc_interface/src/tests.rs b/compiler/rustc_interface/src/tests.rs
index ff2196d5857..947d8e80cb0 100644
--- a/compiler/rustc_interface/src/tests.rs
+++ b/compiler/rustc_interface/src/tests.rs
@@ -3,17 +3,16 @@ use crate::interface::parse_cfgspecs;
 
 use rustc_data_structures::fx::FxHashSet;
 use rustc_errors::{emitter::HumanReadableErrorType, registry, ColorConfig};
-use rustc_session::config::InstrumentCoverage;
-use rustc_session::config::Strip;
+use rustc_session::config::rustc_optgroups;
 use rustc_session::config::{build_configuration, build_session_options, to_crate_config};
 use rustc_session::config::{
-    rustc_optgroups, ErrorOutputType, ExternLocation, LocationDetail, Options, Passes,
-};
-use rustc_session::config::{
     BranchProtection, Externs, OomStrategy, OutputType, OutputTypes, PAuthKey, PacRet,
     ProcMacroExecutionStrategy, SymbolManglingVersion, WasiExecModel,
 };
 use rustc_session::config::{CFGuard, ExternEntry, LinkerPluginLto, LtoCli, SwitchWithOptPath};
+use rustc_session::config::{DumpMonoStatsFormat, MirSpanview};
+use rustc_session::config::{ErrorOutputType, ExternLocation, LocationDetail, Options, Strip};
+use rustc_session::config::{InstrumentCoverage, Passes};
 use rustc_session::lint::Level;
 use rustc_session::search_paths::SearchPath;
 use rustc_session::utils::{CanonicalizedPath, NativeLib, NativeLibKind};
@@ -647,6 +646,9 @@ fn test_unstable_options_tracking_hash() {
     untracked!(dump_mir_dir, String::from("abc"));
     untracked!(dump_mir_exclude_pass_number, true);
     untracked!(dump_mir_graphviz, true);
+    untracked!(dump_mir_spanview, Some(MirSpanview::Statement));
+    untracked!(dump_mono_stats, SwitchWithOptPath::Enabled(Some("mono-items-dir/".into())));
+    untracked!(dump_mono_stats_format, DumpMonoStatsFormat::Json);
     untracked!(dylib_lto, true);
     untracked!(emit_stack_sizes, true);
     untracked!(future_incompat_test, true);
diff --git a/compiler/rustc_monomorphize/Cargo.toml b/compiler/rustc_monomorphize/Cargo.toml
index 6ee5330b63f..c8af10576b4 100644
--- a/compiler/rustc_monomorphize/Cargo.toml
+++ b/compiler/rustc_monomorphize/Cargo.toml
@@ -6,6 +6,8 @@ edition = "2021"
 [lib]
 
 [dependencies]
+serde = "1"
+serde_json = "1"
 smallvec = { version = "1.8.1", features = [ "union", "may_dangle" ] }
 tracing = "0.1"
 rustc_data_structures = { path = "../rustc_data_structures" }
diff --git a/compiler/rustc_monomorphize/src/partitioning/mod.rs b/compiler/rustc_monomorphize/src/partitioning/mod.rs
index 4f25fc71314..f0417b06a04 100644
--- a/compiler/rustc_monomorphize/src/partitioning/mod.rs
+++ b/compiler/rustc_monomorphize/src/partitioning/mod.rs
@@ -109,7 +109,7 @@ use rustc_middle::mir::mono::{CodegenUnit, Linkage};
 use rustc_middle::ty::print::with_no_trimmed_paths;
 use rustc_middle::ty::query::Providers;
 use rustc_middle::ty::TyCtxt;
-use rustc_session::config::SwitchWithOptPath;
+use rustc_session::config::{DumpMonoStatsFormat, SwitchWithOptPath};
 use rustc_span::symbol::Symbol;
 
 use crate::collector::InliningMap;
@@ -492,9 +492,11 @@ fn dump_mono_items_stats<'tcx>(
         Path::new(".")
     };
 
-    let filename = format!("{}.mono_items.md", crate_name.unwrap_or("unknown-crate"));
+    let format = tcx.sess.opts.unstable_opts.dump_mono_stats_format;
+    let ext = format.extension();
+    let filename = format!("{}.mono_items.{ext}", crate_name.unwrap_or("unknown-crate"));
     let output_path = output_directory.join(&filename);
-    let file = File::create(output_path)?;
+    let file = File::create(&output_path)?;
     let mut file = BufWriter::new(file);
 
     // Gather instantiated mono items grouped by def_id
@@ -508,30 +510,44 @@ fn dump_mono_items_stats<'tcx>(
         }
     }
 
+    #[derive(serde::Serialize)]
+    struct MonoItem {
+        name: String,
+        instantiation_count: usize,
+        size_estimate: usize,
+        total_estimate: usize,
+    }
+
     // Output stats sorted by total instantiated size, from heaviest to lightest
     let mut stats: Vec<_> = items_per_def_id
         .into_iter()
         .map(|(def_id, items)| {
+            let name = with_no_trimmed_paths!(tcx.def_path_str(def_id));
             let instantiation_count = items.len();
             let size_estimate = items[0].size_estimate(tcx);
             let total_estimate = instantiation_count * size_estimate;
-            (def_id, instantiation_count, size_estimate, total_estimate)
+            MonoItem { name, instantiation_count, size_estimate, total_estimate }
         })
         .collect();
-    stats.sort_unstable_by_key(|(_, _, _, total_estimate)| cmp::Reverse(*total_estimate));
+    stats.sort_unstable_by_key(|item| cmp::Reverse(item.total_estimate));
 
     if !stats.is_empty() {
-        writeln!(
-            file,
-            "| Item | Instantiation count | Estimated Cost Per Instantiation | Total Estimated Cost |"
-        )?;
-        writeln!(file, "| --- | ---: | ---: | ---: |")?;
-        for (def_id, instantiation_count, size_estimate, total_estimate) in stats {
-            let item = with_no_trimmed_paths!(tcx.def_path_str(def_id));
-            writeln!(
-                file,
-                "| {item} | {instantiation_count} | {size_estimate} | {total_estimate} |"
-            )?;
+        match format {
+            DumpMonoStatsFormat::Json => serde_json::to_writer(file, &stats)?,
+            DumpMonoStatsFormat::Markdown => {
+                writeln!(
+                    file,
+                    "| Item | Instantiation count | Estimated Cost Per Instantiation | Total Estimated Cost |"
+                )?;
+                writeln!(file, "| --- | ---: | ---: | ---: |")?;
+
+                for MonoItem { name, instantiation_count, size_estimate, total_estimate } in stats {
+                    writeln!(
+                        file,
+                        "| `{name}` | {instantiation_count} | {size_estimate} | {total_estimate} |"
+                    )?;
+                }
+            }
         }
     }
 
diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs
index 3bafd3730bd..f3758cd7d27 100644
--- a/compiler/rustc_session/src/config.rs
+++ b/compiler/rustc_session/src/config.rs
@@ -2975,3 +2975,21 @@ pub enum ProcMacroExecutionStrategy {
     /// Run the proc-macro code on a different thread.
     CrossThread,
 }
+
+/// Which format to use for `-Z dump-mono-stats`
+#[derive(Clone, Copy, PartialEq, Hash, Debug)]
+pub enum DumpMonoStatsFormat {
+    /// Pretty-print a markdown table
+    Markdown,
+    /// Emit structured JSON
+    Json,
+}
+
+impl DumpMonoStatsFormat {
+    pub fn extension(self) -> &'static str {
+        match self {
+            Self::Markdown => "md",
+            Self::Json => "json",
+        }
+    }
+}
diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs
index ae01efebacc..7ff667ecb2c 100644
--- a/compiler/rustc_session/src/options.rs
+++ b/compiler/rustc_session/src/options.rs
@@ -377,6 +377,7 @@ mod desc {
     pub const parse_linker_flavor: &str = ::rustc_target::spec::LinkerFlavorCli::one_of();
     pub const parse_optimization_fuel: &str = "crate=integer";
     pub const parse_mir_spanview: &str = "`statement` (default), `terminator`, or `block`";
+    pub const parse_dump_mono_stats: &str = "`markdown` (default) or `json`";
     pub const parse_instrument_coverage: &str =
         "`all` (default), `except-unused-generics`, `except-unused-functions`, or `off`";
     pub const parse_unpretty: &str = "`string` or `string=string`";
@@ -820,6 +821,21 @@ mod parse {
         true
     }
 
+    pub(crate) fn parse_dump_mono_stats(slot: &mut DumpMonoStatsFormat, v: Option<&str>) -> bool {
+        match v {
+            None => true,
+            Some("json") => {
+                *slot = DumpMonoStatsFormat::Json;
+                true
+            }
+            Some("markdown") => {
+                *slot = DumpMonoStatsFormat::Markdown;
+                true
+            }
+            Some(_) => false,
+        }
+    }
+
     pub(crate) fn parse_instrument_coverage(
         slot: &mut Option<InstrumentCoverage>,
         v: Option<&str>,
@@ -1295,7 +1311,9 @@ options! {
         an additional `.html` file showing the computed coverage spans."),
     dump_mono_stats: SwitchWithOptPath = (SwitchWithOptPath::Disabled,
         parse_switch_with_opt_path, [UNTRACKED],
-        "output statistics about monomorphization collection (format: markdown)"),
+        "output statistics about monomorphization collection"),
+    dump_mono_stats_format: DumpMonoStatsFormat = (DumpMonoStatsFormat::Markdown, parse_dump_mono_stats, [UNTRACKED],
+        "the format to use for -Z dump-mono-stats (`markdown` (default) or `json`)"),
     dwarf_version: Option<u32> = (None, parse_opt_number, [TRACKED],
         "version of DWARF debug information to emit (default: 2 or 4, depending on platform)"),
     dylib_lto: bool = (false, parse_bool, [UNTRACKED],
diff --git a/src/doc/unstable-book/src/compiler-flags/dump-mono-stats-format.md b/src/doc/unstable-book/src/compiler-flags/dump-mono-stats-format.md
new file mode 100644
index 00000000000..a497a75261f
--- /dev/null
+++ b/src/doc/unstable-book/src/compiler-flags/dump-mono-stats-format.md
@@ -0,0 +1,6 @@
+# `dump-mono-stats-format`
+
+--------------------
+
+The `-Z dump-mono-stats-format` compiler flag controls what file format to use for `-Z dump-mono-stats`.
+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).
diff --git a/src/doc/unstable-book/src/compiler-flags/dump-mono-stats.md b/src/doc/unstable-book/src/compiler-flags/dump-mono-stats.md
new file mode 100644
index 00000000000..4c8bc8b4578
--- /dev/null
+++ b/src/doc/unstable-book/src/compiler-flags/dump-mono-stats.md
@@ -0,0 +1,14 @@
+# `dump-mono-stats`
+
+--------------------
+
+The `-Z dump-mono-stats` compiler flag generates a file with a list of the monomorphized items in the current crate.
+It is useful for investigating compile times.
+
+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.
+
+See also `-Z dump-mono-stats-format` and `-Z print-mono-items`. Unlike `print-mono-items`,
+`dump-mono-stats` aggregates monomorphized items by definition and includes a size estimate of how
+large the item is when codegened.
+
+See <https://rustc-dev-guide.rust-lang.org/backend/monomorph.html> for an overview of monomorphized items.
diff --git a/src/test/run-make/dump-mono-stats/Makefile b/src/test/run-make/dump-mono-stats/Makefile
new file mode 100644
index 00000000000..fe1112fb0a4
--- /dev/null
+++ b/src/test/run-make/dump-mono-stats/Makefile
@@ -0,0 +1,5 @@
+include ../../run-make-fulldeps/tools.mk
+
+all:
+	$(RUSTC) --crate-type lib foo.rs -Z dump-mono-stats=$(TMPDIR) -Zdump-mono-stats-format=json
+	cat $(TMPDIR)/foo.mono_items.json | $(CGREP) '"name":"bar"'
diff --git a/src/test/run-make/dump-mono-stats/foo.rs b/src/test/run-make/dump-mono-stats/foo.rs
new file mode 100644
index 00000000000..c5c0bc606cd
--- /dev/null
+++ b/src/test/run-make/dump-mono-stats/foo.rs
@@ -0,0 +1 @@
+pub fn bar() {}
diff --git a/src/test/rustdoc-ui/z-help.stdout b/src/test/rustdoc-ui/z-help.stdout
index 53677b18377..9bd6c5fedf5 100644
--- a/src/test/rustdoc-ui/z-help.stdout
+++ b/src/test/rustdoc-ui/z-help.stdout
@@ -35,7 +35,8 @@
     -Z            dump-mir-exclude-pass-number=val -- exclude the pass number when dumping MIR (used in tests) (default: no)
     -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)
     -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.
-    -Z                         dump-mono-stats=val -- output statistics about monomorphization collection (format: markdown)
+    -Z                         dump-mono-stats=val -- output statistics about monomorphization collection
+    -Z                  dump-mono-stats-format=val -- the format to use for -Z dump-mono-stats (`markdown` (default) or `json`)
     -Z                           dwarf-version=val -- version of DWARF debug information to emit (default: 2 or 4, depending on platform)
     -Z                               dylib-lto=val -- enables LTO for dylib crate type
     -Z                        emit-stack-sizes=val -- emit a section containing stack size metadata (default: no)