Skip to content

Commit c04d366

Browse files
authored
cli: support generating cu reports (#99)
1 parent 655cf6b commit c04d366

File tree

7 files changed

+111
-24
lines changed

7 files changed

+111
-24
lines changed

Cargo.lock

Lines changed: 2 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

bencher/src/lib.rs

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -56,9 +56,10 @@
5656
//! | bench3 | 2,811 | +2,361 |
5757
//! ```
5858
59-
mod result;
59+
pub mod result;
6060

6161
use {
62+
chrono::Utc,
6263
mollusk_svm::{result::ProgramResult, Mollusk},
6364
result::{write_results, MolluskComputeUnitBenchResult},
6465
solana_account::Account,
@@ -113,6 +114,7 @@ impl<'a> MolluskComputeUnitBencher<'a> {
113114

114115
/// Execute the benches.
115116
pub fn execute(&mut self) {
117+
let table_header = Utc::now().to_string();
116118
let solana_version = get_solana_version();
117119
let bench_results = std::mem::take(&mut self.benches)
118120
.into_iter()
@@ -132,11 +134,11 @@ impl<'a> MolluskComputeUnitBencher<'a> {
132134
MolluskComputeUnitBenchResult::new(name, result)
133135
})
134136
.collect::<Vec<_>>();
135-
write_results(&self.out_dir, &solana_version, bench_results);
137+
write_results(&self.out_dir, &table_header, &solana_version, bench_results);
136138
}
137139
}
138140

139-
fn get_solana_version() -> String {
141+
pub fn get_solana_version() -> String {
140142
match Command::new("solana").arg("--version").output() {
141143
Ok(output) if output.status.success() => {
142144
String::from_utf8_lossy(&output.stdout).trim().to_string()

bencher/src/result.rs

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,12 @@
11
//! Compute unit benchmarking results and checks.
22
33
use {
4-
chrono::{DateTime, Utc},
54
mollusk_svm::result::InstructionResult,
65
num_format::{Locale, ToFormattedString},
76
std::path::Path,
87
};
98

10-
pub(crate) struct MolluskComputeUnitBenchResult<'a> {
9+
pub struct MolluskComputeUnitBenchResult<'a> {
1110
name: &'a str,
1211
cus_consumed: u64,
1312
}
@@ -19,8 +18,9 @@ impl<'a> MolluskComputeUnitBenchResult<'a> {
1918
}
2019
}
2120

22-
pub(crate) fn write_results(
21+
pub fn write_results(
2322
out_dir: &Path,
23+
table_header: &str,
2424
solana_version: &str,
2525
results: Vec<MolluskComputeUnitBenchResult>,
2626
) {
@@ -38,7 +38,7 @@ pub(crate) fn write_results(
3838
.map(|content| parse_last_md_table(content));
3939

4040
// Prepare to write a new table.
41-
let mut md_table = md_header(solana_version);
41+
let mut md_table = md_header(table_header, solana_version);
4242

4343
// Evaluate the results against the previous table, if any.
4444
// If there are changes, write a new table.
@@ -80,24 +80,23 @@ pub(crate) fn write_results(
8080
}
8181
}
8282

83-
fn md_header(solana_version: &str) -> String {
84-
let now: DateTime<Utc> = Utc::now();
83+
fn md_header(table_header: &str, solana_version: &str) -> String {
8584
format!(
86-
r#"#### Compute Units: {}
85+
r#"#### {}
8786
8887
Solana CLI Version: {}
8988
9089
| Name | CUs | Delta |
9190
|------|------|-------|
9291
"#,
93-
now, solana_version,
92+
table_header, solana_version,
9493
)
9594
}
9695

9796
fn parse_last_md_table(content: &str) -> Vec<MolluskComputeUnitBenchResult> {
9897
let mut results = vec![];
9998

100-
for line in content.lines().skip(4) {
99+
for line in content.lines().skip(6) {
101100
if line.starts_with("####") || line.is_empty() {
102101
break;
103102
}

cli/Cargo.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,12 @@ edition = { workspace = true }
99
version = { workspace = true }
1010

1111
[dependencies]
12+
chrono = { workspace = true }
1213
clap = { workspace = true, features = ["derive"] }
1314
serde = { workspace = true, features = ["derive"] }
1415
serde_json = { workspace = true }
1516
serde_yaml = { workspace = true }
17+
mollusk-svm-bencher = { workspace = true }
1618
mollusk-svm-fuzz-fixture = { workspace = true }
1719
mollusk-svm-fuzz-fixture-firedancer = { workspace = true }
1820
mollusk-svm = { workspace = true, features = ["fuzz", "fuzz-fd", "serde"] }

cli/src/main.rs

Lines changed: 31 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ use {
88
clap::{Parser, Subcommand},
99
config::ConfigFile,
1010
mollusk_svm::{result::Compare, Mollusk},
11+
runner::CusReport,
1112
solana_pubkey::Pubkey,
1213
std::{fs, path::Path, str::FromStr},
1314
};
@@ -30,6 +31,14 @@ enum SubCommand {
3031
/// Path to the config file for validation checks.
3132
#[arg(short, long)]
3233
config: Option<String>,
34+
/// Directory to write a compute unit consumption report.
35+
#[arg(long)]
36+
cus_report: Option<String>,
37+
/// Table header for the compute unit consumption report.
38+
///
39+
/// Note this flag is ignored if `cus_report` is not set.
40+
#[arg(long)]
41+
cus_report_table_header: Option<String>,
3342
/// Skip comparing compute unit consumption, but compare everything
3443
/// else.
3544
///
@@ -72,6 +81,14 @@ enum SubCommand {
7281
/// Path to the config file for validation checks.
7382
#[arg(short, long)]
7483
config: Option<String>,
84+
/// Directory to write a compute unit consumption report.
85+
#[arg(long)]
86+
cus_report: Option<String>,
87+
/// Table header for the compute unit consumption report.
88+
///
89+
/// Note this flag is ignored if `cus_report` is not set.
90+
#[arg(long)]
91+
cus_report_table_header: Option<String>,
7592
/// Skip comparing compute unit consumption, but compare everything
7693
/// else.
7794
///
@@ -136,6 +153,8 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
136153
fixture,
137154
program_id,
138155
config,
156+
cus_report,
157+
cus_report_table_header,
139158
ignore_compute_units,
140159
inputs_only,
141160
program_logs,
@@ -156,18 +175,24 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
156175

157176
let fixtures = search_paths(&fixture, "fix")?;
158177

159-
Runner::new(checks, inputs_only, program_logs, proto, verbose).run_all(
160-
None,
161-
&mut mollusk,
162-
&fixtures,
163-
)?
178+
Runner::new(
179+
checks,
180+
cus_report.map(|path| CusReport::new(path, cus_report_table_header)),
181+
inputs_only,
182+
program_logs,
183+
proto,
184+
verbose,
185+
)
186+
.run_all(None, &mut mollusk, &fixtures)?
164187
}
165188
SubCommand::RunTest {
166189
elf_path_source,
167190
elf_path_target,
168191
fixture,
169192
program_id,
170193
config,
194+
cus_report,
195+
cus_report_table_header,
171196
ignore_compute_units,
172197
program_logs,
173198
proto,
@@ -194,6 +219,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
194219

195220
Runner::new(
196221
checks,
222+
cus_report.map(|path| CusReport::new(path, cus_report_table_header)),
197223
/* inputs_only */ true,
198224
program_logs,
199225
proto,

cli/src/runner.rs

Lines changed: 62 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,14 @@
11
//! CLI runner. Many jobs share the same pattern but do different core actions.
22
33
use {
4+
chrono::Utc,
45
clap::ValueEnum,
56
mollusk_svm::{
67
result::{Compare, Config, InstructionResult},
78
Mollusk,
89
},
10+
mollusk_svm_bencher::{get_solana_version, result::MolluskComputeUnitBenchResult},
11+
std::path::PathBuf,
912
};
1013

1114
#[derive(Clone, Debug, Default, ValueEnum)]
@@ -17,8 +20,26 @@ pub enum ProtoLayout {
1720
Firedancer,
1821
}
1922

23+
pub struct CusReport {
24+
pub path: String,
25+
pub table_header: String,
26+
}
27+
28+
impl CusReport {
29+
pub fn new(path: String, table_header: Option<String>) -> Self {
30+
let table_header = table_header.unwrap_or_else(|| Utc::now().to_string());
31+
Self { path, table_header }
32+
}
33+
}
34+
35+
pub struct RunResult<'a> {
36+
pub pass: bool,
37+
pub bench_result: Option<MolluskComputeUnitBenchResult<'a>>,
38+
}
39+
2040
pub struct Runner {
2141
checks: Vec<Compare>,
42+
cus_report: Option<CusReport>,
2243
inputs_only: bool,
2344
program_logs: bool,
2445
proto: ProtoLayout,
@@ -28,13 +49,15 @@ pub struct Runner {
2849
impl Runner {
2950
pub fn new(
3051
checks: Vec<Compare>,
52+
cus_report: Option<CusReport>,
3153
inputs_only: bool,
3254
program_logs: bool,
3355
proto: ProtoLayout,
3456
verbose: bool,
3557
) -> Self {
3658
Self {
3759
checks,
60+
cus_report,
3861
inputs_only,
3962
program_logs,
4063
proto,
@@ -66,12 +89,12 @@ impl Runner {
6689
}
6790
}
6891

69-
pub fn run(
92+
fn run<'a>(
7093
&self,
7194
ground: Option<&mut Mollusk>,
7295
target: &mut Mollusk,
73-
fixture_path: &str,
74-
) -> Result<bool, Box<dyn std::error::Error>> {
96+
fixture_path: &'a str,
97+
) -> Result<RunResult<'a>, Box<dyn std::error::Error>> {
7598
// Disable stdout logging of program logs if not specified.
7699
if !self.program_logs {
77100
solana_logger::setup_with("");
@@ -141,6 +164,16 @@ impl Runner {
141164

142165
let (target_result, effects) = self.run_fixture(target, fixture_path);
143166

167+
// Record a bench result for the CU report, if specified.
168+
let bench_result = if self.cus_report.is_some() {
169+
Some(MolluskComputeUnitBenchResult::new(
170+
parse_fixture_name(fixture_path),
171+
target_result.clone(),
172+
))
173+
} else {
174+
None
175+
};
176+
144177
if self.program_logs {
145178
println!();
146179
}
@@ -201,7 +234,7 @@ impl Runner {
201234
println!();
202235
}
203236

204-
Ok(pass)
237+
Ok(RunResult { pass, bench_result })
205238
}
206239

207240
pub fn run_all(
@@ -211,11 +244,16 @@ impl Runner {
211244
fixtures: &[String],
212245
) -> Result<(), Box<dyn std::error::Error>> {
213246
let mut failures = 0;
247+
let mut bench_results = Vec::new();
214248

215249
for fixture_path in fixtures {
216-
let result = self.run(ground.as_deref_mut(), target, fixture_path)?;
250+
let mut result = self.run(ground.as_deref_mut(), target, fixture_path)?;
251+
252+
if let Some(bench_result) = result.bench_result.take() {
253+
bench_results.push(bench_result);
254+
}
217255

218-
if !result {
256+
if !result.pass {
219257
failures += 1;
220258
}
221259
}
@@ -227,6 +265,24 @@ impl Runner {
227265
std::process::exit(1);
228266
}
229267

268+
if let Some(cus_report) = &self.cus_report {
269+
let solana_version = get_solana_version();
270+
mollusk_svm_bencher::result::write_results(
271+
&PathBuf::from(&cus_report.path),
272+
&cus_report.table_header,
273+
&solana_version,
274+
bench_results,
275+
);
276+
}
277+
230278
Ok(())
231279
}
232280
}
281+
282+
fn parse_fixture_name(fixture_path: &str) -> &str {
283+
fixture_path
284+
.rsplit_once('/')
285+
.map_or(fixture_path, |(_, name)| name)
286+
.split_once('.')
287+
.map_or_else(|| fixture_path, |(name, _)| name)
288+
}

harness/src/result.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ impl From<Result<(), InstructionError>> for ProgramResult {
7575
}
7676

7777
/// The overall result of the instruction.
78-
#[derive(Debug, PartialEq, Eq)]
78+
#[derive(Clone, Debug, PartialEq, Eq)]
7979
pub struct InstructionResult {
8080
/// The number of compute units consumed by the instruction.
8181
pub compute_units_consumed: u64,

0 commit comments

Comments
 (0)