1use mobench_domain::{BoundRunReportV2, ReportCounts};
2use serde::{Deserialize, Serialize};
3use std::path::PathBuf;
4
5#[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#[derive(Debug, Serialize, Deserialize, Clone)]
24pub struct DeviceSummary {
25 pub device: String,
26 pub benchmarks: Vec<BenchmarkStats>,
27}
28
29#[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#[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#[derive(Debug, Serialize, Deserialize, Clone, Default)]
58pub struct BenchmarkResourceUsage {
59 pub cpu_total_ms: Option<u64>,
60 pub cpu_median_ms: Option<u64>,
61 #[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#[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#[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#[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#[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}