11//! Render HTML report from timing tracking data.
22
3+ use std:: borrow:: Cow ;
34use std:: collections:: HashMap ;
45use std:: io:: Write ;
56use std:: time:: Instant ;
@@ -14,13 +15,10 @@ use super::Concurrency;
1415use super :: UnitData ;
1516use super :: UnitTime ;
1617
17- const FRONTEND_SECTION_NAME : & str = "Frontend" ;
18- const CODEGEN_SECTION_NAME : & str = "Codegen" ;
19-
2018/// Contains post-processed data of individual compilation sections.
2119enum AggregatedSections {
2220 /// We know the names and durations of individual compilation sections
23- Sections ( Vec < ( String , SectionData ) > ) ,
21+ Sections ( Vec < ( SectionName , SectionData ) > ) ,
2422 /// We only know when .rmeta was generated, so we can distill frontend and codegen time.
2523 OnlyMetadataTime {
2624 frontend : SectionData ,
@@ -30,6 +28,50 @@ enum AggregatedSections {
3028 OnlyTotalDuration ,
3129}
3230
31+ /// Name of an individual compilation section.
32+ #[ derive( Clone , Hash , Eq , PartialEq ) ]
33+ pub ( super ) enum SectionName {
34+ Frontend ,
35+ Codegen ,
36+ Named ( String ) ,
37+ Other ,
38+ }
39+
40+ impl SectionName {
41+ /// Lower case name.
42+ fn name ( & self ) -> Cow < ' static , str > {
43+ match self {
44+ SectionName :: Frontend => "frontend" . into ( ) ,
45+ SectionName :: Codegen => "codegen" . into ( ) ,
46+ SectionName :: Named ( n) => n. to_lowercase ( ) . into ( ) ,
47+ SectionName :: Other => "other" . into ( ) ,
48+ }
49+ }
50+
51+ fn capitalized_name ( & self ) -> String {
52+ // Make the first "letter" uppercase. We could probably just assume ASCII here, but this
53+ // should be Unicode compatible.
54+ fn capitalize ( s : & str ) -> String {
55+ let first_char = s
56+ . chars ( )
57+ . next ( )
58+ . map ( |c| c. to_uppercase ( ) . to_string ( ) )
59+ . unwrap_or_default ( ) ;
60+ format ! ( "{first_char}{}" , s. chars( ) . skip( 1 ) . collect:: <String >( ) )
61+ }
62+ capitalize ( & self . name ( ) )
63+ }
64+ }
65+
66+ impl serde:: ser:: Serialize for SectionName {
67+ fn serialize < S > ( & self , serializer : S ) -> Result < S :: Ok , S :: Error >
68+ where
69+ S : serde:: Serializer ,
70+ {
71+ self . name ( ) . serialize ( serializer)
72+ }
73+ }
74+
3375/// Postprocessed section data that has both start and an end.
3476#[ derive( Copy , Clone , serde:: Serialize ) ]
3577pub ( super ) struct SectionData {
@@ -225,51 +267,23 @@ fn write_unit_table(ctx: &RenderContext<'_>, f: &mut impl Write) -> CargoResult<
225267 let mut units: Vec < & UnitTime > = ctx. unit_times . iter ( ) . collect ( ) ;
226268 units. sort_unstable_by ( |a, b| b. duration . partial_cmp ( & a. duration ) . unwrap ( ) ) ;
227269
228- // Make the first "letter" uppercase. We could probably just assume ASCII here, but this
229- // should be Unicode compatible.
230- fn capitalize ( s : & str ) -> String {
231- let first_char = s
232- . chars ( )
233- . next ( )
234- . map ( |c| c. to_uppercase ( ) . to_string ( ) )
235- . unwrap_or_default ( ) ;
236- format ! ( "{first_char}{}" , s. chars( ) . skip( 1 ) . collect:: <String >( ) )
237- }
238-
239270 // We can have a bunch of situations here.
240271 // - -Zsection-timings is enabled, and we received some custom sections, in which
241272 // case we use them to determine the headers.
242273 // - We have at least one rmeta time, so we hard-code Frontend and Codegen headers.
243274 // - We only have total durations, so we don't add any additional headers.
244- let aggregated: Vec < AggregatedSections > = units
245- . iter ( )
246- . map ( |u|
247- // Normalize the section names so that they are capitalized, so that we can later
248- // refer to them with the capitalized name both when computing headers and when
249- // looking up cells.
250- match aggregate_sections ( u) {
251- AggregatedSections :: Sections ( sections) => AggregatedSections :: Sections (
252- sections. into_iter ( )
253- . map ( |( name, data) | ( capitalize ( & name) , data) )
254- . collect ( )
255- ) ,
256- s => s
257- } )
258- . collect ( ) ;
275+ let aggregated: Vec < AggregatedSections > = units. iter ( ) . map ( |u| aggregate_sections ( u) ) . collect ( ) ;
259276
260- let headers: Vec < String > = if let Some ( sections) = aggregated. iter ( ) . find_map ( |s| match s {
277+ let headers: Vec < _ > = if let Some ( sections) = aggregated. iter ( ) . find_map ( |s| match s {
261278 AggregatedSections :: Sections ( sections) => Some ( sections) ,
262279 _ => None ,
263280 } ) {
264- sections. into_iter ( ) . map ( |s| s. 0 . clone ( ) ) . collect ( )
281+ sections. iter ( ) . map ( |s| s. 0 . clone ( ) ) . collect ( )
265282 } else if aggregated
266283 . iter ( )
267284 . any ( |s| matches ! ( s, AggregatedSections :: OnlyMetadataTime { .. } ) )
268285 {
269- vec ! [
270- FRONTEND_SECTION_NAME . to_string( ) ,
271- CODEGEN_SECTION_NAME . to_string( ) ,
272- ]
286+ vec ! [ SectionName :: Frontend , SectionName :: Codegen ]
273287 } else {
274288 vec ! [ ]
275289 } ;
@@ -289,11 +303,14 @@ fn write_unit_table(ctx: &RenderContext<'_>, f: &mut impl Write) -> CargoResult<
289303</thead>
290304<tbody>
291305"# ,
292- headers = headers. iter( ) . map( |h| format!( "<th>{h}</th>" ) ) . join( "\n " )
306+ headers = headers
307+ . iter( )
308+ . map( |h| format!( "<th>{}</th>" , h. capitalized_name( ) ) )
309+ . join( "\n " )
293310 ) ?;
294311
295312 for ( i, ( unit, aggregated_sections) ) in units. iter ( ) . zip ( aggregated) . enumerate ( ) {
296- let format_duration = |section : Option < SectionData > | match section {
313+ let format_duration = |section : Option < & SectionData > | match section {
297314 Some ( section) => {
298315 let duration = section. duration ( ) ;
299316 let pct = ( duration / unit. duration ) * 100.0 ;
@@ -306,28 +323,23 @@ fn write_unit_table(ctx: &RenderContext<'_>, f: &mut impl Write) -> CargoResult<
306323 // arbitrary set of headers, and an arbitrary set of sections per unit, so we always
307324 // initiate the cells to be empty, and then try to find a corresponding column for which
308325 // we might have data.
309- let mut cells: HashMap < & str , SectionData > = Default :: default ( ) ;
326+ let mut cells = HashMap :: new ( ) ;
310327
311328 match & aggregated_sections {
312329 AggregatedSections :: Sections ( sections) => {
313330 for ( name, data) in sections {
314- cells. insert ( & name, * data) ;
331+ cells. insert ( name, data) ;
315332 }
316333 }
317334 AggregatedSections :: OnlyMetadataTime { frontend, codegen } => {
318- cells. insert ( FRONTEND_SECTION_NAME , * frontend) ;
319- cells. insert ( CODEGEN_SECTION_NAME , * codegen) ;
335+ cells. insert ( & SectionName :: Frontend , frontend) ;
336+ cells. insert ( & SectionName :: Codegen , codegen) ;
320337 }
321338 AggregatedSections :: OnlyTotalDuration => { }
322339 } ;
323340 let cells = headers
324341 . iter ( )
325- . map ( |header| {
326- format ! (
327- "<td>{}</td>" ,
328- format_duration( cells. remove( header. as_str( ) ) )
329- )
330- } )
342+ . map ( |header| format ! ( "<td>{}</td>" , format_duration( cells. remove( header) ) ) )
331343 . join ( "\n " ) ;
332344
333345 let features = unit. unit . features . join ( ", " ) ;
@@ -396,7 +408,7 @@ fn to_unit_data(unit_times: &[UnitTime]) -> Vec<UnitData> {
396408 && section. end < ut. duration
397409 {
398410 sections. push ( (
399- "other" . to_string ( ) ,
411+ SectionName :: Other ,
400412 SectionData {
401413 start : section. end ,
402414 end : ut. duration ,
@@ -442,7 +454,7 @@ fn aggregate_sections(unit_time: &UnitTime) -> AggregatedSections {
442454 // The frontend section is currently implicit in rustc, it is assumed to start at
443455 // compilation start and end when codegen starts. So we hard-code it here.
444456 let mut previous_section = (
445- FRONTEND_SECTION_NAME . to_string ( ) ,
457+ SectionName :: Frontend ,
446458 CompilationSection {
447459 start : 0.0 ,
448460 end : None ,
@@ -452,18 +464,18 @@ fn aggregate_sections(unit_time: &UnitTime) -> AggregatedSections {
452464 // Store the previous section, potentially setting its end to the start of the
453465 // current section.
454466 sections. push ( (
455- previous_section. 0 . clone ( ) ,
467+ previous_section. 0 ,
456468 SectionData {
457469 start : previous_section. 1 . start ,
458470 end : previous_section. 1 . end . unwrap_or ( section. start ) ,
459471 } ,
460472 ) ) ;
461- previous_section = ( name, section) ;
473+ previous_section = ( SectionName :: Named ( name) , section) ;
462474 }
463475 // Store the last section, potentially setting its end to the end of the whole
464476 // compilation.
465477 sections. push ( (
466- previous_section. 0 . clone ( ) ,
478+ previous_section. 0 ,
467479 SectionData {
468480 start : previous_section. 1 . start ,
469481 end : previous_section. 1 . end . unwrap_or ( end) ,
0 commit comments