about summary refs log tree commit diff
diff options
context:
space:
mode:
authorJakub Beránek <berykubik@gmail.com>2025-03-04 12:29:51 +0100
committerJakub Beránek <berykubik@gmail.com>2025-03-04 12:30:41 +0100
commitead58ea6f8bf860459fe5351a83717a8e591b0af (patch)
treef0a96ba960edb285fe6e86a55accb3cfa4d5ca15
parent2e5ab4e6a07a9ebdb6e593ca19112148250e6906 (diff)
downloadrust-ead58ea6f8bf860459fe5351a83717a8e591b0af.tar.gz
rust-ead58ea6f8bf860459fe5351a83717a8e591b0af.zip
Move `BuildStep` and metric logging into `build_helper`
-rw-r--r--src/build_helper/src/metrics.rs80
-rw-r--r--src/tools/opt-dist/src/metrics.rs73
2 files changed, 83 insertions, 70 deletions
diff --git a/src/build_helper/src/metrics.rs b/src/build_helper/src/metrics.rs
index c9be63ed534..4b585c15182 100644
--- a/src/build_helper/src/metrics.rs
+++ b/src/build_helper/src/metrics.rs
@@ -1,3 +1,5 @@
+use std::time::Duration;
+
 use serde_derive::{Deserialize, Serialize};
 
 #[derive(Serialize, Deserialize)]
@@ -100,3 +102,81 @@ fn null_as_f64_nan<'de, D: serde::Deserializer<'de>>(d: D) -> Result<f64, D::Err
     use serde::Deserialize as _;
     Option::<f64>::deserialize(d).map(|f| f.unwrap_or(f64::NAN))
 }
