Skip to content

Commit fe8a052

Browse files
committed
Parse and store section timing events
1 parent 2736a5d commit fe8a052

File tree

4 files changed

+89
-1
lines changed

4 files changed

+89
-1
lines changed

src/cargo/core/compiler/job_queue/job_state.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ use cargo_util::ProcessBuilder;
77
use crate::CargoResult;
88
use crate::core::compiler::build_runner::OutputFile;
99
use crate::core::compiler::future_incompat::FutureBreakageItem;
10+
use crate::core::compiler::timings::SectionTiming;
1011
use crate::util::Queue;
1112

1213
use super::{Artifact, DiagDedupe, Job, JobId, Message};
@@ -143,6 +144,10 @@ impl<'a, 'gctx> JobState<'a, 'gctx> {
143144
.push(Message::Finish(self.id, Artifact::Metadata, Ok(())));
144145
}
145146

147+
pub fn on_section_timing_emitted(&self, section: SectionTiming) {
148+
self.messages.push(Message::SectionTiming(self.id, section));
149+
}
150+
146151
/// Drives a [`Job`] to finish. This ensures that a [`Message::Finish`] is
147152
/// sent even if our job panics.
148153
pub(super) fn run_to_finish(self, job: Job) {

src/cargo/core/compiler/job_queue/mod.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -133,7 +133,7 @@ pub use self::job::{Job, Work};
133133
pub use self::job_state::JobState;
134134
use super::build_runner::OutputFile;
135135
use super::custom_build::Severity;
136-
use super::timings::Timings;
136+
use super::timings::{SectionTiming, Timings};
137137
use super::{BuildContext, BuildPlan, BuildRunner, CompileMode, Unit};
138138
use crate::core::compiler::descriptive_pkg_name;
139139
use crate::core::compiler::future_incompat::{
@@ -374,6 +374,7 @@ enum Message {
374374
Token(io::Result<Acquired>),
375375
Finish(JobId, Artifact, CargoResult<()>),
376376
FutureIncompatReport(JobId, Vec<FutureBreakageItem>),
377+
SectionTiming(JobId, SectionTiming),
377378
}
378379

379380
impl<'gctx> JobQueue<'gctx> {
@@ -714,6 +715,9 @@ impl<'gctx> DrainState<'gctx> {
714715
let token = acquired_token.context("failed to acquire jobserver token")?;
715716
self.tokens.push(token);
716717
}
718+
Message::SectionTiming(id, section) => {
719+
self.timings.unit_section_timing(id, &section);
720+
}
717721
}
718722

719723
Ok(())

src/cargo/core/compiler/mod.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,7 @@ use self::output_depinfo::output_depinfo;
9090
use self::output_sbom::build_sbom;
9191
use self::unit_graph::UnitDep;
9292
use crate::core::compiler::future_incompat::FutureIncompatReport;
93+
use crate::core::compiler::timings::SectionTiming;
9394
pub use crate::core::compiler::unit::{Unit, UnitInterner};
9495
use crate::core::manifest::TargetSourcePath;
9596
use crate::core::profiles::{PanicStrategy, Profile, StripInner};
@@ -1968,6 +1969,12 @@ fn on_stderr_line_inner(
19681969
return Ok(true);
19691970
}
19701971

1972+
let res = serde_json::from_str::<SectionTiming>(compiler_message.get());
1973+
if let Ok(timing_record) = res {
1974+
state.on_section_timing_emitted(timing_record);
1975+
return Ok(false);
1976+
}
1977+
19711978
// Depending on what we're emitting from Cargo itself, we figure out what to
19721979
// do with this JSON message.
19731980
match options.format {

src/cargo/core/compiler/timings.rs

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ use std::collections::HashMap;
1616
use std::io::{BufWriter, Write};
1717
use std::thread::available_parallelism;
1818
use std::time::{Duration, Instant};
19+
use tracing::warn;
1920

2021
/// Tracking information for the entire build.
2122
///
@@ -63,6 +64,14 @@ pub struct Timings<'gctx> {
6364
cpu_usage: Vec<(f64, f64)>,
6465
}
6566

67+
/// Section of compilation.
68+
struct TimingSection {
69+
/// Start of the section, as an offset in seconds from `UnitTime::start`.
70+
start: f64,
71+
/// End of the section, as an offset in seconds from `UnitTime::start`.
72+
end: Option<f64>,
73+
}
74+
6675
/// Tracking information for an individual unit.
6776
struct UnitTime {
6877
unit: Unit,
@@ -79,6 +88,8 @@ struct UnitTime {
7988
unlocked_units: Vec<Unit>,
8089
/// Same as `unlocked_units`, but unlocked by rmeta.
8190
unlocked_rmeta_units: Vec<Unit>,
91+
/// Individual compilation section durations, gathered from `--json=timings`.
92+
sections: HashMap<String, TimingSection>,
8293
}
8394

8495
/// Periodic concurrency tracking information.
@@ -181,6 +192,7 @@ impl<'gctx> Timings<'gctx> {
181192
rmeta_time: None,
182193
unlocked_units: Vec::new(),
183194
unlocked_rmeta_units: Vec::new(),
195+
sections: Default::default(),
184196
};
185197
assert!(self.active.insert(id, unit_time).is_none());
186198
}
@@ -233,6 +245,26 @@ impl<'gctx> Timings<'gctx> {
233245
self.unit_times.push(unit_time);
234246
}
235247

248+
/// Handle the start/end of a compilation section.
249+
pub fn unit_section_timing(&mut self, id: JobId, section_timing: &SectionTiming) {
250+
if !self.enabled {
251+
return;
252+
}
253+
let Some(unit_time) = self.active.get_mut(&id) else {
254+
return;
255+
};
256+
let now = self.start.elapsed().as_secs_f64();
257+
258+
match section_timing.event {
259+
SectionTimingEvent::Start => {
260+
unit_time.start_section(&section_timing.name, now);
261+
}
262+
SectionTimingEvent::End => {
263+
unit_time.end_section(&section_timing.name, now);
264+
}
265+
}
266+
}
267+
236268
/// This is called periodically to mark the concurrency of internal structures.
237269
pub fn mark_concurrency(&mut self, active: usize, waiting: usize, inactive: usize) {
238270
if !self.enabled {
@@ -578,6 +610,46 @@ impl UnitTime {
578610
fn name_ver(&self) -> String {
579611
format!("{} v{}", self.unit.pkg.name(), self.unit.pkg.version())
580612
}
613+
614+
fn start_section(&mut self, name: &str, now: f64) {
615+
if self
616+
.sections
617+
.insert(
618+
name.to_string(),
619+
TimingSection {
620+
start: now - self.start,
621+
end: None,
622+
},
623+
)
624+
.is_some()
625+
{
626+
warn!("Compilation section {name} started more than once");
627+
}
628+
}
629+
630+
fn end_section(&mut self, name: &str, now: f64) {
631+
let Some(section) = self.sections.get_mut(name) else {
632+
warn!("Compilation section {name} ended, but it has no start recorded");
633+
return;
634+
};
635+
section.end = Some(now - self.start);
636+
}
637+
}
638+
639+
/// Start or end of a section timing.
640+
#[derive(serde::Deserialize, Debug)]
641+
#[serde(rename_all = "kebab-case")]
642+
pub enum SectionTimingEvent {
643+
Start,
644+
End,
645+
}
646+
647+
/// Represents a certain section (phase) of rustc compilation.
648+
/// It is emitted by rustc when the `--json=timings` flag is used.
649+
#[derive(serde::Deserialize, Debug)]
650+
pub struct SectionTiming {
651+
pub name: String,
652+
pub event: SectionTimingEvent,
581653
}
582654

583655
fn render_rustc_info(bcx: &BuildContext<'_, '_>) -> String {

0 commit comments

Comments
 (0)