From 199e9e7cb8da6f14f13416d364b74da1651a60c7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Sat, 9 Aug 2025 10:16:56 +0200 Subject: [PATCH] Add compilation sections to the HTML unit table --- src/cargo/core/compiler/timings.rs | 84 ++++++++++++++++++++++++++++-- 1 file changed, 80 insertions(+), 4 deletions(-) diff --git a/src/cargo/core/compiler/timings.rs b/src/cargo/core/compiler/timings.rs index a9a4ceef3..e198b05ac 100644 --- a/src/cargo/core/compiler/timings.rs +++ b/src/cargo/core/compiler/timings.rs @@ -105,7 +105,47 @@ impl UnitTime { fn aggregate_sections(&self) -> AggregatedSections { let end = self.duration; - if let Some(rmeta) = self.rmeta_time { + if !self.sections.is_empty() { + // We have some detailed compilation section timings, so we postprocess them + // Since it is possible that we do not have an end timestamp for a given compilation + // section, we need to iterate them and if an end is missing, we assign the end of + // the section to the start of the following section. + + let mut sections = vec![]; + + // The frontend section is currently implicit in rustc, it is assumed to start at + // compilation start and end when codegen starts. So we hard-code it here. + let mut previous_section = ( + FRONTEND_SECTION_NAME.to_string(), + CompilationSection { + start: 0.0, + end: None, + }, + ); + for (name, section) in self.sections.clone() { + // Store the previous section, potentially setting its end to the start of the + // current section. + sections.push(( + previous_section.0.clone(), + SectionData { + start: previous_section.1.start, + end: previous_section.1.end.unwrap_or(section.start), + }, + )); + previous_section = (name, section); + } + // Store the last section, potentially setting its end to the end of the whole + // compilation. + sections.push(( + previous_section.0.clone(), + SectionData { + start: previous_section.1.start, + end: previous_section.1.end.unwrap_or(end), + }, + )); + + AggregatedSections::Sections(sections) + } else if let Some(rmeta) = self.rmeta_time { // We only know when the rmeta time was generated AggregatedSections::OnlyMetadataTime { frontend: SectionData { @@ -153,6 +193,8 @@ impl SectionData { /// Contains post-processed data of individual compilation sections. #[derive(serde::Serialize)] enum AggregatedSections { + /// We know the names and durations of individual compilation sections + Sections(Vec<(String, SectionData)>), /// We only know when .rmeta was generated, so we can distill frontend and codegen time. OnlyMetadataTime { frontend: SectionData, @@ -609,15 +651,44 @@ impl<'gctx> Timings<'gctx> { let mut units: Vec<&UnitTime> = self.unit_times.iter().collect(); units.sort_unstable_by(|a, b| b.duration.partial_cmp(&a.duration).unwrap()); + // Make the first "letter" uppercase. We could probably just assume ASCII here, but this + // should be Unicode compatible. + fn capitalize(s: &str) -> String { + let first_char = s + .chars() + .next() + .map(|c| c.to_uppercase().to_string()) + .unwrap_or_default(); + format!("{first_char}{}", s.chars().skip(1).collect::()) + } + // We can have a bunch of situations here. // - -Zsection-timings is enabled, and we received some custom sections, in which // case we use them to determine the headers. // - We have at least one rmeta time, so we hard-code Frontend and Codegen headers. // - We only have total durations, so we don't add any additional headers. - let aggregated: Vec = - units.iter().map(|u| u.aggregate_sections()).collect(); + let aggregated: Vec = units + .iter() + .map(|u| + // Normalize the section names so that they are capitalized, so that we can later + // refer to them with the capitalized name both when computing headers and when + // looking up cells. + match u.aggregate_sections() { + AggregatedSections::Sections(sections) => AggregatedSections::Sections( + sections.into_iter() + .map(|(name, data)| (capitalize(&name), data)) + .collect() + ), + s => s + }) + .collect(); - let headers: Vec = if aggregated + let headers: Vec = if let Some(sections) = aggregated.iter().find_map(|s| match s { + AggregatedSections::Sections(sections) => Some(sections), + _ => None, + }) { + sections.into_iter().map(|s| s.0.clone()).collect() + } else if aggregated .iter() .any(|s| matches!(s, AggregatedSections::OnlyMetadataTime { .. })) { @@ -664,6 +735,11 @@ impl<'gctx> Timings<'gctx> { let mut cells: HashMap<&str, SectionData> = Default::default(); match &aggregated_sections { + AggregatedSections::Sections(sections) => { + for (name, data) in sections { + cells.insert(&name, *data); + } + } AggregatedSections::OnlyMetadataTime { frontend, codegen } => { cells.insert(FRONTEND_SECTION_NAME, *frontend); cells.insert(CODEGEN_SECTION_NAME, *codegen);