Skip to content

Commit 1e14229

Browse files
committed
Create a span for each executed step
1 parent c846f7c commit 1e14229

File tree

3 files changed

+108
-28
lines changed

3 files changed

+108
-28
lines changed

src/bootstrap/src/bin/main.rs

Lines changed: 75 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -268,6 +268,7 @@ fn setup_tracing(profiling_enabled: bool) -> Option<TracingGuard> {
268268
use std::io::BufWriter;
269269
use std::sync::atomic::{AtomicU32, Ordering};
270270

271+
use bootstrap::STEP_NAME_TARGET;
271272
use chrono::{DateTime, Utc};
272273
use tracing::field::{Field, Visit};
273274
use tracing::{Event, Id, Level, Subscriber};
@@ -277,18 +278,40 @@ fn setup_tracing(profiling_enabled: bool) -> Option<TracingGuard> {
277278

278279
let filter = EnvFilter::from_env("BOOTSTRAP_TRACING");
279280

281+
/// Visitor that extracts both known and unknown field values from events and spans.
280282
#[derive(Default)]
281283
struct FieldValues {
284+
/// Main event message
282285
message: Option<String>,
286+
/// Name of a recorded psna
287+
step_name: Option<String>,
288+
/// The rest of arbitrary event/span fields
283289
fields: Vec<(&'static str, String)>,
284290
}
285291

286292
impl Visit for FieldValues {
293+
/// Record fields if possible using `record_str`, to avoid rendering simple strings with
294+
/// their `Debug` representation, which adds extra quotes.
295+
fn record_str(&mut self, field: &Field, value: &str) {
296+
match field.name() {
297+
"step_name" => {
298+
self.step_name = Some(value.to_string());
299+
}
300+
name => {
301+
self.fields.push((name, value.to_string()));
302+
}
303+
}
304+
}
305+
287306
fn record_debug(&mut self, field: &Field, value: &dyn Debug) {
288-
if field.name() == "message" {
289-
self.message = Some(format!("{value:?}"));
290-
} else {
291-
self.fields.push((field.name(), format!("{value:?}")));
307+
let formatted = format!("{value:?}");
308+
match field.name() {
309+
"message" => {
310+
self.message = Some(formatted);
311+
}
312+
name => {
313+
self.fields.push((name, formatted));
314+
}
292315
}
293316
}
294317
}
@@ -298,6 +321,9 @@ fn setup_tracing(profiling_enabled: bool) -> Option<TracingGuard> {
298321
Enter,
299322
}
300323

324+
/// Holds the name of a step, stored in `tracing_subscriber`'s extensions.
325+
struct StepNameExtension(String);
326+
301327
#[derive(Default)]
302328
struct TracingPrinter {
303329
indent: AtomicU32,
@@ -369,17 +395,31 @@ fn setup_tracing(profiling_enabled: bool) -> Option<TracingGuard> {
369395
}
370396
}
371397

372-
write!(writer, "{}", span.name())?;
373-
if let Some(values) = field_values.filter(|v| !v.fields.is_empty()) {
374-
write!(writer, " [")?;
375-
for (index, (name, value)) in values.fields.iter().enumerate() {
376-
write!(writer, "{name} = {value}")?;
377-
if index < values.fields.len() - 1 {
378-
write!(writer, ", ")?;
398+
// We handle steps specially. We instrument them dynamically in `Builder::ensure`,
399+
// and we want to have custom name for each step span. But tracing doesn't allow setting
400+
// dynamic span names. So we detect step spans here and override their name.
401+
if span.metadata().target() == STEP_NAME_TARGET {
402+
let name = field_values.and_then(|v| v.step_name.as_deref()).unwrap_or(span.name());
403+
write!(writer, "{name}")?;
404+
405+
// There should be only one more field called `args`
406+
if let Some(values) = field_values {
407+
let field = &values.fields[0];
408+
write!(writer, " {{{}}}", field.1)?;
409+
}
410+
} else {
411+
write!(writer, "{}", span.name())?;
412+
if let Some(values) = field_values.filter(|v| !v.fields.is_empty()) {
413+
write!(writer, " [")?;
414+
for (index, (name, value)) in values.fields.iter().enumerate() {
415+
write!(writer, "{name} = {value}")?;
416+
if index < values.fields.len() - 1 {
417+
write!(writer, ", ")?;
418+
}
379419
}
420+
write!(writer, "]")?;
380421
}
381-
write!(writer, "]")?;
382-
}
422+
};
383423

384424
write_location(writer, span.metadata())?;
385425
writeln!(writer)?;
@@ -424,18 +464,18 @@ fn setup_tracing(profiling_enabled: bool) -> Option<TracingGuard> {
424464
self.write_event(&mut writer, event).unwrap();
425465
}
426466

427-
fn on_new_span(
428-
&self,
429-
attrs: &tracing::span::Attributes<'_>,
430-
id: &Id,
431-
_ctx: Context<'_, S>,
432-
) {
467+
fn on_new_span(&self, attrs: &tracing::span::Attributes<'_>, id: &Id, ctx: Context<'_, S>) {
433468
// Record value of span fields
434469
// Note that we do not implement changing values of span fields after they are created.
435470
// For that we would also need to implement the `on_record` method
436-
437471
let mut field_values = FieldValues::default();
438472
attrs.record(&mut field_values);
473+
474+
// We need to propagate the actual name of the span to the Chrome layer below, because
475+
// it cannot access field values. We do that through extensions.
476+
if let Some(step_name) = field_values.step_name.clone() {
477+
ctx.span(id).unwrap().extensions_mut().insert(StepNameExtension(step_name));
478+
}
439479
self.span_values.lock().unwrap().insert(id.clone(), field_values);
440480
}
441481

@@ -466,8 +506,21 @@ fn setup_tracing(profiling_enabled: bool) -> Option<TracingGuard> {
466506
let chrome_tracing_path = tempdir.path().join("bootstrap-trace.json");
467507
let file = BufWriter::new(File::create(&chrome_tracing_path).unwrap());
468508

469-
let chrome_layer =
470-
tracing_chrome::ChromeLayerBuilder::new().writer(file).include_args(true);
509+
let chrome_layer = tracing_chrome::ChromeLayerBuilder::new()
510+
.writer(file)
511+
.include_args(true)
512+
.name_fn(Box::new(|event_or_span| match event_or_span {
513+
tracing_chrome::EventOrSpan::Event(e) => e.metadata().name().to_string(),
514+
tracing_chrome::EventOrSpan::Span(s) => {
515+
if s.metadata().target() == STEP_NAME_TARGET
516+
&& let Some(extension) = s.extensions().get::<StepNameExtension>()
517+
{
518+
extension.0.clone()
519+
} else {
520+
s.metadata().name().to_string()
521+
}
522+
}
523+
}));
471524
let (chrome_layer, guard) = chrome_layer.build();
472525

473526
tracing::subscriber::set_global_default(registry.with(chrome_layer)).unwrap();

src/bootstrap/src/core/builder/mod.rs

Lines changed: 31 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -947,6 +947,9 @@ impl Step for Libdir {
947947
}
948948
}
949949

950+
#[cfg(feature = "tracing")]
951+
pub const STEP_NAME_TARGET: &str = "STEP";
952+
950953
impl<'a> Builder<'a> {
951954
fn get_step_descriptions(kind: Kind) -> Vec<StepDescription> {
952955
macro_rules! describe {
@@ -1709,6 +1712,20 @@ You have to build a stage1 compiler for `{}` first, and then use it to build a s
17091712
let zero = Duration::new(0, 0);
17101713
let parent = self.time_spent_on_dependencies.replace(zero);
17111714

1715+
#[cfg(feature = "tracing")]
1716+
let _span = {
1717+
// Keep the target and field names synchronized with `setup_tracing`.
1718+
let span = tracing::info_span!(
1719+
target: STEP_NAME_TARGET,
1720+
// We cannot use a dynamic name here, so instead we record the actual step name
1721+
// in the step_name field.
1722+
"step",
1723+
step_name = step_name::<S>(),
1724+
args = step_debug_args(&step)
1725+
);
1726+
span.entered()
1727+
};
1728+
17121729
let out = step.clone().run(self);
17131730
let dur = start.elapsed();
17141731
let deps = self.time_spent_on_dependencies.replace(parent + dur);
@@ -1805,15 +1822,23 @@ You have to build a stage1 compiler for `{}` first, and then use it to build a s
18051822
}
18061823
}
18071824

1808-
fn pretty_print_step<S: Step>(step: &S) -> String {
1809-
let step_dbg_repr = format!("{step:?}");
1810-
let brace_index = step_dbg_repr.find('{').unwrap_or(0);
1811-
1825+
/// Return qualified step name, e.g. `compile::Rustc`.
1826+
fn step_name<S: Step>() -> String {
18121827
// Normalize step type path to only keep the module and the type name
18131828
let path = type_name::<S>().rsplit("::").take(2).collect::<Vec<_>>();
1814-
let type_string = path.into_iter().rev().collect::<Vec<_>>().join("::");
1829+
path.into_iter().rev().collect::<Vec<_>>().join("::")
1830+
}
1831+
1832+
/// Renders `step` using its `Debug` implementation and extract the field arguments out of it.
1833+
fn step_debug_args<S: Step>(step: &S) -> String {
1834+
let step_dbg_repr = format!("{step:?}");
1835+
let brace_start = step_dbg_repr.find('{').unwrap_or(0);
1836+
let brace_end = step_dbg_repr.rfind('}').unwrap_or(step_dbg_repr.len());
1837+
step_dbg_repr[brace_start + 1..brace_end - 1].trim().to_string()
1838+
}
18151839

1816-
format!("{type_string} {}", &step_dbg_repr[brace_index..])
1840+
fn pretty_print_step<S: Step>(step: &S) -> String {
1841+
format!("{} {{ {} }}", step_name::<S>(), step_debug_args(step))
18171842
}
18181843

18191844
impl<'a> AsRef<ExecutionContext> for Builder<'a> {

src/bootstrap/src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,8 @@ mod core;
4343
mod utils;
4444

4545
pub use core::builder::PathSet;
46+
#[cfg(feature = "tracing")]
47+
pub use core::builder::STEP_NAME_TARGET;
4648
pub use core::config::flags::{Flags, Subcommand};
4749
pub use core::config::{ChangeId, Config};
4850

0 commit comments

Comments
 (0)