From 8cacc027af7a1fa90d913be4726db95341e57074 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Thu, 4 Sep 2025 09:59:47 +0200 Subject: [PATCH 1/6] Remove unused `Serialize` derive --- src/cargo/core/compiler/timings.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/cargo/core/compiler/timings.rs b/src/cargo/core/compiler/timings.rs index e198b05ac40..6b87592402e 100644 --- a/src/cargo/core/compiler/timings.rs +++ b/src/cargo/core/compiler/timings.rs @@ -191,7 +191,6 @@ 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)>), From 18915936bd3584a49326697590fa5c41e3662be5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Thu, 4 Sep 2025 10:16:38 +0200 Subject: [PATCH 2/6] Generalize section rendering in pipeline graph --- src/cargo/core/compiler/timings.js | 36 +++++++++++++++++++++--------- src/cargo/core/compiler/timings.rs | 2 +- 2 files changed, 26 insertions(+), 12 deletions(-) diff --git a/src/cargo/core/compiler/timings.js b/src/cargo/core/compiler/timings.js index 00b57e33a3b..b341e81f8fe 100644 --- a/src/cargo/core/compiler/timings.js +++ b/src/cargo/core/compiler/timings.js @@ -66,7 +66,7 @@ const BG_COLOR = getCssColor('--background'); const CANVAS_BG = getCssColor('--canvas-background'); const AXES_COLOR = getCssColor('--canvas-axes'); const GRID_COLOR = getCssColor('--canvas-grid'); -const BLOCK_COLOR = getCssColor('--canvas-block'); +const CODEGEN_COLOR = getCssColor('--canvas-codegen'); const CUSTOM_BUILD_COLOR = getCssColor('--canvas-custom-build'); const NOT_CUSTOM_BUILD_COLOR = getCssColor('--canvas-not-custom-build'); const DEP_LINE_COLOR = getCssColor('--canvas-dep-line'); @@ -134,12 +134,17 @@ function render_pipeline_graph() { let unit = units[i]; let y = i * Y_TICK_DIST + 1; let x = px_per_sec * unit.start; - let rmeta_x = null; + + const sections = []; if (unit.rmeta_time != null) { - rmeta_x = x + px_per_sec * unit.rmeta_time; + sections.push({ + name: "codegen", + start: x + px_per_sec * unit.rmeta_time, + width: (unit.duration - unit.rmeta_time) * px_per_sec + }); } let width = Math.max(px_per_sec * unit.duration, 1.0); - UNIT_COORDS[unit.i] = {x, y, width, rmeta_x}; + UNIT_COORDS[unit.i] = {x, y, width, sections}; const count = unitCount.get(unit.name) || 0; unitCount.set(unit.name, count + 1); @@ -148,7 +153,7 @@ function render_pipeline_graph() { // Draw the blocks. for (i=0; i Date: Thu, 4 Sep 2025 10:21:54 +0200 Subject: [PATCH 3/6] Render link time in pipeline graph --- src/cargo/core/compiler/timings.js | 17 ++++++++++++++++- src/cargo/core/compiler/timings.rs | 11 ++++++++++- 2 files changed, 26 insertions(+), 2 deletions(-) diff --git a/src/cargo/core/compiler/timings.js b/src/cargo/core/compiler/timings.js index b341e81f8fe..515b6fbbcfc 100644 --- a/src/cargo/core/compiler/timings.js +++ b/src/cargo/core/compiler/timings.js @@ -67,6 +67,7 @@ const CANVAS_BG = getCssColor('--canvas-background'); const AXES_COLOR = getCssColor('--canvas-axes'); const GRID_COLOR = getCssColor('--canvas-grid'); const CODEGEN_COLOR = getCssColor('--canvas-codegen'); +const LINK_COLOR = getCssColor('--canvas-link'); const CUSTOM_BUILD_COLOR = getCssColor('--canvas-custom-build'); const NOT_CUSTOM_BUILD_COLOR = getCssColor('--canvas-not-custom-build'); const DEP_LINE_COLOR = getCssColor('--canvas-dep-line'); @@ -136,7 +137,19 @@ function render_pipeline_graph() { let x = px_per_sec * unit.start; const sections = []; - if (unit.rmeta_time != null) { + if (unit.sections !== null) { + // We have access to compilation sections + for (const section of unit.sections) { + const [name, {start, end}] = section; + sections.push({ + name, + start: x + px_per_sec * start, + width: (end - start) * px_per_sec + }); + } + } + else if (unit.rmeta_time != null) { + // We only know the rmeta time sections.push({ name: "codegen", start: x + px_per_sec * unit.rmeta_time, @@ -188,6 +201,8 @@ function render_pipeline_graph() { function get_section_color(name) { if (name === "codegen") { return CODEGEN_COLOR; + } else if (name === "link") { + return LINK_COLOR; } else { // We do not know what section this is, so just use the default color return NOT_CUSTOM_BUILD_COLOR; diff --git a/src/cargo/core/compiler/timings.rs b/src/cargo/core/compiler/timings.rs index 071a5f2cad3..b3e8c7129b7 100644 --- a/src/cargo/core/compiler/timings.rs +++ b/src/cargo/core/compiler/timings.rs @@ -586,6 +586,7 @@ impl<'gctx> Timings<'gctx> { rmeta_time: Option, unlocked_units: Vec, unlocked_rmeta_units: Vec, + sections: Option>, } let round = |x: f64| (x * 100.0).round() / 100.0; let unit_data: Vec = self @@ -599,7 +600,6 @@ impl<'gctx> Timings<'gctx> { "todo" } .to_string(); - // These filter on the unlocked units because not all unlocked // units are actually "built". For example, Doctest mode units // don't actually generate artifacts. @@ -613,6 +613,13 @@ impl<'gctx> Timings<'gctx> { .iter() .filter_map(|unit| unit_map.get(unit).copied()) .collect(); + let aggregated = ut.aggregate_sections(); + let sections = match aggregated { + AggregatedSections::Sections(sections) => Some(sections), + AggregatedSections::OnlyMetadataTime { .. } + | AggregatedSections::OnlyTotalDuration => None, + }; + UnitData { i, name: ut.unit.pkg.name().to_string(), @@ -624,6 +631,7 @@ impl<'gctx> Timings<'gctx> { rmeta_time: ut.rmeta_time.map(round), unlocked_units, unlocked_rmeta_units, + sections, } }) .collect(); @@ -871,6 +879,7 @@ static HTML_TMPL: &str = r#" --canvas-axes: #303030; --canvas-grid: #e6e6e6; --canvas-codegen: #aa95e8; + --canvas-link: #95e8aa; --canvas-custom-build: #f0b165; --canvas-not-custom-build: #95cce8; --canvas-dep-line: #ddd; From 2dc90bb6b78e31b63f0d696c483034b2c8f1d674 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Thu, 4 Sep 2025 10:43:39 +0200 Subject: [PATCH 4/6] Add a function to draw a legend --- src/cargo/core/compiler/timings.js | 113 ++++++++++++++++++----------- 1 file changed, 72 insertions(+), 41 deletions(-) diff --git a/src/cargo/core/compiler/timings.js b/src/cargo/core/compiler/timings.js index 515b6fbbcfc..3231ccb51ea 100644 --- a/src/cargo/core/compiler/timings.js +++ b/src/cargo/core/compiler/timings.js @@ -197,6 +197,61 @@ function render_pipeline_graph() { ctx.restore(); } +// Draw a legend at the current position of the ctx. +// entries should be an array of objects with the following scheme: +// { +// "name": [string], +// "color": [string], +// "line": [bool] +// } +function draw_legend(ctx, width, entries) { + const entry_height = 20; + const height = entries.length * entry_height + 2; // Add a bit of margin to the bottom + + // Draw background + ctx.fillStyle = BG_COLOR; + ctx.strokeStyle = TEXT_COLOR; + ctx.lineWidth = 1; + ctx.textBaseline = 'middle'; + ctx.textAlign = 'start'; + ctx.beginPath(); + ctx.rect(0, 0, width, height); + ctx.stroke(); + ctx.fill(); + + ctx.lineWidth = 2; + + // Dimension of a block + const block_height = 15; + const block_width = 30; + + // Margin from the left edge + const x_start = 5; + // Width of the "mark" section (line/block) + const mark_width = 45; + + // Draw legend entries + let y = 10; + for (const entry of entries) { + ctx.beginPath(); + + if (entry.line) { + ctx.strokeStyle = entry.color; + ctx.moveTo(x_start, y); + ctx.lineTo(x_start + mark_width, y); + ctx.stroke(); + } else { + ctx.fillStyle = entry.color; + ctx.fillRect(x_start + (mark_width - block_width) / 2, y - (block_height / 2), block_width, block_height); + } + + ctx.fillStyle = TEXT_COLOR; + ctx.fillText(entry.name, x_start + mark_width + 4, y + 1); + + y += entry_height; + } +} + // Determine the color of a section block based on the section name. function get_section_color(name) { if (name === "codegen") { @@ -325,47 +380,23 @@ function render_timing_graph() { ctx.restore(); ctx.save(); ctx.translate(canvas_width-200, MARGIN); - // background - ctx.fillStyle = BG_COLOR; - ctx.strokeStyle = TEXT_COLOR; - ctx.lineWidth = 1; - ctx.textBaseline = 'middle' - ctx.textAlign = 'start'; - ctx.beginPath(); - ctx.rect(0, 0, 150, 82); - ctx.stroke(); - ctx.fill(); - - ctx.fillStyle = TEXT_COLOR; - ctx.beginPath(); - ctx.lineWidth = 2; - ctx.strokeStyle = 'red'; - ctx.moveTo(5, 10); - ctx.lineTo(50, 10); - ctx.stroke(); - ctx.fillText('Waiting', 54, 11); - - ctx.beginPath(); - ctx.strokeStyle = 'blue'; - ctx.moveTo(5, 30); - ctx.lineTo(50, 30); - ctx.stroke(); - ctx.fillText('Inactive', 54, 31); - - ctx.beginPath(); - ctx.strokeStyle = 'green'; - ctx.moveTo(5, 50); - ctx.lineTo(50, 50); - ctx.stroke(); - ctx.fillText('Active', 54, 51); - - ctx.beginPath(); - ctx.fillStyle = cpuFillStyle - ctx.fillRect(15, 60, 30, 15); - ctx.fill(); - ctx.fillStyle = TEXT_COLOR; - ctx.fillText('CPU Usage', 54, 71); - + draw_legend(ctx, 150, [{ + name: "Waiting", + color: "red", + line: true + }, { + name: "Inactive", + color: "blue", + line: true + }, { + name: "Active", + color: "green", + line: true + }, { + name: "CPU Usage", + color: cpuFillStyle, + line: false + }]); ctx.restore(); } From 124374846a13b11df444146a6c91268de4f78fac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Thu, 4 Sep 2025 10:48:03 +0200 Subject: [PATCH 5/6] Add legend to pipeline graph --- src/cargo/core/compiler/timings.js | 35 ++++++++++++++++++++++++++++-- 1 file changed, 33 insertions(+), 2 deletions(-) diff --git a/src/cargo/core/compiler/timings.js b/src/cargo/core/compiler/timings.js index 3231ccb51ea..03d883794a7 100644 --- a/src/cargo/core/compiler/timings.js +++ b/src/cargo/core/compiler/timings.js @@ -163,6 +163,8 @@ function render_pipeline_graph() { unitCount.set(unit.name, count + 1); } + const presentSections = new Set(); + // Draw the blocks. for (i=0; i Date: Fri, 5 Sep 2025 17:32:55 +0200 Subject: [PATCH 6/6] Add "Other" section after linking --- src/cargo/core/compiler/timings.js | 11 +++++++++++ src/cargo/core/compiler/timings.rs | 23 ++++++++++++++++++++++- 2 files changed, 33 insertions(+), 1 deletion(-) diff --git a/src/cargo/core/compiler/timings.js b/src/cargo/core/compiler/timings.js index 03d883794a7..46efae9394d 100644 --- a/src/cargo/core/compiler/timings.js +++ b/src/cargo/core/compiler/timings.js @@ -68,6 +68,8 @@ const AXES_COLOR = getCssColor('--canvas-axes'); const GRID_COLOR = getCssColor('--canvas-grid'); const CODEGEN_COLOR = getCssColor('--canvas-codegen'); const LINK_COLOR = getCssColor('--canvas-link'); +// Final leftover section after link +const OTHER_COLOR = getCssColor('--canvas-other'); const CUSTOM_BUILD_COLOR = getCssColor('--canvas-custom-build'); const NOT_CUSTOM_BUILD_COLOR = getCssColor('--canvas-not-custom-build'); const DEP_LINE_COLOR = getCssColor('--canvas-dep-line'); @@ -222,6 +224,13 @@ function render_pipeline_graph() { line: false }); } + if (presentSections.has("other")) { + legend_entries.push({ + name: "Other", + color: OTHER_COLOR, + line: false + }); + } draw_legend(ctx, 160, legend_entries); ctx.restore(); } @@ -289,6 +298,8 @@ function get_section_color(name) { return CODEGEN_COLOR; } else if (name === "link") { return LINK_COLOR; + } else if (name === "other") { + return OTHER_COLOR; } else { // We do not know what section this is, so just use the default color return NOT_CUSTOM_BUILD_COLOR; diff --git a/src/cargo/core/compiler/timings.rs b/src/cargo/core/compiler/timings.rs index b3e8c7129b7..4f8b2b82b39 100644 --- a/src/cargo/core/compiler/timings.rs +++ b/src/cargo/core/compiler/timings.rs @@ -615,7 +615,27 @@ impl<'gctx> Timings<'gctx> { .collect(); let aggregated = ut.aggregate_sections(); let sections = match aggregated { - AggregatedSections::Sections(sections) => Some(sections), + AggregatedSections::Sections(mut sections) => { + // We draw the sections in the pipeline graph in a way where the frontend + // section has the "default" build color, and then additional sections + // (codegen, link) are overlayed on top with a different color. + // However, there might be some time after the final (usually link) section, + // which definitely shouldn't be classified as "Frontend". We thus try to + // detect this situation and add a final "Other" section. + if let Some((_, section)) = sections.last() + && section.end < ut.duration + { + sections.push(( + "other".to_string(), + SectionData { + start: section.end, + end: ut.duration, + }, + )); + } + + Some(sections) + } AggregatedSections::OnlyMetadataTime { .. } | AggregatedSections::OnlyTotalDuration => None, }; @@ -880,6 +900,7 @@ static HTML_TMPL: &str = r#" --canvas-grid: #e6e6e6; --canvas-codegen: #aa95e8; --canvas-link: #95e8aa; + --canvas-other: #e895aa; --canvas-custom-build: #f0b165; --canvas-not-custom-build: #95cce8; --canvas-dep-line: #ddd;