Skip to main content

cu_profiler_core/budget/
mod.rs

1//! Budget policy engine.
2//!
3//! Given a [`crate::model::Measurement`], a [`BudgetPolicy`], and an optional
4//! baseline figure, [`evaluate`] produces a list of structured [`PolicyResult`]s
5//! — one per clause that the policy actually constrains.
6
7mod policy;
8mod result;
9
10pub use policy::BudgetPolicy;
11pub use result::{PolicyResult, PolicyStatus, Severity};
12
13use crate::model::Measurement;
14
15/// Evaluate every active clause of `policy` against `measurement`.
16///
17/// `baseline_units`, when present, enables the regression clauses.
18#[must_use]
19pub fn evaluate(
20    measurement: &Measurement,
21    policy: &BudgetPolicy,
22    baseline_units: Option<u64>,
23) -> Vec<PolicyResult> {
24    let mut out = Vec::new();
25    let actual = measurement.total_cu;
26
27    if let Some(max) = policy.absolute_max_cu {
28        out.push(eval_absolute(actual, max));
29        if let Some(warn_pct) = policy.warn_at_budget_pct {
30            if let Some(r) = eval_warn_threshold(actual, max, warn_pct) {
31                out.push(r);
32            }
33        }
34        if let Some(min_margin) = policy.min_margin_pct {
35            out.push(eval_min_margin(actual, max, min_margin));
36        }
37    }
38
39    if let Some(base) = baseline_units {
40        if let Some(pct) = policy.max_regression_pct {
41            out.push(eval_regression_pct(actual, base, pct));
42        }
43        if let Some(units) = policy.max_regression_units {
44            out.push(eval_regression_units(actual, base, units));
45        }
46    }
47
48    if let Some(max_count) = policy.max_cpi_count {
49        out.push(eval_cpi_count(measurement.cpi_count, max_count));
50    }
51    if let Some(max_depth) = policy.max_cpi_depth {
52        out.push(eval_cpi_depth(measurement.cpi_depth, max_depth));
53    }
54    if let Some(max_unattr) = policy.max_unattributed_pct {
55        out.push(eval_unattributed(measurement.unattributed_pct, max_unattr));
56    }
57    if let (Some(warn), Some(overhead)) = (
58        policy.instrumentation_overhead_warn_pct,
59        measurement.instrumentation_overhead_pct,
60    ) {
61        out.push(eval_overhead(overhead, warn));
62    }
63
64    out
65}
66
67/// Roll a set of results up into a single worst-case status.
68#[must_use]
69pub fn overall_status(results: &[PolicyResult]) -> PolicyStatus {
70    results
71        .iter()
72        .fold(PolicyStatus::Pass, |acc, r| acc.max(r.status))
73}
74
75fn pct(part: u64, whole: u64) -> f64 {
76    if whole == 0 {
77        0.0
78    } else {
79        (part as f64 / whole as f64) * 100.0
80    }
81}
82
83fn eval_absolute(actual: u64, max: u64) -> PolicyResult {
84    let (status, severity) = if actual > max {
85        (PolicyStatus::Fail, Severity::Error)
86    } else {
87        (PolicyStatus::Pass, Severity::Info)
88    };
89    PolicyResult {
90        policy_id: "absolute_max_cu".into(),
91        status,
92        severity,
93        actual: Some(actual as f64),
94        expected: Some(max as f64),
95        message: format!("{actual} CU against an absolute maximum of {max} CU"),
96        remediation: (status == PolicyStatus::Fail).then(|| {
97            "Reduce hot-path compute: move cheap validation before CPIs and cut event emission."
98                .to_string()
99        }),
100    }
101}
102
103fn eval_warn_threshold(actual: u64, max: u64, warn_pct: f64) -> Option<PolicyResult> {
104    if actual > max {
105        return None; // already covered by the absolute clause as a Fail
106    }
107    let used = pct(actual, max);
108    if used < warn_pct {
109        return None;
110    }
111    Some(PolicyResult {
112        policy_id: "warn_at_budget_pct".into(),
113        status: PolicyStatus::Warn,
114        severity: Severity::Warning,
115        actual: Some(used),
116        expected: Some(warn_pct),
117        message: format!("{used:.1}% of budget used (warns at {warn_pct:.0}%)"),
118        remediation: Some("Approaching the budget ceiling; profile the hottest scope.".into()),
119    })
120}
121
122fn eval_min_margin(actual: u64, max: u64, min_margin: f64) -> PolicyResult {
123    let margin = 100.0 - pct(actual, max);
124    let ok = margin >= min_margin;
125    PolicyResult {
126        policy_id: "min_margin_pct".into(),
127        status: if ok {
128            PolicyStatus::Pass
129        } else {
130            PolicyStatus::Warn
131        },
132        severity: if ok {
133            Severity::Info
134        } else {
135            Severity::Warning
136        },
137        actual: Some(margin),
138        expected: Some(min_margin),
139        message: format!("{margin:.1}% margin below budget (minimum {min_margin:.0}%)"),
140        remediation: (!ok).then(|| "Thin margin; a small regression could breach budget.".into()),
141    }
142}
143
144fn eval_regression_pct(actual: u64, base: u64, max_pct: f64) -> PolicyResult {
145    let delta = actual as i64 - base as i64;
146    let delta_pct = if base == 0 {
147        0.0
148    } else {
149        (delta as f64 / base as f64) * 100.0
150    };
151    let ok = delta_pct <= max_pct;
152    PolicyResult {
153        policy_id: "max_regression_pct".into(),
154        status: if ok {
155            PolicyStatus::Pass
156        } else {
157            PolicyStatus::Fail
158        },
159        severity: if ok { Severity::Info } else { Severity::Error },
160        actual: Some(delta_pct),
161        expected: Some(max_pct),
162        message: format!(
163            "regression {delta_pct:+.2}% vs baseline {base} CU (allowed +{max_pct:.2}%)"
164        ),
165        remediation: (!ok)
166            .then(|| "Inspect the CPI count and recently changed validation path.".into()),
167    }
168}
169
170fn eval_regression_units(actual: u64, base: u64, max_units: u64) -> PolicyResult {
171    let delta = actual as i64 - base as i64;
172    let ok = delta <= max_units as i64;
173    PolicyResult {
174        policy_id: "max_regression_units".into(),
175        status: if ok {
176            PolicyStatus::Pass
177        } else {
178            PolicyStatus::Fail
179        },
180        severity: if ok { Severity::Info } else { Severity::Error },
181        actual: Some(delta as f64),
182        expected: Some(max_units as f64),
183        message: format!("regression {delta:+} CU vs baseline (allowed +{max_units} CU)"),
184        remediation: (!ok)
185            .then(|| "Compute grew beyond the unit budget; bisect recent changes.".into()),
186    }
187}
188
189fn eval_cpi_count(actual: u32, max: u32) -> PolicyResult {
190    let ok = actual <= max;
191    PolicyResult {
192        policy_id: "max_cpi_count".into(),
193        status: if ok {
194            PolicyStatus::Pass
195        } else {
196            PolicyStatus::Fail
197        },
198        severity: if ok { Severity::Info } else { Severity::Error },
199        actual: Some(actual as f64),
200        expected: Some(max as f64),
201        message: format!("{actual} CPIs (maximum {max})"),
202        remediation: (!ok).then(|| "Check for duplicate ATA creation or redundant CPIs.".into()),
203    }
204}
205
206fn eval_cpi_depth(actual: u32, max: u32) -> PolicyResult {
207    let ok = actual <= max;
208    PolicyResult {
209        policy_id: "max_cpi_depth".into(),
210        status: if ok {
211            PolicyStatus::Pass
212        } else {
213            PolicyStatus::Fail
214        },
215        severity: if ok { Severity::Info } else { Severity::Error },
216        actual: Some(actual as f64),
217        expected: Some(max as f64),
218        message: format!("CPI depth {actual} (maximum {max})"),
219        remediation: (!ok).then(|| "Deep CPI nesting risks the runtime invoke-depth limit.".into()),
220    }
221}
222
223fn eval_unattributed(actual_pct: f64, max_pct: f64) -> PolicyResult {
224    let ok = actual_pct <= max_pct;
225    PolicyResult {
226        policy_id: "max_unattributed_pct".into(),
227        status: if ok {
228            PolicyStatus::Pass
229        } else {
230            PolicyStatus::Warn
231        },
232        severity: if ok {
233            Severity::Info
234        } else {
235            Severity::Warning
236        },
237        actual: Some(actual_pct),
238        expected: Some(max_pct),
239        message: format!("{actual_pct:.1}% unattributed CU (maximum {max_pct:.0}%)"),
240        remediation: (!ok).then(|| {
241            "Add scope markers around account validation and math to attribute CU.".into()
242        }),
243    }
244}
245
246fn eval_overhead(actual_pct: f64, warn_pct: f64) -> PolicyResult {
247    let ok = actual_pct <= warn_pct;
248    PolicyResult {
249        policy_id: "instrumentation_overhead_warn_pct".into(),
250        status: if ok {
251            PolicyStatus::Pass
252        } else {
253            PolicyStatus::Warn
254        },
255        severity: if ok {
256            Severity::Info
257        } else {
258            Severity::Warning
259        },
260        actual: Some(actual_pct),
261        expected: Some(warn_pct),
262        message: format!("instrumentation overhead {actual_pct:.1}% (warns at {warn_pct:.0}%)"),
263        remediation: (!ok).then(|| "Reduce the number of profiler markers in the hot path.".into()),
264    }
265}
266
267#[cfg(test)]
268mod tests {
269    use super::*;
270    use crate::model::Measurement;
271
272    fn measurement(total: u64) -> Measurement {
273        Measurement {
274            total_cu: total,
275            ..Measurement::empty()
276        }
277    }
278
279    #[test]
280    fn absolute_budget_fails_when_exceeded() {
281        let m = measurement(120_000);
282        let policy = BudgetPolicy {
283            absolute_max_cu: Some(100_000),
284            ..Default::default()
285        };
286        let results = evaluate(&m, &policy, None);
287        assert_eq!(overall_status(&results), PolicyStatus::Fail);
288        assert!(results[0].remediation.is_some());
289    }
290
291    #[test]
292    fn warn_threshold_triggers_below_ceiling() {
293        let m = measurement(96_000);
294        let policy = BudgetPolicy {
295            absolute_max_cu: Some(100_000),
296            warn_at_budget_pct: Some(90.0),
297            ..Default::default()
298        };
299        let results = evaluate(&m, &policy, None);
300        assert_eq!(overall_status(&results), PolicyStatus::Warn);
301    }
302
303    #[test]
304    fn regression_pct_fails_over_allowance() {
305        let m = measurement(96_812);
306        let policy = BudgetPolicy {
307            max_regression_pct: Some(5.0),
308            ..Default::default()
309        };
310        let results = evaluate(&m, &policy, Some(91_204));
311        assert_eq!(overall_status(&results), PolicyStatus::Fail);
312    }
313
314    #[test]
315    fn within_all_limits_passes() {
316        let m = measurement(50_000);
317        let policy = BudgetPolicy {
318            absolute_max_cu: Some(100_000),
319            warn_at_budget_pct: Some(90.0),
320            max_regression_pct: Some(5.0),
321            ..Default::default()
322        };
323        let results = evaluate(&m, &policy, Some(49_500));
324        assert_eq!(overall_status(&results), PolicyStatus::Pass);
325    }
326}