cbtop/golden_trace/
types.rs1use serde::{Deserialize, Serialize};
4use std::collections::HashMap;
5
6#[derive(Debug, Clone, PartialEq)]
8pub enum GoldenTraceError {
9 NoBaseline,
11 IoError(String),
13 ParseError(String),
15 InvalidTrace(String),
17 VersionMismatch { expected: String, actual: String },
19}
20
21impl std::fmt::Display for GoldenTraceError {
22 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
23 match self {
24 Self::NoBaseline => write!(f, "No golden baseline exists"),
25 Self::IoError(msg) => write!(f, "IO error: {}", msg),
26 Self::ParseError(msg) => write!(f, "Parse error: {}", msg),
27 Self::InvalidTrace(msg) => write!(f, "Invalid trace: {}", msg),
28 Self::VersionMismatch { expected, actual } => {
29 write!(f, "Version mismatch: expected {}, got {}", expected, actual)
30 }
31 }
32 }
33}
34
35impl std::error::Error for GoldenTraceError {}
36
37pub type GoldenTraceResult<T> = Result<T, GoldenTraceError>;
39
40#[derive(Debug, Clone, Serialize, Deserialize, Default)]
42pub struct SyscallBreakdown {
43 pub read_count: u64,
45 pub write_count: u64,
47 pub mmap_count: u64,
49 pub futex_count: u64,
51 pub other_count: u64,
53}
54
55impl SyscallBreakdown {
56 pub fn new() -> Self {
58 Self::default()
59 }
60
61 pub fn total(&self) -> u64 {
63 self.read_count + self.write_count + self.mmap_count + self.futex_count + self.other_count
64 }
65
66 pub fn percentage_diff(&self, baseline: &SyscallBreakdown) -> SyscallBreakdownDelta {
68 SyscallBreakdownDelta {
69 read_delta: Self::calc_delta(self.read_count, baseline.read_count),
70 write_delta: Self::calc_delta(self.write_count, baseline.write_count),
71 mmap_delta: Self::calc_delta(self.mmap_count, baseline.mmap_count),
72 futex_delta: Self::calc_delta(self.futex_count, baseline.futex_count),
73 other_delta: Self::calc_delta(self.other_count, baseline.other_count),
74 total_delta: Self::calc_delta(self.total(), baseline.total()),
75 }
76 }
77
78 fn calc_delta(current: u64, baseline: u64) -> f64 {
79 if baseline == 0 {
80 if current == 0 {
81 0.0
82 } else {
83 100.0 }
85 } else {
86 ((current as f64 - baseline as f64) / baseline as f64) * 100.0
87 }
88 }
89}
90
91#[derive(Debug, Clone)]
93pub struct SyscallBreakdownDelta {
94 pub read_delta: f64,
96 pub write_delta: f64,
98 pub mmap_delta: f64,
100 pub futex_delta: f64,
102 pub other_delta: f64,
104 pub total_delta: f64,
106}
107
108impl SyscallBreakdownDelta {
109 pub fn max_delta(&self) -> f64 {
111 self.read_delta
112 .abs()
113 .max(self.write_delta.abs())
114 .max(self.mmap_delta.abs())
115 .max(self.futex_delta.abs())
116 .max(self.other_delta.abs())
117 }
118
119 pub fn exceeds_threshold(&self, threshold_percent: f64) -> bool {
121 self.read_delta.abs() > threshold_percent
122 || self.write_delta.abs() > threshold_percent
123 || self.mmap_delta.abs() > threshold_percent
124 || self.futex_delta.abs() > threshold_percent
125 || self.other_delta.abs() > threshold_percent
126 }
127}
128
129#[derive(Debug, Clone, Serialize, Deserialize)]
131pub struct TraceMetrics {
132 pub total_time_us: f64,
134 pub p50_latency_us: f64,
136 pub p99_latency_us: f64,
138 pub throughput: f64,
140 pub peak_memory_bytes: u64,
142 pub syscalls: SyscallBreakdown,
144 #[serde(default)]
146 pub custom: HashMap<String, f64>,
147}
148
149impl Default for TraceMetrics {
150 fn default() -> Self {
151 Self {
152 total_time_us: 0.0,
153 p50_latency_us: 0.0,
154 p99_latency_us: 0.0,
155 throughput: 0.0,
156 peak_memory_bytes: 0,
157 syscalls: SyscallBreakdown::default(),
158 custom: HashMap::new(),
159 }
160 }
161}
162
163impl TraceMetrics {
164 pub fn new() -> Self {
166 Self::default()
167 }
168
169 pub fn total_time_us(mut self, us: f64) -> Self {
171 self.total_time_us = us;
172 self
173 }
174
175 pub fn p50_latency_us(mut self, us: f64) -> Self {
177 self.p50_latency_us = us;
178 self
179 }
180
181 pub fn p99_latency_us(mut self, us: f64) -> Self {
183 self.p99_latency_us = us;
184 self
185 }
186
187 pub fn throughput(mut self, ops_per_sec: f64) -> Self {
189 self.throughput = ops_per_sec;
190 self
191 }
192
193 pub fn peak_memory_bytes(mut self, bytes: u64) -> Self {
195 self.peak_memory_bytes = bytes;
196 self
197 }
198
199 pub fn syscalls(mut self, syscalls: SyscallBreakdown) -> Self {
201 self.syscalls = syscalls;
202 self
203 }
204
205 pub fn with_custom(mut self, key: &str, value: f64) -> Self {
207 self.custom.insert(key.to_string(), value);
208 self
209 }
210
211 pub fn is_valid(&self) -> bool {
213 self.total_time_us >= 0.0
214 && self.p50_latency_us >= 0.0
215 && self.p99_latency_us >= 0.0
216 && self.throughput >= 0.0
217 && !self.total_time_us.is_nan()
218 && !self.p50_latency_us.is_nan()
219 && !self.p99_latency_us.is_nan()
220 && !self.throughput.is_nan()
221 }
222}