Skip to main content

cbtop/golden_trace/
types.rs

1//! Core types for golden traces: errors, syscall breakdowns, and metrics.
2
3use serde::{Deserialize, Serialize};
4use std::collections::HashMap;
5
6/// Golden trace error
7#[derive(Debug, Clone, PartialEq)]
8pub enum GoldenTraceError {
9    /// No golden trace exists
10    NoBaseline,
11    /// IO error
12    IoError(String),
13    /// Parse error
14    ParseError(String),
15    /// Invalid trace data
16    InvalidTrace(String),
17    /// Version mismatch
18    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
37/// Result type for golden trace operations
38pub type GoldenTraceResult<T> = Result<T, GoldenTraceError>;
39
40/// Syscall breakdown for trace comparison
41#[derive(Debug, Clone, Serialize, Deserialize, Default)]
42pub struct SyscallBreakdown {
43    /// Read syscall count
44    pub read_count: u64,
45    /// Write syscall count
46    pub write_count: u64,
47    /// Mmap syscall count
48    pub mmap_count: u64,
49    /// Futex syscall count
50    pub futex_count: u64,
51    /// Other syscall count
52    pub other_count: u64,
53}
54
55impl SyscallBreakdown {
56    /// Create new breakdown
57    pub fn new() -> Self {
58        Self::default()
59    }
60
61    /// Total syscall count
62    pub fn total(&self) -> u64 {
63        self.read_count + self.write_count + self.mmap_count + self.futex_count + self.other_count
64    }
65
66    /// Calculate percentage difference from baseline
67    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 // New syscalls appeared
84            }
85        } else {
86            ((current as f64 - baseline as f64) / baseline as f64) * 100.0
87        }
88    }
89}
90
91/// Syscall breakdown delta (percentage changes)
92#[derive(Debug, Clone)]
93pub struct SyscallBreakdownDelta {
94    /// Read syscall delta %
95    pub read_delta: f64,
96    /// Write syscall delta %
97    pub write_delta: f64,
98    /// Mmap syscall delta %
99    pub mmap_delta: f64,
100    /// Futex syscall delta %
101    pub futex_delta: f64,
102    /// Other syscall delta %
103    pub other_delta: f64,
104    /// Total syscall delta %
105    pub total_delta: f64,
106}
107
108impl SyscallBreakdownDelta {
109    /// Get maximum absolute delta
110    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    /// Check if any delta exceeds threshold
120    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/// Performance metrics for trace comparison
130#[derive(Debug, Clone, Serialize, Deserialize)]
131pub struct TraceMetrics {
132    /// Total execution time in microseconds
133    pub total_time_us: f64,
134    /// P50 latency in microseconds
135    pub p50_latency_us: f64,
136    /// P99 latency in microseconds
137    pub p99_latency_us: f64,
138    /// Throughput (ops/sec)
139    pub throughput: f64,
140    /// Peak memory usage in bytes
141    pub peak_memory_bytes: u64,
142    /// Syscall breakdown
143    pub syscalls: SyscallBreakdown,
144    /// Custom metrics
145    #[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    /// Create new metrics
165    pub fn new() -> Self {
166        Self::default()
167    }
168
169    /// Builder: set total time
170    pub fn total_time_us(mut self, us: f64) -> Self {
171        self.total_time_us = us;
172        self
173    }
174
175    /// Builder: set P50 latency
176    pub fn p50_latency_us(mut self, us: f64) -> Self {
177        self.p50_latency_us = us;
178        self
179    }
180
181    /// Builder: set P99 latency
182    pub fn p99_latency_us(mut self, us: f64) -> Self {
183        self.p99_latency_us = us;
184        self
185    }
186
187    /// Builder: set throughput
188    pub fn throughput(mut self, ops_per_sec: f64) -> Self {
189        self.throughput = ops_per_sec;
190        self
191    }
192
193    /// Builder: set peak memory
194    pub fn peak_memory_bytes(mut self, bytes: u64) -> Self {
195        self.peak_memory_bytes = bytes;
196        self
197    }
198
199    /// Builder: set syscall breakdown
200    pub fn syscalls(mut self, syscalls: SyscallBreakdown) -> Self {
201        self.syscalls = syscalls;
202        self
203    }
204
205    /// Builder: add custom metric
206    pub fn with_custom(mut self, key: &str, value: f64) -> Self {
207        self.custom.insert(key.to_string(), value);
208        self
209    }
210
211    /// Check if metrics are valid
212    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}