From 980756ee09b652333551da515c2cbd54918078ff Mon Sep 17 00:00:00 2001 From: lukacan Date: Sun, 17 Mar 2024 16:57:27 +0100 Subject: [PATCH] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20=20Refactor=20feature?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGELOG.md | 2 +- crates/cli/src/command/fuzz.rs | 12 +- crates/client/Cargo.toml | 1 + .../derive/fuzz_test_executor/src/lib.rs | 31 ++-- crates/client/src/commander.rs | 137 ++++++++++-------- crates/client/src/fuzzer/client_stats.rs | 26 ---- crates/client/src/fuzzer/data_builder.rs | 62 ++++---- crates/client/src/fuzzer/fuzzing_stats.rs | 75 ++++++++++ crates/client/src/fuzzer/mod.rs | 2 +- .../fuzzer/program_test_client_blocking.rs | 84 +---------- crates/client/src/lib.rs | 2 +- .../src/templates/trident-tests/test_fuzz.rs | 6 +- .../fuzz_fuzz_test_executor.expanded.rs | 10 +- examples/fuzz_example3/Cargo.lock | 66 ++++++++- examples/fuzz_example3/Trident.toml | 2 +- .../fuzz_tests/fuzz_0/test_fuzz.rs | 6 +- 16 files changed, 291 insertions(+), 233 deletions(-) delete mode 100644 crates/client/src/fuzzer/client_stats.rs create mode 100644 crates/client/src/fuzzer/fuzzing_stats.rs diff --git a/CHANGELOG.md b/CHANGELOG.md index 252dc6f30..063edfe22 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,7 +10,7 @@ incremented upon a breaking change and the patch version will be incremented for ## [Unreleased] ### Added -- feat/fuzzer-stats-logging, an optional stats output, triggered by `--run-with-stats`, during fuzzing sessions ([#144](https://github.com/Ackee-Blockchain/trident/pull/144)) +- feat/fuzzer-stats-logging, an optional stats output, triggered by `--stats` or `-s`, during fuzzing sessions ([#144](https://github.com/Ackee-Blockchain/trident/pull/144)) - feat/support of automatically obtaining fully qualified paths of Data Accounts Custom types for `accounts_snapshots.rs` ([#141](https://github.com/Ackee-Blockchain/trident/pull/141)) - feat/allow direct accounts manipulation and storage ([#142](https://github.com/Ackee-Blockchain/trident/pull/142)) - feat/support of non-corresponding instruction and context names ([#130](https://github.com/Ackee-Blockchain/trident/pull/130)) diff --git a/crates/cli/src/command/fuzz.rs b/crates/cli/src/command/fuzz.rs index b359d024d..aea801b84 100644 --- a/crates/cli/src/command/fuzz.rs +++ b/crates/cli/src/command/fuzz.rs @@ -18,9 +18,9 @@ pub enum FuzzCommand { /// Trident will return exit code 1 in case of found crash files in the crash folder. This is checked before and after the fuzz test run. #[arg(short, long)] with_exit_code: bool, - /// Trident will show statistics during fuzzing session. It is recommended to turn `keep_output` and `verbose` on true. + /// Trident will show statistics after the fuzzing session. It is recommended to turn `keep_output` on true. #[arg(short, long)] - run_with_stats: bool, + stats: bool, }, /// Debug fuzz target with crash file Run_Debug { @@ -53,14 +53,12 @@ pub async fn fuzz(root: Option, subcmd: FuzzCommand) { FuzzCommand::Run { target, with_exit_code, - run_with_stats, + stats, } => { if with_exit_code { - commander.run_fuzzer_with_exit_code(target).await?; - } else if run_with_stats { - commander.run_fuzzer_with_stats(target).await?; + commander.run_fuzzer_with_exit_code(target, stats).await?; } else { - commander.run_fuzzer(target).await?; + commander.run_fuzzer(target, stats).await?; } } FuzzCommand::Run_Debug { diff --git a/crates/client/Cargo.toml b/crates/client/Cargo.toml index 963321d9e..580e3e48d 100644 --- a/crates/client/Cargo.toml +++ b/crates/client/Cargo.toml @@ -72,3 +72,4 @@ solana-banks-client = "<1.18" indicatif = "0.17.8" regex = "1.10.3" solana-logger = "<1.18" +prettytable = "0.10.0" diff --git a/crates/client/derive/fuzz_test_executor/src/lib.rs b/crates/client/derive/fuzz_test_executor/src/lib.rs index b80c67ce8..ded7a7b2d 100644 --- a/crates/client/derive/fuzz_test_executor/src/lib.rs +++ b/crates/client/derive/fuzz_test_executor/src/lib.rs @@ -13,10 +13,10 @@ pub fn fuzz_test_executor(input: TokenStream) -> TokenStream { let variant_name = &variant.ident; quote! { #enum_name::#variant_name (ix) => { - #[cfg(fuzzing)] - if client.allow_stats() { - client.accumulate_executed_ix(self.to_context_string()); - } + #[cfg(fuzzing_with_stats)] + if let Some(fs) = fuzzing_stats{ + fs.increase_invoked(self.to_context_string()); + }; let (mut signers, metas) = ix.get_accounts(client, &mut accounts.borrow_mut()) .map_err(|e| e.with_origin(Origin::Instruction(self.to_context_string()))) .expect("Accounts calculation expect"); @@ -45,20 +45,20 @@ pub fn fuzz_test_executor(input: TokenStream) -> TokenStream { .map_err(|e| e.with_origin(Origin::Instruction(self.to_context_string()))); if tx_res.is_ok() { - #[cfg(fuzzing)] - if client.allow_stats() { - client.accumulate_successful_ix(self.to_context_string()); - } + #[cfg(fuzzing_with_stats)] + if let Some(fs) = fuzzing_stats{ + fs.increase_successfully_invoked(self.to_context_string()); + }; snaphot.capture_after(client).unwrap(); let (acc_before, acc_after) = snaphot.get_snapshot() .map_err(|e| e.with_origin(Origin::Instruction(self.to_context_string()))) .expect("Snapshot deserialization expect"); // we want to panic if we cannot unwrap to cause a crash if let Err(e) = ix.check(acc_before, acc_after, data).map_err(|e| e.with_origin(Origin::Instruction(self.to_context_string()))) { - #[cfg(fuzzing)] - if client.allow_stats() { - client.show_accumulated_stats(); - } + #[cfg(fuzzing_with_stats)] + if let Some(fs) = fuzzing_stats{ + fs.output_serialized(); + }; eprintln!( "CRASH DETECTED! Custom check after the {} instruction did not pass!", self.to_context_string()); @@ -71,14 +71,13 @@ pub fn fuzz_test_executor(input: TokenStream) -> TokenStream { quote! { impl FuzzTestExecutor for FuzzInstruction { - fn run_fuzzer( + fn run_fuzzer( &self, program_id: Pubkey, accounts: &RefCell, - client: &mut C, + client: &mut impl FuzzClient, + fuzzing_stats: &mut Option<&mut FuzzingStatistics>, ) -> core::result::Result<(), Box> - where - C: FuzzClient + StatsLogger, { match self { #(#display_match_arms)* diff --git a/crates/client/src/commander.rs b/crates/client/src/commander.rs index a2b7e4f3a..09a7e8370 100644 --- a/crates/client/src/commander.rs +++ b/crates/client/src/commander.rs @@ -19,6 +19,8 @@ use tokio::{ }; use crate::constants::*; +use crate::fuzzing_stats::FuzzingStatistics; +use tokio::io::AsyncBufReadExt; #[derive(Error, Debug)] pub enum Error { @@ -146,7 +148,7 @@ impl Commander { } /// Runs fuzzer on the given target with exit code option. #[throws] - pub async fn run_fuzzer_with_exit_code(&self, target: String) { + pub async fn run_fuzzer_with_exit_code(&self, target: String, with_stats: bool) { let config = Config::new(); // obtain hfuzz_run_args from env variable, this variable can contain multiple @@ -181,26 +183,31 @@ impl Commander { } } - let mut child = Command::new("cargo") - .env("HFUZZ_RUN_ARGS", fuzz_args) - .env("CARGO_TARGET_DIR", cargo_target_dir) - .env("HFUZZ_WORKSPACE", hfuzz_workspace) - .arg("hfuzz") - .arg("run") - .arg(target) - .spawn()?; - - tokio::select! { - res = child.wait() => - match res { - Ok(status) => if !status.success() { - println!("Honggfuzz exited with an error!"); - }, - Err(_) => throw!(Error::FuzzingFailed), - }, - _ = signal::ctrl_c() => { - tokio::time::sleep(tokio::time::Duration::from_millis(100)).await; - }, + match with_stats { + true => { + let mut child = Command::new("cargo") + .env("HFUZZ_RUN_ARGS", fuzz_args) + .env("CARGO_TARGET_DIR", cargo_target_dir) + .env("HFUZZ_WORKSPACE", hfuzz_workspace) + .env("RUSTFLAGS", "--cfg fuzzing_with_stats") + .arg("hfuzz") + .arg("run") + .arg(target) + .stdout(Stdio::piped()) + .spawn()?; + Self::handle_child_with_stats(&mut child).await?; + } + false => { + let mut child = Command::new("cargo") + .env("HFUZZ_RUN_ARGS", fuzz_args) + .env("CARGO_TARGET_DIR", cargo_target_dir) + .env("HFUZZ_WORKSPACE", hfuzz_workspace) + .arg("hfuzz") + .arg("run") + .arg(target) + .spawn()?; + Self::handle_child(&mut child).await?; + } } if let Ok(crash_files) = get_crash_files(&crash_dir, &ext) { @@ -216,7 +223,7 @@ impl Commander { /// Runs fuzzer on the given target. #[throws] - pub async fn run_fuzzer(&self, target: String) { + pub async fn run_fuzzer(&self, target: String, with_stats: bool) { let config = Config::new(); let hfuzz_run_args = std::env::var("HFUZZ_RUN_ARGS").unwrap_or_default(); @@ -228,15 +235,36 @@ impl Commander { let fuzz_args = config.get_fuzz_args(hfuzz_run_args); - let mut child = Command::new("cargo") - .env("HFUZZ_RUN_ARGS", fuzz_args) - .env("CARGO_TARGET_DIR", cargo_target_dir) - .env("HFUZZ_WORKSPACE", hfuzz_workspace) - .arg("hfuzz") - .arg("run") - .arg(target) - .spawn()?; + match with_stats { + true => { + let mut child = Command::new("cargo") + .env("HFUZZ_RUN_ARGS", fuzz_args) + .env("CARGO_TARGET_DIR", cargo_target_dir) + .env("HFUZZ_WORKSPACE", hfuzz_workspace) + .env("RUSTFLAGS", "--cfg fuzzing_with_stats") + .arg("hfuzz") + .arg("run") + .arg(target) + .stdout(Stdio::piped()) + .spawn()?; + Self::handle_child_with_stats(&mut child).await?; + } + false => { + let mut child = Command::new("cargo") + .env("HFUZZ_RUN_ARGS", fuzz_args) + .env("CARGO_TARGET_DIR", cargo_target_dir) + .env("HFUZZ_WORKSPACE", hfuzz_workspace) + .arg("hfuzz") + .arg("run") + .arg(target) + .spawn()?; + Self::handle_child(&mut child).await?; + } + } + } + #[throws] + async fn handle_child(child: &mut Child) { tokio::select! { res = child.wait() => match res { @@ -251,45 +279,38 @@ impl Commander { } } - /// Runs fuzzer on the given target and turn FUZZING_STATS env variable on true - /// Based on the variable, fuzzer will show statistics during fuzzing session #[throws] - pub async fn run_fuzzer_with_stats(&self, target: String) { - let config = Config::new(); - - let hfuzz_run_args = std::env::var("HFUZZ_RUN_ARGS").unwrap_or_default(); - - // std::env::set_var("FUZZING_STATS", "true"); - - let cargo_target_dir = std::env::var("CARGO_TARGET_DIR") - .unwrap_or_else(|_| config.get_env_arg("CARGO_TARGET_DIR")); - let hfuzz_workspace = std::env::var("HFUZZ_WORKSPACE") - .unwrap_or_else(|_| config.get_env_arg("HFUZZ_WORKSPACE")); - - let fuzz_args = config.get_fuzz_args(hfuzz_run_args); - - let mut child = Command::new("cargo") - .env("HFUZZ_RUN_ARGS", fuzz_args) - .env("CARGO_TARGET_DIR", cargo_target_dir) - .env("HFUZZ_WORKSPACE", hfuzz_workspace) - .env("FUZZING_STATS", "true") - .arg("hfuzz") - .arg("run") - .arg(target) - .spawn()?; + async fn handle_child_with_stats(child: &mut Child) { + let mut stats_logger = FuzzingStatistics::new(); tokio::select! { - res = child.wait() => + res = child.wait() =>{ + + let stdout = child.stdout.take().expect("child did not have a handle to stdout"); + let mut reader = tokio::io::BufReader::new(stdout).lines(); + while let Some(line) = reader.next_line().await? { + stats_logger.insert_serialized(&line); + } match res { - Ok(status) => if !status.success() { - println!("Honggfuzz exited with an error!"); + Ok(status) => { + if !status.success() { + println!("Honggfuzz exited with an error!"); + } }, Err(_) => throw!(Error::FuzzingFailed), + } }, _ = signal::ctrl_c() => { tokio::time::sleep(tokio::time::Duration::from_millis(100)).await; + + let stdout = child.stdout.take().expect("child did not have a handle to stdout"); + let mut reader = tokio::io::BufReader::new(stdout).lines(); + while let Some(line) = reader.next_line().await? { + stats_logger.insert_serialized(&line); + } }, } + stats_logger.show_table(); } /// Runs fuzzer on the given target. diff --git a/crates/client/src/fuzzer/client_stats.rs b/crates/client/src/fuzzer/client_stats.rs deleted file mode 100644 index 110dc6ae4..000000000 --- a/crates/client/src/fuzzer/client_stats.rs +++ /dev/null @@ -1,26 +0,0 @@ -use std::collections::HashMap; - -#[derive(Debug)] -pub struct AccumulatedStats { - pub executed: u64, - pub successful: u64, -} - -#[derive(Debug, Default)] -pub struct Stats { - pub accumulated_stats: HashMap, - pub iterations: u64, -} - -pub trait StatsLogger { - /// Accumulate number of invocations for corresponding instruction - fn accumulate_executed_ix(&mut self, ix: String); - /// Accumulate number of successful invocations for corresponding instruction - fn accumulate_successful_ix(&mut self, ix: String); - /// Show the Accumulated stats - fn show_accumulated_stats(&mut self); - /// Increase number of iterations - fn increase_iterrations(&mut self); - /// Return if show stats are enabled - fn allow_stats(&self) -> bool; -} diff --git a/crates/client/src/fuzzer/data_builder.rs b/crates/client/src/fuzzer/data_builder.rs index 312a77725..31ca77e0b 100644 --- a/crates/client/src/fuzzer/data_builder.rs +++ b/crates/client/src/fuzzer/data_builder.rs @@ -12,9 +12,8 @@ use std::cell::RefCell; use std::error::Error; use std::fmt::Display; -use crate::client_stats::StatsLogger; use crate::error::*; - +use crate::fuzzing_stats::FuzzingStatistics; pub struct FuzzData { pub pre_ixs: Vec, pub ixs: Vec, @@ -53,14 +52,11 @@ impl FuzzData where T: FuzzTestExecutor + Display, { - pub fn run_with_runtime( + pub fn run_with_runtime( &self, program_id: Pubkey, - client: &mut C, - ) -> core::result::Result<(), Box> - where - C: FuzzClient + StatsLogger, - { + client: &mut impl FuzzClient, + ) -> core::result::Result<(), Box> { #[cfg(fuzzing_debug)] { solana_logger::setup_with_default( @@ -75,38 +71,50 @@ where eprintln!("{}", ix); } eprintln!("------ End of Instructions sequence ------ "); + for fuzz_ix in &mut self.iter() { + eprintln!("Currently processing: {}", fuzz_ix); + fuzz_ix.run_fuzzer(program_id, &self.accounts, client, &mut None)?; + } + Ok(()) } - #[cfg(fuzzing)] - if client.allow_stats() { + #[cfg(fuzzing_with_stats)] + { // the line below will prevent the fuzzing output from unnecessary messages like: // [2024-03-13T11:57:20.158011532Z INFO solana_program_test_anchor_fix] "fuzz_example0" builtin program // I guess we can remove it once we do not need solana_program_test_anchor_fix. - // std::env::set_var("RUST_LOG", "solana_program_test_anchor_fix=off"); - // solana_logger::setup_with_default("off"); - client.increase_iterrations(); - } - for fuzz_ix in &mut self.iter() { - #[cfg(fuzzing_debug)] - eprintln!("Currently processing: {}", fuzz_ix); - fuzz_ix.run_fuzzer(program_id, &self.accounts, client)?; + std::env::set_var("RUST_LOG", "solana_program_test_anchor_fix=off"); + solana_logger::setup_with_default("off"); + + let mut stats_logger = FuzzingStatistics::new(); + for fuzz_ix in &mut self.iter() { + fuzz_ix.run_fuzzer( + program_id, + &self.accounts, + client, + &mut Some(&mut stats_logger), + )?; + } + stats_logger.output_serialized(); + Ok(()) } - #[cfg(fuzzing)] - if client.allow_stats() { - client.show_accumulated_stats(); + #[cfg(not(fuzzing_with_stats))] + { + for fuzz_ix in &mut self.iter() { + fuzz_ix.run_fuzzer(program_id, &self.accounts, client, &mut None)?; + } + Ok(()) } - Ok(()) } } pub trait FuzzTestExecutor { - fn run_fuzzer( + fn run_fuzzer( &self, program_id: Pubkey, accounts: &RefCell, - client: &mut C, - ) -> core::result::Result<(), Box> - where - C: FuzzClient + StatsLogger; + client: &mut impl FuzzClient, + fuzzing_stats: &mut Option<&mut FuzzingStatistics>, + ) -> core::result::Result<(), Box>; } #[allow(unused_variables)] diff --git a/crates/client/src/fuzzer/fuzzing_stats.rs b/crates/client/src/fuzzer/fuzzing_stats.rs new file mode 100644 index 000000000..6d1728729 --- /dev/null +++ b/crates/client/src/fuzzer/fuzzing_stats.rs @@ -0,0 +1,75 @@ +use prettytable::{row, Table}; +use std::collections::HashMap; +#[derive(Debug, serde::Serialize, serde::Deserialize)] +pub struct IterationStats { + pub invoked: u64, + pub successfully_invoked: u64, +} + +#[derive(Debug, Default)] +pub struct FuzzingStatistics { + pub instructions: HashMap, +} + +impl FuzzingStatistics { + pub fn new() -> Self { + let empty_instructions = HashMap::::default(); + Self { + instructions: empty_instructions, + } + } + pub fn output_serialized(&self) { + let serialized = serde_json::to_string(&self.instructions).unwrap(); + println!("{}", serialized); + } + pub fn increase_invoked(&mut self, instruction: String) { + self.instructions + .entry(instruction) + .and_modify(|iterations_stats| iterations_stats.invoked += 1) + .or_insert(IterationStats { + invoked: 1, + successfully_invoked: 0, + }); + } + pub fn increase_successfully_invoked(&mut self, instruction: String) { + self.instructions + .entry(instruction) + .and_modify(|iterations_stats| iterations_stats.successfully_invoked += 1) + .or_insert( + // this should not occure as instruction has to be invoked + // and then successfully_invoked + IterationStats { + invoked: 1, + successfully_invoked: 1, + }, + ); + } + pub fn insert_serialized(&mut self, serialized_iteration: &str) { + let result = serde_json::from_str::>( + serialized_iteration, + ); + + if let Ok(deserialized_instruction) = result { + for (key, value) in deserialized_instruction { + self.instructions + .entry(key) + .and_modify(|instruction_stats| { + instruction_stats.invoked += value.invoked; + instruction_stats.successfully_invoked += value.successfully_invoked; + }) + .or_insert_with(|| IterationStats { + invoked: value.invoked, + successfully_invoked: value.successfully_invoked, + }); + } + } + } + pub fn show_table(&self) { + let mut table = Table::new(); + table.add_row(row!["Instruction", "Invoked", "Successfully Invoked"]); + for (instruction, stats) in &self.instructions { + table.add_row(row![instruction, stats.invoked, stats.successfully_invoked]); + } + table.printstd(); + } +} diff --git a/crates/client/src/fuzzer/mod.rs b/crates/client/src/fuzzer/mod.rs index 65628f60f..b478cf711 100644 --- a/crates/client/src/fuzzer/mod.rs +++ b/crates/client/src/fuzzer/mod.rs @@ -1,7 +1,7 @@ pub mod accounts_storage; -pub mod client_stats; pub mod data_builder; pub mod fuzzer_generator; +pub mod fuzzing_stats; #[cfg(feature = "fuzzing")] pub mod program_test_client_blocking; pub mod snapshot; diff --git a/crates/client/src/fuzzer/program_test_client_blocking.rs b/crates/client/src/fuzzer/program_test_client_blocking.rs index 08a892b41..b1d79bb03 100644 --- a/crates/client/src/fuzzer/program_test_client_blocking.rs +++ b/crates/client/src/fuzzer/program_test_client_blocking.rs @@ -10,16 +10,12 @@ use solana_sdk::{ use spl_token::state::Mint; use tokio::runtime::Builder; -use crate::client_stats::{AccumulatedStats, Stats, StatsLogger}; -use crate::constants::*; use crate::data_builder::FuzzClient; use crate::error::*; pub struct ProgramTestClientBlocking { ctx: ProgramTestContext, rt: tokio::runtime::Runtime, - stats: Stats, - allow_stats: bool, } impl ProgramTestClientBlocking { @@ -30,33 +26,9 @@ impl ProgramTestClientBlocking { ) -> Result { let program_test = ProgramTest::new(program_name, program_id, entry); let rt: tokio::runtime::Runtime = Builder::new_current_thread().enable_all().build()?; - let ctx = rt.block_on(program_test.start_with_context()); - let allow_stats = std::env::var("FUZZING_STATS").is_ok(); - #[cfg(fuzzing)] - if allow_stats { - std::env::set_var("RUST_LOG", "solana_program_test_anchor_fix=off"); - solana_logger::setup_with_default("off"); - } - - let stats = Stats::default(); - Ok(Self { - ctx, - rt, - allow_stats, - stats, - }) - } - pub fn new_clean( - &mut self, - program_name: &str, - program_id: Pubkey, - entry: Option, - ) -> Result<(), FuzzClientError> { - let program_test = ProgramTest::new(program_name, program_id, entry); - let ctx = self.rt.block_on(program_test.start_with_context()); - self.ctx = ctx; - Ok(()) + let ctx = rt.block_on(program_test.start_with_context()); + Ok(Self { ctx, rt }) } } @@ -200,55 +172,3 @@ impl FuzzClient for ProgramTestClientBlocking { Ok(self.rt.block_on(self.ctx.banks_client.get_rent())?) } } - -impl StatsLogger for ProgramTestClientBlocking { - fn accumulate_executed_ix(&mut self, ix: String) { - self.stats - .accumulated_stats - .entry(ix) - .and_modify(|e| e.executed = e.executed.checked_add(1).ok_or(u64::MAX).unwrap()) - .or_insert(AccumulatedStats { - executed: 1, - successful: 0, - }); - } - fn accumulate_successful_ix(&mut self, ix: String) { - self.stats - .accumulated_stats - .entry(ix) - .and_modify(|e| e.successful = e.successful.checked_add(1).ok_or(u64::MAX).unwrap()) - .or_insert(AccumulatedStats { - executed: 0, - successful: 1, - }); - } - fn show_accumulated_stats(&mut self) { - let stats = &self.stats; - let not_successful: Vec<(&String, &AccumulatedStats)> = stats - .accumulated_stats - .iter() - .filter(|e| e.1.successful == 0) - .collect(); - - if not_successful.is_empty() { - println!("{SUCCESS} Accumulated stats: {:#?}", self.stats); - } else { - eprintln!( - "{ERROR} The {:#?} instruction/s have 0 successful invocations", - not_successful - ); - } - } - - fn increase_iterrations(&mut self) { - self.stats.iterations = self - .stats - .iterations - .checked_add(1) - .ok_or(u64::MAX) - .unwrap(); - } - fn allow_stats(&self) -> bool { - self.allow_stats - } -} diff --git a/crates/client/src/lib.rs b/crates/client/src/lib.rs index 7627c83f7..449e096ca 100644 --- a/crates/client/src/lib.rs +++ b/crates/client/src/lib.rs @@ -39,9 +39,9 @@ pub mod fuzzing { pub use super::error::*; pub use super::fuzzer::accounts_storage::*; - pub use super::fuzzer::client_stats::StatsLogger; pub use super::fuzzer::data_builder::build_ix_fuzz_data; pub use super::fuzzer::data_builder::*; + pub use super::fuzzing_stats::FuzzingStatistics; pub use super::fuzzer::program_test_client_blocking::ProgramTestClientBlocking; pub use super::fuzzer::snapshot::Snapshot; diff --git a/crates/client/src/templates/trident-tests/test_fuzz.rs b/crates/client/src/templates/trident-tests/test_fuzz.rs index c61c4a682..9b38c65a6 100644 --- a/crates/client/src/templates/trident-tests/test_fuzz.rs +++ b/crates/client/src/templates/trident-tests/test_fuzz.rs @@ -9,11 +9,11 @@ struct MyFuzzData; impl FuzzDataBuilder for MyFuzzData {} fn main() { - let mut client = - ProgramTestClientBlocking::new(PROGRAM_NAME, PROGRAM_ID, processor!(entry)).unwrap(); loop { fuzz_trident!(fuzz_ix: FuzzInstruction, |fuzz_data: MyFuzzData| { - client.new_clean(PROGRAM_NAME, PROGRAM_ID, processor!(entry)).unwrap(); + let mut client = + ProgramTestClientBlocking::new(PROGRAM_NAME, PROGRAM_ID, processor!(entry)) + .unwrap(); let _ = fuzz_data.run_with_runtime(PROGRAM_ID, &mut client); }); } diff --git a/crates/client/tests/test_data/fuzzer_macros/fuzz_fuzz_test_executor.expanded.rs b/crates/client/tests/test_data/fuzzer_macros/fuzz_fuzz_test_executor.expanded.rs index 192c9237a..1077e636d 100644 --- a/crates/client/tests/test_data/fuzzer_macros/fuzz_fuzz_test_executor.expanded.rs +++ b/crates/client/tests/test_data/fuzzer_macros/fuzz_fuzz_test_executor.expanded.rs @@ -4,15 +4,13 @@ pub enum FuzzInstruction { WithdrawUnlocked(WithdrawUnlocked), } impl FuzzTestExecutor for FuzzInstruction { - fn run_fuzzer( + fn run_fuzzer( &self, program_id: Pubkey, accounts: &RefCell, - client: &mut C, - ) -> core::result::Result<(), Box> - where - C: FuzzClient + StatsLogger, - { + client: &mut impl FuzzClient, + fuzzing_stats: &mut Option<&mut FuzzingStatistics>, + ) -> core::result::Result<(), Box> { match self { FuzzInstruction::InitVesting(ix) => { let (mut signers, metas) = ix diff --git a/examples/fuzz_example3/Cargo.lock b/examples/fuzz_example3/Cargo.lock index 56ef76a68..949fccf93 100644 --- a/examples/fuzz_example3/Cargo.lock +++ b/examples/fuzz_example3/Cargo.lock @@ -1044,7 +1044,7 @@ version = "0.15.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0e1f83fc076bd6dd27517eacdf25fef6c4dfe5f1d7448bafaaf3a26f13b5e4eb" dependencies = [ - "encode_unicode", + "encode_unicode 0.3.6", "lazy_static", "libc", "unicode-width", @@ -1183,6 +1183,27 @@ dependencies = [ "subtle", ] +[[package]] +name = "csv" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac574ff4d437a7b5ad237ef331c17ccca63c46479e5b5453eb8e10bb99a759fe" +dependencies = [ + "csv-core", + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "csv-core" +version = "0.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5efa2b3d7902f4b634a20cae3c9c4e6209dc4779feb6863329607560143efa70" +dependencies = [ + "memchr", +] + [[package]] name = "ctr" version = "0.8.0" @@ -1548,6 +1569,12 @@ version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f" +[[package]] +name = "encode_unicode" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34aa73646ffb006b8f5147f3dc182bd4bcb190227ce861fc4a4844bf8e3cb2c0" + [[package]] name = "encoding_rs" version = "0.8.33" @@ -2215,6 +2242,17 @@ version = "2.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f518f335dce6725a761382244631d86cf0ccb2863413590b31338feb467f9c3" +[[package]] +name = "is-terminal" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f23ff5ef2b80d608d61efee834934d862cd92461afc0560dedf493e4c033738b" +dependencies = [ + "hermit-abi 0.3.6", + "libc", + "windows-sys 0.52.0", +] + [[package]] name = "itertools" version = "0.10.5" @@ -2971,6 +3009,20 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c6fa0831dd7cc608c38a5e323422a0077678fa5744aa2be4ad91c4ece8eec8d5" +[[package]] +name = "prettytable" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46480520d1b77c9a3482d39939fcf96831537a250ec62d4fd8fbdf8e0302e781" +dependencies = [ + "csv", + "encode_unicode 1.0.0", + "is-terminal", + "lazy_static", + "term", + "unicode-width", +] + [[package]] name = "proc-macro-crate" version = "0.1.5" @@ -5472,6 +5524,17 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "term" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c59df8ac95d96ff9bede18eb7300b0fda5e5d8d90960e76f8e14ae765eedbf1f" +dependencies = [ + "dirs-next", + "rustversion", + "winapi", +] + [[package]] name = "termcolor" version = "1.4.0" @@ -5846,6 +5909,7 @@ dependencies = [ "lazy_static", "log", "pathdiff", + "prettytable", "proc-macro2", "quinn-proto", "quote", diff --git a/examples/fuzz_example3/Trident.toml b/examples/fuzz_example3/Trident.toml index b807e2a12..80cc01720 100644 --- a/examples/fuzz_example3/Trident.toml +++ b/examples/fuzz_example3/Trident.toml @@ -10,7 +10,7 @@ iterations = 2000 # Number of concurrent fuzzing threads (default: 0 [number of CPUs / 2]) threads = 0 # Don't close children's stdin, stdout, stderr; can be noisy (default: false) -keep_output = false +keep_output = true # Disable ANSI console; use simple log output (default: false) verbose = false # Exit upon seeing the first crash (default: false) diff --git a/examples/fuzz_example3/trident-tests/fuzz_tests/fuzz_0/test_fuzz.rs b/examples/fuzz_example3/trident-tests/fuzz_tests/fuzz_0/test_fuzz.rs index 0bf78a6bf..2bd5d335d 100644 --- a/examples/fuzz_example3/trident-tests/fuzz_tests/fuzz_0/test_fuzz.rs +++ b/examples/fuzz_example3/trident-tests/fuzz_tests/fuzz_0/test_fuzz.rs @@ -18,11 +18,11 @@ impl FuzzDataBuilder for MyFuzzData { } fn main() { - let mut client = - ProgramTestClientBlocking::new(PROGRAM_NAME, PROGRAM_ID, processor!(entry)).unwrap(); loop { fuzz_trident!(fuzz_ix: FuzzInstruction, |fuzz_data: MyFuzzData| { - client.new_clean(PROGRAM_NAME, PROGRAM_ID, processor!(entry)).unwrap(); + let mut client = + ProgramTestClientBlocking::new(PROGRAM_NAME, PROGRAM_ID, processor!(entry)) + .unwrap(); let _ = fuzz_data.run_with_runtime(PROGRAM_ID, &mut client); }); }