1 | use clap::{builder::PossibleValue, value_parser, Arg, ArgAction, ColorChoice, Command, ValueEnum}; |
2 | |
3 | use crate::{ |
4 | config::{ParsedSeconds, SortingAttr}, |
5 | counter::MaxCountUInt, |
6 | time::TimerKind, |
7 | util, |
8 | }; |
9 | |
10 | pub(crate) fn command() -> Command { |
11 | fn option(name: &'static str) -> Arg { |
12 | Arg::new(name).long(name) |
13 | } |
14 | |
15 | fn flag(name: &'static str) -> Arg { |
16 | option(name).action(ArgAction::SetTrue) |
17 | } |
18 | |
19 | fn ignored_flag(name: &'static str) -> Arg { |
20 | flag(name).hide(true) |
21 | } |
22 | |
23 | // Custom arguments not supported by libtest: |
24 | // - bytes-format |
25 | // - sample-count |
26 | // - sample-size |
27 | // - timer |
28 | // - sort |
29 | // - sortr |
30 | |
31 | // TODO: `--format <pretty|terse>` |
32 | |
33 | let mut cmd = Command::new("divan" ); |
34 | |
35 | // Support `cargo-nextest` running us with `--list --format terse`. |
36 | // |
37 | // TODO: Add CI test to ensure this doesn't break. |
38 | if util::is_cargo_nextest() { |
39 | cmd = cmd.arg(option("format" ).value_parser(["terse" ]).requires("list" )); |
40 | } |
41 | |
42 | cmd |
43 | .arg( |
44 | Arg::new("filter" ) |
45 | .value_name("FILTER" ) |
46 | .help("Only run benchmarks whose names match this pattern" ) |
47 | .action(ArgAction::Append), |
48 | ) |
49 | .arg( |
50 | flag("test" ) |
51 | .help("Run benchmarks once to ensure they run successfully" ) |
52 | .conflicts_with("list" ), |
53 | ) |
54 | .arg(flag("list" ).help("Lists benchmarks" ).conflicts_with("test" )) |
55 | .arg( |
56 | option("color" ) |
57 | .value_name("WHEN" ) |
58 | .help("Controls when to use colors" ) |
59 | .value_parser(value_parser!(ColorChoice)) |
60 | ) |
61 | .arg( |
62 | option("skip" ) |
63 | .value_name("FILTER" ) |
64 | .help("Skip benchmarks whose names match this pattern" ) |
65 | .action(ArgAction::Append), |
66 | ) |
67 | .arg(flag("exact" ).help("Filter benchmarks by exact name rather than by pattern" )) |
68 | .arg(flag("ignored" ).help("Run only ignored benchmarks" ).conflicts_with("include-ignored" )) |
69 | .arg( |
70 | flag("include-ignored" ) |
71 | .help("Run ignored and not-ignored benchmarks" ) |
72 | .conflicts_with("ignored" ), |
73 | ) |
74 | .arg( |
75 | option("sort" ) |
76 | .env("DIVAN_SORT" ) |
77 | .value_name("ATTRIBUTE" ) |
78 | .help("Sort benchmarks in ascending order" ) |
79 | .value_parser(value_parser!(SortingAttr)) |
80 | ) |
81 | .arg( |
82 | option("sortr" ) |
83 | .env("DIVAN_SORTR" ) |
84 | .value_name("ATTRIBUTE" ) |
85 | .help("Sort benchmarks in descending order" ) |
86 | .value_parser(value_parser!(SortingAttr)) |
87 | .overrides_with("sort" ), |
88 | ) |
89 | .arg( |
90 | option("timer" ) |
91 | .env("DIVAN_TIMER" ) |
92 | .value_name("os|tsc" ) |
93 | .help("Set the timer used for measuring samples" ) |
94 | .value_parser(value_parser!(TimerKind)), |
95 | ) |
96 | .arg( |
97 | option("sample-count" ) |
98 | .env("DIVAN_SAMPLE_COUNT" ) |
99 | .value_name("N" ) |
100 | .help("Set the number of sampling iterations" ) |
101 | .value_parser(value_parser!(u32)), |
102 | ) |
103 | .arg( |
104 | option("sample-size" ) |
105 | .env("DIVAN_SAMPLE_SIZE" ) |
106 | .value_name("N" ) |
107 | .help("Set the number of iterations inside a single sample" ) |
108 | .value_parser(value_parser!(u32)), |
109 | ) |
110 | .arg( |
111 | option("threads" ) |
112 | .env("DIVAN_THREADS" ) |
113 | .value_name("N" ) |
114 | .value_delimiter(',' ) |
115 | .action(ArgAction::Append) |
116 | .help("Run across multiple threads to measure contention on atomics and locks" ) |
117 | .value_parser(value_parser!(usize)), |
118 | ) |
119 | .arg( |
120 | option("min-time" ) |
121 | .env("DIVAN_MIN_TIME" ) |
122 | .value_name("SECS" ) |
123 | .help("Set the minimum seconds spent benchmarking a single function" ) |
124 | .value_parser(value_parser!(ParsedSeconds)), |
125 | ) |
126 | .arg( |
127 | option("max-time" ) |
128 | .env("DIVAN_MAX_TIME" ) |
129 | .value_name("SECS" ) |
130 | .help("Set the maximum seconds spent benchmarking a single function, with priority over '--min-time'" ) |
131 | .value_parser(value_parser!(ParsedSeconds)), |
132 | ) |
133 | .arg( |
134 | option("skip-ext-time" ) |
135 | .env("DIVAN_SKIP_EXT_TIME" ) |
136 | .value_name("true|false" ) |
137 | .help("When '--min-time' or '--max-time' is set, skip time external to benchmarked functions" ) |
138 | .value_parser(value_parser!(bool)) |
139 | .num_args(0..=1), |
140 | ) |
141 | .arg( |
142 | option("items-count" ) |
143 | .env("DIVAN_ITEMS_COUNT" ) |
144 | .value_name("N" ) |
145 | .help("Set every benchmark to have a throughput of N items" ) |
146 | .value_parser(value_parser!(MaxCountUInt)), |
147 | ) |
148 | .arg( |
149 | option("bytes-count" ) |
150 | .env("DIVAN_BYTES_COUNT" ) |
151 | .value_name("N" ) |
152 | .help("Set every benchmark to have a throughput of N bytes" ) |
153 | .value_parser(value_parser!(MaxCountUInt)), |
154 | ) |
155 | .arg( |
156 | option("bytes-format" ) |
157 | .env("DIVAN_BYTES_FORMAT" ) |
158 | .help("Set the numerical base for bytes in output" ) |
159 | .value_name("decimal|binary" ) |
160 | .value_parser(value_parser!(crate::counter::PrivBytesFormat)) |
161 | ) |
162 | .arg( |
163 | option("chars-count" ) |
164 | .env("DIVAN_CHARS_COUNT" ) |
165 | .value_name("N" ) |
166 | .help("Set every benchmark to have a throughput of N string scalars" ) |
167 | .value_parser(value_parser!(MaxCountUInt)), |
168 | ) |
169 | .arg( |
170 | option("cycles-count" ) |
171 | .env("DIVAN_CYCLES_COUNT" ) |
172 | .value_name("N" ) |
173 | .help("Set every benchmark to have a throughput of N cycles, displayed as Hertz" ) |
174 | .value_parser(value_parser!(MaxCountUInt)), |
175 | ) |
176 | // ignored: |
177 | .args([ignored_flag("bench" ), ignored_flag("nocapture" ), ignored_flag("show-output" )]) |
178 | } |
179 | |
180 | impl ValueEnum for TimerKind { |
181 | fn value_variants<'a>() -> &'a [Self] { |
182 | &[Self::Os, Self::Tsc] |
183 | } |
184 | |
185 | fn to_possible_value(&self) -> Option<PossibleValue> { |
186 | let name: &'static str = match self { |
187 | Self::Os => "os" , |
188 | Self::Tsc => "tsc" , |
189 | }; |
190 | Some(PossibleValue::new(name)) |
191 | } |
192 | } |
193 | |
194 | impl ValueEnum for SortingAttr { |
195 | fn value_variants<'a>() -> &'a [Self] { |
196 | &[Self::Kind, Self::Name, Self::Location] |
197 | } |
198 | |
199 | fn to_possible_value(&self) -> Option<PossibleValue> { |
200 | let name: &'static str = match self { |
201 | Self::Kind => "kind" , |
202 | Self::Name => "name" , |
203 | Self::Location => "location" , |
204 | }; |
205 | Some(PossibleValue::new(name)) |
206 | } |
207 | } |
208 | |