blob: cabe07eda329fb69326ac58f4ba17d4d91ccaf21 [file] [log] [blame]
use crate::timer::TimerSection;
use build_helper::metrics::{JsonNode, JsonRoot};
use camino::Utf8Path;
use std::time::Duration;
#[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())?;
let mut metrics = serde_json::from_slice::<JsonRoot>(&content)?;
let invocation = metrics
.invocations
.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 })
}
/// Logs the individual metrics in a table and add Rustc and LLVM durations to the passed
/// timer.
pub fn record_metrics(metrics: &BuildStep, timer: &mut TimerSection) {
let llvm_steps = metrics.find_all_by_type("bootstrap::llvm::Llvm");
let llvm_duration: Duration = llvm_steps.into_iter().map(|s| s.duration).sum();
let rustc_steps = metrics.find_all_by_type("bootstrap::compile::Rustc");
let rustc_duration: Duration = rustc_steps.into_iter().map(|s| s.duration).sum();
// The LLVM step is part of the Rustc step
let rustc_duration = rustc_duration.saturating_sub(llvm_duration);
if !llvm_duration.is_zero() {
timer.add_duration("LLVM", llvm_duration);
}
if !rustc_duration.is_zero() {
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();
}
log::info!("Build step durations\n{output}");
}