+
+/// Represents a single bootstrap step, with the accumulated duration of all its children.
+#[derive(Clone, Debug)]
+pub struct BuildStep {
+    pub r#type: String,
+    pub children: Vec<BuildStep>,
+    pub duration: Duration,
+}
+
+impl BuildStep {
+    /// Create a `BuildStep` representing a single invocation of bootstrap.
+    /// The most important thing is that the build step aggregates the
+    /// durations of all children, so that it can be easily accessed.
+    pub fn from_invocation(invocation: &JsonInvocation) -> Self {
+        fn parse(node: &JsonNode) -> Option<BuildStep> {
+            match node {
+                JsonNode::RustbuildStep {
+                    type_: kind,
+                    children,
+                    duration_excluding_children_sec,
+                    ..
+                } => {
+                    let children: Vec<_> = children.into_iter().filter_map(parse).collect();
+                    let children_duration = children.iter().map(|c| c.duration).sum::<Duration>();
+                    Some(BuildStep {
+                        r#type: kind.to_string(),
+                        children,
+                        duration: children_duration
+                            + Duration::from_secs_f64(*duration_excluding_children_sec),
+                    })
+                }
+                JsonNode::TestSuite(_) => None,
+            }
+        }
+
+        let duration = Duration::from_secs_f64(invocation.duration_including_children_sec);
+        let children: Vec<_> = invocation.children.iter().filter_map(parse).collect();
+        Self { r#type: "total".to_string(), children, duration }
+    }
+
+    pub fn find_all_by_type(&self, r#type: &str) -> Vec<&Self> {
+        let mut result = Vec::new();
+        self.find_by_type(r#type, &mut result);
+        result
+    }
+
+    fn find_by_type<'a>(&'a self, r#type: &str, result: &mut Vec<&'a Self>) {
+        if self.r#type == r#type {
+            result.push(self);
+        }
+        for child in &self.children {
+            child.find_by_type(r#type, result);
+        }
+    }
+}
+
+/// Writes build steps into a nice indented table.
+pub fn format_build_steps(root: &BuildStep) -> String {
+    use std::fmt::Write;
+
+    let mut substeps: Vec<(u32, &BuildStep)> = Vec::new();
+
+    fn visit<'a>(step: &'a BuildStep, level: u32, substeps: &mut Vec<(u32, &'a BuildStep)>) {
+        substeps.push((level, step));
+        for child in &step.children {
+            visit(child, level + 1, substeps);
+        }
+    }
+
+    visit(root, 0, &mut substeps);
+
+    let mut output = String::new();
+    for (level, step) in substeps {
+        let label = format!("{}{}", ".".repeat(level as usize), step.r#type);
+        writeln!(output, "{label:<65}{:>8.2}s", step.duration.as_secs_f64()).unwrap();
+    }
+    output
+}
diff --git a/src/tools/opt-dist/src/metrics.rs b/src/tools/opt-dist/src/metrics.rs
index 89c4cb12d4c..0a745566eb5 100644
--- a/src/tools/opt-dist/src/metrics.rs
+++ b/src/tools/opt-dist/src/metrics.rs
@@ -1,33 +1,10 @@
 use std::time::Duration;
 
-use build_helper::metrics::{JsonNode, JsonRoot};
+use build_helper::metrics::{BuildStep, JsonRoot, format_build_steps};
 use camino::Utf8Path;
 
 use crate::timer::TimerSection;
 
-#[derive(Clone, Debug)]
-pub struct BuildStep {
-    r#type: String,
-    children: Vec<BuildStep>,
-    duration: Duration,
-}
-
-impl BuildStep {
-    pub fn find_all_by_type(&self, r#type: &str) -> Vec<&BuildStep> {
-        let mut result = Vec::new();
-        self.find_by_type(r#type, &mut result);
-        result
-    }
-    fn find_by_type<'a>(&'a self, r#type: &str, result: &mut Vec<&'a BuildStep>) {
-        if self.r#type == r#type {
-            result.push(self);
-        }
-        for child in &self.children {
-            child.find_by_type(r#type, result);
-        }
-    }
-}
-
 /// Loads the metrics of the most recent bootstrap execution from a metrics.json file.
 pub fn load_metrics(path: &Utf8Path) -> anyhow::Result<BuildStep> {
     let content = std::fs::read(path.as_std_path())?;
@@ -37,30 +14,7 @@ pub fn load_metrics(path: &Utf8Path) -> anyhow::Result<BuildStep> {
         .pop()
         .ok_or_else(|| anyhow::anyhow!("No bootstrap invocation found in metrics file"))?;
 
-    fn parse(node: JsonNode) -> Option<BuildStep> {
-        match node {
-            JsonNode::RustbuildStep {
-                type_: kind,
-                children,
-                duration_excluding_children_sec,
-                ..
-            } => {
-                let children: Vec<_> = children.into_iter().filter_map(parse).collect();
-                let children_duration = children.iter().map(|c| c.duration).sum::<Duration>();
-                Some(BuildStep {
-                    r#type: kind.to_string(),
-                    children,
-                    duration: children_duration
-                        + Duration::from_secs_f64(duration_excluding_children_sec),
-                })
-            }
-            JsonNode::TestSuite(_) => None,
-        }
-    }
-
-    let duration = Duration::from_secs_f64(invocation.duration_including_children_sec);
-    let children: Vec<_> = invocation.children.into_iter().filter_map(parse).collect();
-    Ok(BuildStep { r#type: "root".to_string(), children, duration })
+    Ok(BuildStep::from_invocation(&invocation))
 }
 
 /// Logs the individual metrics in a table and add Rustc and LLVM durations to the passed
@@ -82,27 +36,6 @@ pub fn record_metrics(metrics: &BuildStep, timer: &mut TimerSection) {
         timer.add_duration("Rustc", rustc_duration);
     }
 
-    log_metrics(metrics);
-}
-
-fn log_metrics(metrics: &BuildStep) {
-    use std::fmt::Write;
-
-    let mut substeps: Vec<(u32, &BuildStep)> = Vec::new();
-
-    fn visit<'a>(step: &'a BuildStep, level: u32, substeps: &mut Vec<(u32, &'a BuildStep)>) {
-        substeps.push((level, step));
-        for child in &step.children {
-            visit(child, level + 1, substeps);
-        }
-    }
-
-    visit(metrics, 0, &mut substeps);
-
-    let mut output = String::new();
-    for (level, step) in substeps {
-        let label = format!("{}{}", ".".repeat(level as usize), step.r#type);
-        writeln!(output, "{label:<65}{:>8.2}s", step.duration.as_secs_f64()).unwrap();
-    }
+    let output = format_build_steps(metrics);
     log::info!("Build step durations\n{output}");
 }