1use std::{collections::HashMap, fs::File, path::Path};
2
3use aggregate::{PROOF_TIME_LABEL, PROVE_EXCL_TRACE_TIME_LABEL, TRACE_GEN_TIME_LABEL};
4use eyre::Result;
5use memmap2::Mmap;
6
7use crate::{
8 aggregate::{EXECUTE_METERED_TIME_LABEL, EXECUTE_PREFLIGHT_TIME_LABEL},
9 types::{Labels, Metric, MetricDb, MetricsFile},
10};
11
12pub mod aggregate;
13pub mod summary;
14pub mod types;
15
16impl MetricDb {
17 pub fn new(metrics_file: impl AsRef<Path>) -> Result<Self> {
18 let file = File::open(metrics_file)?;
19 let mmap = unsafe { Mmap::map(&file)? };
22 let metrics: MetricsFile = serde_json::from_slice(&mmap)?;
23
24 let mut db = MetricDb::default();
25
26 for entry in metrics.counter {
28 if entry.value == 0.0 {
29 continue;
30 }
31 let labels = Labels::from(entry.labels);
32 db.add_to_flat_dict(labels, entry.metric, entry.value);
33 }
34
35 for entry in metrics.gauge {
37 let labels = Labels::from(entry.labels);
38 db.add_to_flat_dict(labels, entry.metric, entry.value);
39 }
40
41 db.apply_aggregations();
42 db.separate_by_label_types();
43
44 Ok(db)
45 }
46
47 pub fn apply_aggregations(&mut self) {
49 for metrics in self.flat_dict.values_mut() {
50 let get = |key: &str| metrics.iter().find(|m| m.name == key).map(|m| m.value);
51 let total_proof_time = get(PROOF_TIME_LABEL);
52 if total_proof_time.is_some() {
53 continue;
55 }
56 let execute_metered_time = get(EXECUTE_METERED_TIME_LABEL);
58 let execute_preflight_time = get(EXECUTE_PREFLIGHT_TIME_LABEL);
59 let trace_gen_time = get(TRACE_GEN_TIME_LABEL);
60 let prove_excl_trace_time = get(PROVE_EXCL_TRACE_TIME_LABEL);
61 if let (
62 Some(execute_preflight_time),
63 Some(trace_gen_time),
64 Some(prove_excl_trace_time),
65 ) = (
66 execute_preflight_time,
67 trace_gen_time,
68 prove_excl_trace_time,
69 ) {
70 let total_time = execute_metered_time.unwrap_or(0.0)
71 + execute_preflight_time
72 + trace_gen_time
73 + prove_excl_trace_time;
74 metrics.push(Metric::new(PROOF_TIME_LABEL.to_string(), total_time));
75 }
76 }
77 }
78
79 pub fn add_to_flat_dict(&mut self, labels: Labels, metric: String, value: f64) {
80 self.flat_dict
81 .entry(labels)
82 .or_default()
83 .push(Metric::new(metric, value));
84 }
85
86 pub fn custom_sort_label_keys(label_keys: &mut [String]) {
89 label_keys.sort_by_key(|key| {
91 if key == "group" {
92 (0, key.clone()) } else {
94 (1, key.clone()) }
96 });
97 }
98
99 pub fn separate_by_label_types(&mut self) {
100 self.dict_by_label_types.clear();
101
102 for (labels, metrics) in &self.flat_dict {
103 let mut label_keys: Vec<String> = labels.0.iter().map(|(key, _)| key.clone()).collect();
105 Self::custom_sort_label_keys(&mut label_keys);
106
107 let label_dict: HashMap<String, String> = labels.0.iter().cloned().collect();
109
110 let label_values: Vec<String> = label_keys
111 .iter()
112 .map(|key| {
113 label_dict
114 .get(key)
115 .unwrap_or_else(|| panic!("Label key '{}' should exist in label_dict", key))
116 .clone()
117 })
118 .collect();
119
120 self.dict_by_label_types
122 .entry(label_keys)
123 .or_default()
124 .entry(label_values)
125 .or_default()
126 .extend(metrics.clone());
127 }
128 }
129
130 pub fn generate_markdown_tables(&self) -> String {
131 let mut markdown_output = String::new();
132 let mut sorted_keys: Vec<_> = self.dict_by_label_types.keys().cloned().collect();
134 sorted_keys.sort();
135
136 for label_keys in sorted_keys {
137 if label_keys.contains(&"cycle_tracker_span".to_string()) {
138 continue;
141 }
142 let metrics_dict = &self.dict_by_label_types[&label_keys];
143 let mut metric_names: Vec<String> = metrics_dict
144 .values()
145 .flat_map(|metrics| metrics.iter().map(|m| m.name.clone()))
146 .collect::<std::collections::HashSet<_>>()
147 .into_iter()
148 .collect();
149 metric_names.sort_by(|a, b| b.cmp(a));
150
151 let header = format!(
153 "| {} | {} |",
154 label_keys.join(" | "),
155 metric_names.join(" | ")
156 );
157
158 let separator = "| ".to_string()
159 + &vec!["---"; label_keys.len() + metric_names.len()].join(" | ")
160 + " |";
161
162 markdown_output.push_str(&header);
163 markdown_output.push('\n');
164 markdown_output.push_str(&separator);
165 markdown_output.push('\n');
166
167 for (label_values, metrics) in metrics_dict {
169 let mut row = String::new();
170 row.push_str("| ");
171 row.push_str(&label_values.join(" | "));
172 row.push_str(" | ");
173
174 for metric_name in &metric_names {
176 let metric_value = metrics
177 .iter()
178 .find(|m| &m.name == metric_name)
179 .map(|m| Self::format_number(m.value))
180 .unwrap_or_default();
181
182 row.push_str(&format!("{} | ", metric_value));
183 }
184
185 markdown_output.push_str(&row);
186 markdown_output.push('\n');
187 }
188
189 markdown_output.push('\n');
190 }
191
192 markdown_output
193 }
194}