trident_fuzz_metrics/
lib.rs1#![allow(dead_code)]
2
3mod regression;
4mod transactions;
5pub mod types;
6use std::fs::File;
7use std::io::Write;
8
9use solana_sdk::account::AccountSharedData;
10use types::Seed;
11
12use crate::regression::regression::FuzzingRegression;
13use crate::transactions::transaction_stats::FuzzingStatistics;
14
15pub use crate::regression::compare::compare_regression_files;
16pub use crate::regression::compare::ComparisonResult;
17
18#[derive(Clone, Default)]
19pub struct TridentFuzzingData {
20 master_seed: Option<String>,
21 metrics: FuzzingStatistics,
22 regression: FuzzingRegression,
23}
24
25impl TridentFuzzingData {
27 pub fn add_to_histogram(&mut self, metric_name: &str, value: f64) {
28 self.metrics.add_to_histogram(metric_name, value);
29 }
30
31 pub fn add_to_accumulator(&mut self, metric_name: &str, value: f64) {
32 self.metrics.add_to_accumulator(metric_name, value);
33 }
34
35 pub fn add_executed_transaction(&mut self, transaction_name: &str) {
36 self.metrics.add_executed_transaction(transaction_name);
37 }
38
39 pub fn add_successful_transaction(&mut self, transaction_name: &str) {
40 self.metrics.add_successful_transaction(transaction_name);
41 }
42
43 pub fn add_failed_transaction(
44 &mut self,
45 transaction_name: &str,
46 error: String,
47 logs: Option<Vec<String>>,
48 ) {
49 self.metrics
50 .add_failed_transaction(transaction_name, error, logs);
51 }
52
53 pub fn add_transaction_panicked(
54 &mut self,
55 transaction_name: &str,
56 seed: Seed,
57 panic: String,
58 logs: Option<Vec<String>>,
59 instruction_inputs: String,
60 ) {
61 self.metrics.add_transaction_panicked(
62 transaction_name,
63 seed,
64 panic,
65 logs,
66 instruction_inputs,
67 );
68 }
69
70 pub fn add_custom_instruction_error(
71 &mut self,
72 transaction_name: &str,
73 error_code: &u32,
74 logs: Option<Vec<String>>,
75 ) {
76 self.metrics
77 .add_custom_instruction_error(transaction_name, error_code, logs);
78 }
79}
80
81impl TridentFuzzingData {
83 pub fn add_to_regression(
84 &mut self,
85 iteration_seed: &str,
86 account_name: &str,
87 account_shared_data: &AccountSharedData,
88 ) {
89 self.regression
90 .add_to_regression(iteration_seed, account_name, account_shared_data);
91 }
92}
93
94impl TridentFuzzingData {
96 pub fn with_master_seed(seed: Seed) -> Self {
97 Self {
98 master_seed: Some(hex::encode(seed)),
99 metrics: FuzzingStatistics::new(),
100 regression: FuzzingRegression::default(),
101 }
102 }
103 pub fn add_master_seed(&mut self, seed: &str) {
104 self.master_seed = Some(seed.to_string());
105 }
106}
107
108impl TridentFuzzingData {
110 pub fn generate(&self) -> std::io::Result<()> {
111 if std::env::var("FUZZING_METRICS").is_ok() {
113 self.metrics.show_table();
114
115 if let Ok(metrics_file_name) = std::env::var("FUZZING_JSON") {
116 self.to_json(&metrics_file_name);
117 }
118
119 if let Ok(dashboard_file_name) = std::env::var("FUZZING_DASHBOARD") {
121 self.to_html(&dashboard_file_name)?;
122 }
123 }
124
125 if let Ok(regression_file_name) = std::env::var("FUZZING_REGRESSION") {
127 self.to_regression_json(®ression_file_name)?;
128 }
129
130 Ok(())
131 }
132
133 fn to_json(&self, path: &str) {
134 let mut file = File::create(path).unwrap();
135
136 let mut metrics_copy = self.metrics.clone();
138 for metric in metrics_copy.custom_metrics.values_mut() {
139 metric.finalize_histogram();
140 }
141
142 let mut json_data = serde_json::Map::new();
144
145 json_data.insert("master_seed".to_string(), self.master_seed.clone().into());
147
148 json_data.insert(
150 "transactions".to_string(),
151 serde_json::to_value(&metrics_copy.transactions).unwrap_or_default(),
152 );
153 json_data.insert(
154 "custom_metrics".to_string(),
155 serde_json::to_value(&metrics_copy.custom_metrics).unwrap_or_default(),
156 );
157
158 if let Ok(state_hash) = self.regression.get_snapshots_hash() {
160 json_data.insert("state_snapshots_hash".to_string(), state_hash.into());
161 }
162
163 let serialized = serde_json::to_string_pretty(&json_data).unwrap();
164 file.write_all(serialized.as_bytes()).unwrap();
165 }
166
167 fn to_html(&self, path: &str) -> std::io::Result<()> {
168 let html_content = self.create_dashboard_html();
169 let mut file = File::create(path)?;
170 file.write_all(html_content.as_bytes())?;
171 Ok(())
172 }
173
174 fn create_dashboard_html(&self) -> String {
176 let template = include_str!("dashboard-template/dashboard_template.html");
177
178 let dashboard_data = self.create_dashboard_data_structure();
180 let json_data = serde_json::to_string_pretty(&dashboard_data).unwrap();
181
182 template.replace("{{JSON_DATA}}", &json_data)
183 }
184 fn create_dashboard_data_structure(&self) -> serde_json::Value {
186 let mut custom_metrics_for_display = self.metrics.custom_metrics.clone();
188 for metric in custom_metrics_for_display.values_mut() {
189 metric.finalize_histogram();
190 }
191 let mut instructions = serde_json::Map::new();
192
193 for (transaction_name, stats) in &self.metrics.transactions {
194 let mut instruction_data = serde_json::Map::new();
195 instruction_data.insert("invoked".to_string(), stats.transaction_invoked.into());
196 instruction_data.insert(
197 "transactions_successful".to_string(),
198 stats.transaction_successful.into(),
199 );
200 instruction_data.insert(
201 "transactions_failed".to_string(),
202 stats.transaction_failed.into(),
203 );
204 instruction_data.insert(
205 "transactions_panicked".to_string(),
206 stats.transaction_panicked.into(),
207 );
208
209 let transactions_errors = stats.transactions_errors.to_dashboard_format();
211 let custom_instruction_errors = stats.custom_instruction_errors.to_dashboard_format();
212 let transactions_panics = stats.transactions_panics.to_dashboard_format();
213
214 instruction_data.insert("transactions_errors".to_string(), transactions_errors);
215 instruction_data.insert(
216 "custom_instruction_errors".to_string(),
217 custom_instruction_errors,
218 );
219 instruction_data.insert("transactions_panics".to_string(), transactions_panics);
220
221 instructions.insert(transaction_name.clone(), instruction_data.into());
222 }
223
224 let mut result = serde_json::Map::new();
225 result.insert("instructions".to_string(), instructions.into());
226 result.insert(
227 "custom_metrics".to_string(),
228 serde_json::to_value(&custom_metrics_for_display).unwrap_or_default(),
229 );
230
231 result.insert("master_seed".to_string(), self.master_seed.clone().into());
233
234 if let Ok(state_hash) = self.regression.get_snapshots_hash() {
236 result.insert("state_snapshots_hash".to_string(), state_hash.into());
237 }
238
239 result.into()
240 }
241
242 fn to_regression_json(&self, path: &str) -> std::io::Result<()> {
243 self.regression.generate(path)
245 }
246}
247
248impl TridentFuzzingData {
249 pub fn _merge(&mut self, other: TridentFuzzingData) {
250 self.metrics.merge_from(&other.metrics);
251 self.regression.merge_from(&other.regression);
252 }
253
254 pub fn get_exit_code(&self) -> i32 {
255 self.metrics.get_exit_code()
256 }
257}