Skip to main content

mobench_report/
model.rs

1use mobench_domain::{BoundRunReportV2, ReportCounts};
2use serde::{Deserialize, Serialize};
3use std::path::PathBuf;
4
5/// Canonical compatibility summary consumed by every report adapter.
6///
7/// `T` is the caller's platform type. Keeping it generic lets the report
8/// Module remain independent of CLI parsing while preserving the released
9/// serialized target representation.
10#[derive(Debug, Serialize, Deserialize, Clone)]
11pub struct SummaryReport<T> {
12    pub generated_at: String,
13    pub generated_at_unix: u64,
14    pub target: T,
15    pub function: String,
16    pub iterations: u32,
17    pub warmup: u32,
18    pub devices: Vec<String>,
19    pub device_summaries: Vec<DeviceSummary>,
20}
21
22/// Results attributed to one device.
23#[derive(Debug, Serialize, Deserialize, Clone)]
24pub struct DeviceSummary {
25    pub device: String,
26    pub benchmarks: Vec<BenchmarkStats>,
27}
28
29/// Canonical statistics and diagnostics for one benchmark function.
30#[derive(Debug, Serialize, Deserialize, Clone)]
31pub struct BenchmarkStats {
32    pub function: String,
33    pub samples: usize,
34    pub mean_ns: Option<u64>,
35    pub median_ns: Option<u64>,
36    pub p95_ns: Option<u64>,
37    pub min_ns: Option<u64>,
38    pub max_ns: Option<u64>,
39    #[serde(skip_serializing_if = "Option::is_none")]
40    pub resource_usage: Option<BenchmarkResourceUsage>,
41    #[serde(skip_serializing_if = "Option::is_none")]
42    pub failure: Option<BenchmarkFailureStats>,
43}
44
45/// Stable failure projection used by compatibility and CI reports.
46#[derive(Debug, Serialize, Deserialize, Clone)]
47pub struct BenchmarkFailureStats {
48    pub kind: String,
49    pub message: String,
50    #[serde(skip_serializing_if = "Option::is_none")]
51    pub elapsed_ms: Option<u64>,
52    #[serde(skip_serializing_if = "Option::is_none")]
53    pub exit_reason: Option<String>,
54}
55
56/// Canonical resource statistics associated with a benchmark.
57#[derive(Debug, Serialize, Deserialize, Clone, Default)]
58pub struct BenchmarkResourceUsage {
59    pub cpu_total_ms: Option<u64>,
60    pub cpu_median_ms: Option<u64>,
61    /// Legacy alias for `peak_memory_growth_kb`.
62    #[serde(skip_serializing_if = "Option::is_none")]
63    pub peak_memory_kb: Option<u64>,
64    #[serde(skip_serializing_if = "Option::is_none")]
65    pub peak_memory_growth_kb: Option<u64>,
66    #[serde(skip_serializing_if = "Option::is_none")]
67    pub process_peak_memory_kb: Option<u64>,
68    #[serde(skip_serializing_if = "Option::is_none")]
69    pub total_pss_kb: Option<u64>,
70    #[serde(skip_serializing_if = "Option::is_none")]
71    pub private_dirty_kb: Option<u64>,
72    #[serde(skip_serializing_if = "Option::is_none")]
73    pub native_heap_kb: Option<u64>,
74    #[serde(skip_serializing_if = "Option::is_none")]
75    pub java_heap_kb: Option<u64>,
76}
77
78impl BenchmarkResourceUsage {
79    #[must_use]
80    pub fn peak_memory_growth_or_legacy_kb(&self) -> Option<u64> {
81        self.peak_memory_growth_kb.or(self.peak_memory_kb)
82    }
83
84    #[must_use]
85    pub fn is_empty(&self) -> bool {
86        self.cpu_total_ms.is_none()
87            && self.cpu_median_ms.is_none()
88            && self.peak_memory_kb.is_none()
89            && self.peak_memory_growth_kb.is_none()
90            && self.process_peak_memory_kb.is_none()
91            && self.total_pss_kb.is_none()
92            && self.private_dirty_kb.is_none()
93            && self.native_heap_kb.is_none()
94            && self.java_heap_kb.is_none()
95    }
96}
97
98/// Command-level terminal state included in the canonical v2 report.
99#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
100#[serde(tag = "status", rename_all = "snake_case")]
101pub enum RunOutcome {
102    Complete {
103        expected_sessions: usize,
104        successful_sessions: usize,
105    },
106    Partial {
107        expected_sessions: usize,
108        successful_sessions: usize,
109    },
110    Failed {
111        expected_sessions: usize,
112        successful_sessions: usize,
113    },
114}
115
116impl RunOutcome {
117    #[must_use]
118    pub const fn is_complete(self) -> bool {
119        matches!(self, Self::Complete { .. })
120    }
121}
122
123/// Canonical authenticated report published as `summary.v2.json`.
124#[derive(Debug, Serialize)]
125pub struct CanonicalSummaryV2<'a, T> {
126    pub schema_version: &'static str,
127    pub run_id: &'a str,
128    pub target: T,
129    pub function_id: &'a str,
130    pub requested: ReportCounts,
131    pub lifecycle: RunOutcome,
132    pub reports: &'a [BoundRunReportV2],
133}
134
135impl<'a, T> CanonicalSummaryV2<'a, T> {
136    #[must_use]
137    pub fn new(
138        run_id: &'a str,
139        target: T,
140        function_id: &'a str,
141        requested: ReportCounts,
142        lifecycle: RunOutcome,
143        reports: &'a [BoundRunReportV2],
144    ) -> Self {
145        Self {
146            schema_version: "mobench.summary/v2",
147            run_id,
148            target,
149            function_id,
150            requested,
151            lifecycle,
152            reports,
153        }
154    }
155}
156
157/// Deterministic comparison between two canonical summaries.
158#[derive(Debug, Serialize, Clone)]
159pub struct CompareReport {
160    pub baseline: PathBuf,
161    pub candidate: PathBuf,
162    pub rows: Vec<CompareRow>,
163}
164
165#[derive(Debug, Serialize, Clone)]
166pub struct CompareRow {
167    pub device: String,
168    pub function: String,
169    pub baseline_median_ns: Option<u64>,
170    pub candidate_median_ns: Option<u64>,
171    pub median_delta_pct: Option<f64>,
172    pub median_label: String,
173    pub baseline_p95_ns: Option<u64>,
174    pub candidate_p95_ns: Option<u64>,
175    pub p95_delta_pct: Option<f64>,
176    pub p95_label: String,
177}
178
179/// One metric whose candidate value crosses the configured regression gate.
180#[derive(Debug, Clone, PartialEq)]
181pub struct RegressionFinding {
182    pub device: String,
183    pub function: String,
184    pub metric: String,
185    pub delta_pct: f64,
186}