1+ use std:: fmt:: { Display , Formatter } ;
2+
13use crate :: core:: build_steps:: compile:: { Std , Sysroot } ;
2- use crate :: core:: build_steps:: tool:: { RustcPerf , Tool } ;
4+ use crate :: core:: build_steps:: tool:: RustcPerf ;
35use crate :: core:: builder:: Builder ;
46use crate :: core:: config:: DebuginfoLevel ;
7+ use crate :: utils:: exec:: { BootstrapCommand , command} ;
8+
9+ #[ derive( Debug , Clone , clap:: Parser ) ]
10+ pub struct PerfArgs {
11+ #[ clap( subcommand) ]
12+ cmd : PerfCommand ,
13+ }
14+
15+ #[ derive( Debug , Clone , clap:: Parser ) ]
16+ enum PerfCommand {
17+ /// Run `profile_local eprintln`.
18+ /// This executes the compiler on the given benchmarks and stores its stderr output.
19+ Eprintln {
20+ #[ clap( flatten) ]
21+ opts : SharedOpts ,
22+ } ,
23+ /// Run `profile_local samply`
24+ /// This executes the compiler on the given benchmarks and profiles it with `samply`.
25+ /// You need to install `samply`, e.g. using `cargo install samply`.
26+ Samply {
27+ #[ clap( flatten) ]
28+ opts : SharedOpts ,
29+ } ,
30+ /// Run `profile_local cachegrind`.
31+ /// This executes the compiler on the given benchmarks under `Cachegrind`.
32+ Cachegrind {
33+ #[ clap( flatten) ]
34+ opts : SharedOpts ,
35+ } ,
36+ /// Run compile benchmarks with a locally built compiler.
37+ Benchmark {
38+ /// Identifier to associate benchmark results with
39+ #[ clap( name = "benchmark-id" ) ]
40+ id : String ,
41+
42+ #[ clap( flatten) ]
43+ opts : SharedOpts ,
44+ } ,
45+ /// Compare the results of two previously executed benchmark runs.
46+ Compare {
47+ /// The name of the base artifact to be compared.
48+ base : String ,
49+
50+ /// The name of the modified artifact to be compared.
51+ modified : String ,
52+ } ,
53+ }
54+
55+ #[ derive( Debug , Clone , clap:: Parser ) ]
56+ struct SharedOpts {
57+ /// Select the benchmarks that you want to run (separated by commas).
58+ /// If unspecified, all benchmarks will be executed.
59+ #[ clap( long, global = true , value_delimiter = ',' ) ]
60+ include : Vec < String > ,
61+
62+ /// Select the benchmarks matching a prefix in this comma-separated list that you don't want to run.
63+ #[ clap( long, global = true , value_delimiter = ',' ) ]
64+ exclude : Vec < String > ,
65+
66+ /// Select the scenarios that should be benchmarked.
67+ #[ clap(
68+ long,
69+ global = true ,
70+ value_delimiter = ',' ,
71+ default_value = "Full,IncrFull,IncrUnchanged,IncrPatched"
72+ ) ]
73+ scenarios : Vec < Scenario > ,
74+ /// Select the profiles that should be benchmarked.
75+ #[ clap( long, global = true , value_delimiter = ',' , default_value = "Check,Debug,Opt" ) ]
76+ profiles : Vec < Profile > ,
77+ }
78+
79+ #[ derive( Clone , Copy , Debug , clap:: ValueEnum ) ]
80+ #[ value( rename_all = "PascalCase" ) ]
81+ pub enum Profile {
82+ Check ,
83+ Debug ,
84+ Doc ,
85+ Opt ,
86+ Clippy ,
87+ }
88+
89+ impl Display for Profile {
90+ fn fmt ( & self , f : & mut Formatter < ' _ > ) -> std:: fmt:: Result {
91+ let name = match self {
92+ Profile :: Check => "Check" ,
93+ Profile :: Debug => "Debug" ,
94+ Profile :: Doc => "Doc" ,
95+ Profile :: Opt => "Opt" ,
96+ Profile :: Clippy => "Clippy" ,
97+ } ;
98+ f. write_str ( name)
99+ }
100+ }
101+
102+ #[ derive( Clone , Copy , Debug , clap:: ValueEnum ) ]
103+ #[ value( rename_all = "PascalCase" ) ]
104+ pub enum Scenario {
105+ Full ,
106+ IncrFull ,
107+ IncrUnchanged ,
108+ IncrPatched ,
109+ }
110+
111+ impl Display for Scenario {
112+ fn fmt ( & self , f : & mut Formatter < ' _ > ) -> std:: fmt:: Result {
113+ let name = match self {
114+ Scenario :: Full => "Full" ,
115+ Scenario :: IncrFull => "IncrFull" ,
116+ Scenario :: IncrUnchanged => "IncrUnchanged" ,
117+ Scenario :: IncrPatched => "IncrPatched" ,
118+ } ;
119+ f. write_str ( name)
120+ }
121+ }
5122
6123/// Performs profiling using `rustc-perf` on a built version of the compiler.
7- pub fn perf ( builder : & Builder < ' _ > ) {
124+ pub fn perf ( builder : & Builder < ' _ > , args : & PerfArgs ) {
8125 let collector = builder. ensure ( RustcPerf {
9126 compiler : builder. compiler ( 0 , builder. config . build ) ,
10127 target : builder. config . build ,
11128 } ) ;
12129
13- if builder. build . config . rust_debuginfo_level_rustc == DebuginfoLevel :: None {
130+ let is_profiling = match & args. cmd {
131+ PerfCommand :: Eprintln { .. }
132+ | PerfCommand :: Samply { .. }
133+ | PerfCommand :: Cachegrind { .. } => true ,
134+ PerfCommand :: Benchmark { .. } | PerfCommand :: Compare { .. } => false ,
135+ } ;
136+ if is_profiling && builder. build . config . rust_debuginfo_level_rustc == DebuginfoLevel :: None {
14137 builder. info ( r#"WARNING: You are compiling rustc without debuginfo, this will make profiling less useful.
15138Consider setting `rust.debuginfo-level = 1` in `config.toml`."# ) ;
16139 }
@@ -21,15 +144,69 @@ Consider setting `rust.debuginfo-level = 1` in `config.toml`."#);
21144 let rustc = sysroot. join ( "bin/rustc" ) ;
22145
23146 let rustc_perf_dir = builder. build . tempdir ( ) . join ( "rustc-perf" ) ;
24- let profile_results_dir = rustc_perf_dir. join ( "results" ) ;
147+ let results_dir = rustc_perf_dir. join ( "results" ) ;
148+ builder. create_dir ( & results_dir) ;
149+
150+ let mut cmd = command ( collector) ;
151+
152+ // We need to set the working directory to `src/tools/rustc-perf`, so that it can find the directory
153+ // with compile-time benchmarks.
154+ cmd. current_dir ( builder. src . join ( "src/tools/rustc-perf" ) ) ;
155+
156+ let db_path = results_dir. join ( "results.db" ) ;
157+
158+ match & args. cmd {
159+ PerfCommand :: Eprintln { opts }
160+ | PerfCommand :: Samply { opts }
161+ | PerfCommand :: Cachegrind { opts } => {
162+ cmd. arg ( "profile_local" ) ;
163+ cmd. arg ( match & args. cmd {
164+ PerfCommand :: Eprintln { .. } => "eprintln" ,
165+ PerfCommand :: Samply { .. } => "samply" ,
166+ PerfCommand :: Cachegrind { .. } => "cachegrind" ,
167+ _ => unreachable ! ( ) ,
168+ } ) ;
169+
170+ cmd. arg ( "--out-dir" ) . arg ( & results_dir) ;
171+ cmd. arg ( rustc) ;
172+
173+ apply_shared_opts ( & mut cmd, opts) ;
174+ cmd. run ( builder) ;
25175
26- // We need to take args passed after `--` and pass them to `rustc-perf-wrapper`
27- let args = std:: env:: args ( ) . skip_while ( |a| a != "--" ) . skip ( 1 ) ;
176+ println ! ( "You can find the results at `{}`" , & results_dir. display( ) ) ;
177+ }
178+ PerfCommand :: Benchmark { id, opts } => {
179+ cmd. arg ( "bench_local" ) ;
180+ cmd. arg ( "--db" ) . arg ( & db_path) ;
181+ cmd. arg ( "--id" ) . arg ( id) ;
182+ cmd. arg ( rustc) ;
28183
29- let mut cmd = builder. tool_cmd ( Tool :: RustcPerfWrapper ) ;
30- cmd. env ( "RUSTC_REAL" , rustc)
31- . env ( "PERF_COLLECTOR" , collector)
32- . env ( "PERF_RESULT_DIR" , profile_results_dir)
33- . args ( args) ;
34- cmd. run ( builder) ;
184+ apply_shared_opts ( & mut cmd, opts) ;
185+ cmd. run ( builder) ;
186+ }
187+ PerfCommand :: Compare { base, modified } => {
188+ cmd. arg ( "bench_cmp" ) ;
189+ cmd. arg ( "--db" ) . arg ( & db_path) ;
190+ cmd. arg ( base) . arg ( modified) ;
191+
192+ cmd. run ( builder) ;
193+ }
194+ }
195+ }
196+
197+ fn apply_shared_opts ( cmd : & mut BootstrapCommand , opts : & SharedOpts ) {
198+ if !opts. include . is_empty ( ) {
199+ cmd. arg ( "--include" ) . arg ( opts. include . join ( "," ) ) ;
200+ }
201+ if !opts. exclude . is_empty ( ) {
202+ cmd. arg ( "--exclude" ) . arg ( opts. exclude . join ( "," ) ) ;
203+ }
204+ if !opts. profiles . is_empty ( ) {
205+ cmd. arg ( "--profiles" )
206+ . arg ( opts. profiles . iter ( ) . map ( |p| p. to_string ( ) ) . collect :: < Vec < _ > > ( ) . join ( "," ) ) ;
207+ }
208+ if !opts. scenarios . is_empty ( ) {
209+ cmd. arg ( "--scenarios" )
210+ . arg ( opts. scenarios . iter ( ) . map ( |p| p. to_string ( ) ) . collect :: < Vec < _ > > ( ) . join ( "," ) ) ;
211+ }
35212}
0 commit comments