1
+ use std:: fmt:: { Display , Formatter } ;
2
+
1
3
use crate :: core:: build_steps:: compile:: { Std , Sysroot } ;
2
- use crate :: core:: build_steps:: tool:: { RustcPerf , Tool } ;
4
+ use crate :: core:: build_steps:: tool:: RustcPerf ;
3
5
use crate :: core:: builder:: Builder ;
4
6
use 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
+ }
5
122
6
123
/// 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 ) {
8
125
let collector = builder. ensure ( RustcPerf {
9
126
compiler : builder. compiler ( 0 , builder. config . build ) ,
10
127
target : builder. config . build ,
11
128
} ) ;
12
129
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 {
14
137
builder. info ( r#"WARNING: You are compiling rustc without debuginfo, this will make profiling less useful.
15
138
Consider setting `rust.debuginfo-level = 1` in `config.toml`."# ) ;
16
139
}
@@ -21,15 +144,69 @@ Consider setting `rust.debuginfo-level = 1` in `config.toml`."#);
21
144
let rustc = sysroot. join ( "bin/rustc" ) ;
22
145
23
146
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) ;
25
175
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) ;
28
183
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
+ }
35
212
}
0 commit comments