fuchsia_inspect_bench_utils/
lib.rs1use fidl_fuchsia_inspect::{TreeMarker, TreeProxy};
6use fuchsia_async::{Scope, Task};
7use fuchsia_criterion::{FuchsiaCriterion, criterion};
8use fuchsia_inspect::Inspector;
9use fuchsia_inspect::hierarchy::{DiagnosticsHierarchy, DiagnosticsHierarchyGetter};
10use futures::{FutureExt, future};
11use inspect_runtime::TreeServerSendPreference;
12use inspect_runtime::service::handle_request_stream;
13use rand::distr::Uniform;
14use rand::rngs::StdRng;
15use rand::{Rng, SeedableRng};
16use std::borrow::Cow;
17use std::collections::{HashSet, VecDeque};
18use std::future::IntoFuture;
19use std::time::Duration;
20
21pub fn spawn_server(
23 inspector: Inspector,
24) -> Result<(TreeProxy, Task<Result<(), anyhow::Error>>), anyhow::Error> {
25 let (tree, request_stream) = fidl::endpoints::create_proxy_and_stream::<TreeMarker>();
26 let scope = Scope::new_with_name("tree_server_scope");
27 let tree_server_task = scope.compute(handle_request_stream(
28 inspector,
29 TreeServerSendPreference::default(),
30 request_stream,
31 scope.new_child_with_name("tree_server"),
32 ));
33 Ok((tree, Task::spawn(future::join(tree_server_task, scope.into_future()).map(|(r, _)| r))))
34}
35
36pub struct CriterionConfig {
37 pub warm_up_time: Duration,
38 pub measurement_time: Duration,
39 pub sample_size: usize,
40}
41
42impl Default for CriterionConfig {
43 fn default() -> CriterionConfig {
44 CriterionConfig {
45 warm_up_time: Duration::from_millis(1),
46 measurement_time: Duration::from_millis(100),
47 sample_size: 10,
48 }
49 }
50}
51
52pub fn configured_criterion(config: CriterionConfig) -> FuchsiaCriterion {
53 let CriterionConfig { warm_up_time, measurement_time, sample_size } = config;
54 let mut c = FuchsiaCriterion::default();
55 let internal_c: &mut criterion::Criterion = &mut c;
56 *internal_c = std::mem::take(internal_c)
57 .warm_up_time(warm_up_time)
58 .measurement_time(measurement_time)
59 .sample_size(sample_size);
63 c
64}
65
66pub struct InspectHierarchyGenerator<R: SeedableRng + Rng> {
67 rng: R,
68 pub inspector: Inspector,
69}
70
71impl<R: SeedableRng + Rng> InspectHierarchyGenerator<R> {
72 pub fn new(rng: R, inspector: Inspector) -> Self {
73 Self { rng, inspector }
74 }
75
76 fn generate_prufer_sequence(&mut self, n: usize) -> Vec<usize> {
78 (0..n - 2).map(|_| self.rng.sample(Uniform::new(0, n).unwrap())).collect()
79 }
80
81 fn generate_hierarchy_from_prufer(&mut self, sequence: &[usize]) {
83 let n = sequence.len() + 2;
84 let mut degree = vec![1; n];
85
86 for node in sequence {
87 degree[*node] += 1;
88 }
89
90 let mut ptr = 0;
91 while ptr < n && degree[ptr] != 1 {
92 ptr += 1;
93 }
94 let mut leaf = ptr;
95
96 let mut edges: Vec<Vec<usize>> = vec![Vec::new(); n];
97 for v in sequence {
98 edges[leaf].push(*v);
99 edges[*v].push(leaf);
100
101 degree[*v] -= 1;
102 if degree[*v] == 1 && *v < ptr {
103 leaf = *v;
104 } else {
105 ptr += 1;
106 while ptr < n && degree[ptr] != 1 {
107 ptr += 1;
108 }
109 leaf = ptr;
110 }
111 }
112 edges[leaf].push(n - 1);
113 edges[n - 1].push(leaf);
114
115 let mut unprocessed_nodes = VecDeque::new();
119 unprocessed_nodes.push_back((0, self.inspector.root().clone_weak()));
120
121 let mut processed_edges: HashSet<(usize, usize)> = HashSet::with_capacity(n - 1);
122
123 while let Some((u, node)) = unprocessed_nodes.pop_front() {
124 for v in &edges[u] {
125 if processed_edges.contains(&(*v, u)) {
127 continue;
128 }
129 processed_edges.insert((u, *v));
131
132 let child_node = node.create_child(format!("child_{}", *v));
133
134 child_node.record_uint("value", *v as u64);
136 unprocessed_nodes.push_back((*v, child_node.clone_weak()));
137 node.record(child_node);
138 }
139 }
140 }
141
142 pub fn generate_hierarchy(&mut self, n: usize) {
144 let prufer_sequence = self.generate_prufer_sequence(n);
145 self.generate_hierarchy_from_prufer(&prufer_sequence);
146 }
147}
148
149impl<R: SeedableRng + Rng> DiagnosticsHierarchyGetter<String> for InspectHierarchyGenerator<R> {
150 async fn get_diagnostics_hierarchy<'a>(&'a self) -> Cow<'_, DiagnosticsHierarchy>
151 where
152 String: 'a,
153 {
154 self.inspector.get_diagnostics_hierarchy().await
155 }
156}
157
158pub fn filled_hierarchy_generator(seed: u64, size: usize) -> InspectHierarchyGenerator<StdRng> {
160 assert!(size > 1, "Generator requires a size greater than 1");
161 let inspector = Inspector::default();
162 let mut hierarchy_generator =
163 InspectHierarchyGenerator::new(StdRng::seed_from_u64(seed), inspector);
164 hierarchy_generator.generate_hierarchy(size);
165 hierarchy_generator
166}
167
168#[cfg(test)]
169mod tests {
170 use super::*;
171 use diagnostics_assertions::assert_json_diff;
172
173 #[fuchsia::test]
174 async fn random_generated_hierarchy_is_reproducible() {
175 let hierarchy_generator = filled_hierarchy_generator(0, 10);
176 assert_json_diff!(
177 hierarchy_generator,
178 root: {
179 child_6: {
180 value: 6,
181 child_5: {
182 value: 5,
183 child_3: {
184 value: 3
185 },
186 child_9: {
187 value: 9
188 }
189 }
190 },
191 child_8: {
192 value: 8,
193 child_1: {
194 value: 1
195 },
196 child_7: {
197 value: 7,
198 child_2: {
199 value: 2
200 },
201 child_4: {
202 value: 4
203 }
204 }
205 }
206 });
207 }
208}