1#![warn(clippy::unwrap_used)]
5
6use anyhow::{Result, anyhow};
7use fidl_fuchsia_kernel as fkernel;
8use fuchsia_inspect::{ArrayProperty, Inspector};
9use futures::FutureExt;
10use log::debug;
11use stalls::StallProvider;
12use stalls::refaults::RefaultProvider;
13use std::future::Future;
14
15pub fn serve(
17 kernel_stats_proxy: fkernel::StatsProxy,
18 stall_provider: impl StallProvider,
19 refault_provider: impl RefaultProvider,
20) -> Result<impl Future<Output = ()>> {
21 debug!("Start serving inspect tree.");
22
23 let inspector = fuchsia_inspect::component::inspector();
26
27 let inspect_controller =
29 inspect_runtime::publish(inspector, inspect_runtime::PublishOptions::default())
30 .ok_or_else(|| anyhow!("Failed to serve server handling `fuchsia.inspect.Tree`"))?;
31
32 build_inspect_tree(kernel_stats_proxy, stall_provider, refault_provider, inspector);
33 Ok(inspect_controller)
34}
35
36fn build_inspect_tree(
37 kernel_stats_proxy: fkernel::StatsProxy,
38 stall_provider: impl StallProvider,
39 refault_provider: impl RefaultProvider,
40 inspector: &Inspector,
41) {
42 {
44 let kernel_stats_proxy = kernel_stats_proxy.clone();
45 inspector.root().record_lazy_child("kmem_stats", move || {
46 let kernel_stats_proxy = kernel_stats_proxy.clone();
47 async move {
48 let inspector = Inspector::default();
49 let root = inspector.root();
50 let mem_stats = kernel_stats_proxy.get_memory_stats().await?;
51 mem_stats.total_bytes.map(|v| root.record_uint("total_bytes", v));
52 mem_stats.free_bytes.map(|v| root.record_uint("free_bytes", v));
53 mem_stats.free_loaned_bytes.map(|v| root.record_uint("free_loaned_bytes", v));
54 mem_stats.wired_bytes.map(|v| root.record_uint("wired_bytes", v));
55 mem_stats.total_heap_bytes.map(|v| root.record_uint("total_heap_bytes", v));
56 mem_stats.free_heap_bytes.map(|v| root.record_uint("free_heap_bytes", v));
57 mem_stats.vmo_bytes.map(|v| root.record_uint("vmo_bytes", v));
58 mem_stats.mmu_overhead_bytes.map(|v| root.record_uint("mmu_overhead_bytes", v));
59 mem_stats.ipc_bytes.map(|v| root.record_uint("ipc_bytes", v));
60 mem_stats.cache_bytes.map(|v| root.record_uint("cache_bytes", v));
61 mem_stats.slab_bytes.map(|v| root.record_uint("slab_bytes", v));
62 mem_stats.zram_bytes.map(|v| root.record_uint("zram_bytes", v));
63 mem_stats.other_bytes.map(|v| root.record_uint("other_bytes", v));
64 mem_stats
65 .vmo_reclaim_total_bytes
66 .map(|v| root.record_uint("vmo_reclaim_total_bytes", v));
67 mem_stats
68 .vmo_reclaim_newest_bytes
69 .map(|v| root.record_uint("vmo_reclaim_newest_bytes", v));
70 mem_stats
71 .vmo_reclaim_oldest_bytes
72 .map(|v| root.record_uint("vmo_reclaim_oldest_bytes", v));
73 mem_stats
74 .vmo_reclaim_disabled_bytes
75 .map(|v| root.record_uint("vmo_reclaim_disabled_bytes", v));
76 mem_stats
77 .vmo_discardable_locked_bytes
78 .map(|v| root.record_uint("vmo_discardable_locked_bytes", v));
79 mem_stats
80 .vmo_discardable_unlocked_bytes
81 .map(|v| root.record_uint("vmo_discardable_unlocked_bytes", v));
82 Ok(inspector)
83 }
84 .boxed()
85 })
86 };
87
88 {
89 inspector.root().record_lazy_child("kmem_stats_compression", move || {
90 let kernel_stats_proxy = kernel_stats_proxy.clone();
91 async move {
92 let inspector = Inspector::default();
93 let cmp_stats = kernel_stats_proxy.get_memory_stats_compression().await?;
94 cmp_stats
95 .uncompressed_storage_bytes
96 .map(|v| inspector.root().record_uint("uncompressed_storage_bytes", v));
97 cmp_stats
98 .compressed_storage_bytes
99 .map(|v| inspector.root().record_uint("compressed_storage_bytes", v));
100 cmp_stats
101 .compressed_fragmentation_bytes
102 .map(|v| inspector.root().record_uint("compressed_fragmentation_bytes", v));
103 cmp_stats
104 .compression_time
105 .map(|v| inspector.root().record_int("compression_time", v));
106 cmp_stats
107 .decompression_time
108 .map(|v| inspector.root().record_int("decompression_time", v));
109 cmp_stats
110 .total_page_compression_attempts
111 .map(|v| inspector.root().record_uint("total_page_compression_attempts", v));
112 cmp_stats
113 .failed_page_compression_attempts
114 .map(|v| inspector.root().record_uint("failed_page_compression_attempts", v));
115 cmp_stats
116 .total_page_decompressions
117 .map(|v| inspector.root().record_uint("total_page_decompressions", v));
118 cmp_stats
119 .compressed_page_evictions
120 .map(|v| inspector.root().record_uint("compressed_page_evictions", v));
121 cmp_stats
122 .eager_page_compressions
123 .map(|v| inspector.root().record_uint("eager_page_compressions", v));
124 cmp_stats
125 .memory_pressure_page_compressions
126 .map(|v| inspector.root().record_uint("memory_pressure_page_compressions", v));
127 cmp_stats
128 .critical_memory_page_compressions
129 .map(|v| inspector.root().record_uint("critical_memory_page_compressions", v));
130 cmp_stats
131 .pages_decompressed_unit_ns
132 .map(|v| inspector.root().record_uint("pages_decompressed_unit_ns", v));
133 cmp_stats.pages_decompressed_within_log_time.map(|v| {
134 let array =
135 inspector.root().create_uint_array("pages_decompressed_within_log_time", 8);
136 array.set(0, v[0]);
138 array.set(1, v[1]);
139 array.set(2, v[2]);
140 array.set(3, v[3]);
141 array.set(4, v[4]);
142 array.set(5, v[5]);
143 array.set(6, v[6]);
144 array.set(7, v[7]);
145 inspector.root().record(array);
146 });
147 Ok(inspector)
148 }
149 .boxed()
150 });
151 }
152
153 {
154 inspector.root().record_lazy_child("stalls", move || {
155 let stall_provider = stall_provider.clone();
156 async move {
157 let stall_info = stall_provider.get_stall_info()?;
158 let inspector = Inspector::default();
159 inspector.root().record_uint(
160 "some_ms",
161 stall_info.some.as_millis().try_into().unwrap_or(u64::MAX),
162 );
163 inspector.root().record_uint(
164 "full_ms",
165 stall_info.full.as_millis().try_into().unwrap_or(u64::MAX),
166 );
167 Ok(inspector)
168 }
169 .boxed()
170 });
171 }
172
173 {
174 inspector.root().record_lazy_child("refaults", move || {
175 let refault_provider = refault_provider.clone();
176 async move {
177 let refault_info = refault_provider.get_count();
178 let inspector = Inspector::default();
179 inspector.root().record_uint("count", refault_info);
180 Ok(inspector)
181 }
182 .boxed()
183 });
184 }
185}
186
187#[cfg(test)]
188mod tests {
189 #![allow(clippy::unwrap_used)]
190 use super::*;
191 use diagnostics_assertions::assert_data_tree;
192 use fuchsia_async as fasync;
193 use futures::TryStreamExt;
194 use stalls::MemoryStallMetrics;
195 use std::time::Duration;
196
197 async fn serve_kernel_stats(
198 mut request_stream: fkernel::StatsRequestStream,
199 ) -> Result<(), fidl::Error> {
200 while let Some(request) = request_stream.try_next().await? {
201 match request {
202 fkernel::StatsRequest::GetMemoryStats { responder } => {
203 responder
204 .send(&fkernel::MemoryStats {
205 total_bytes: Some(1),
206 free_bytes: Some(2),
207 wired_bytes: Some(3),
208 total_heap_bytes: Some(4),
209 free_heap_bytes: Some(5),
210 vmo_bytes: Some(6),
211 mmu_overhead_bytes: Some(7),
212 ipc_bytes: Some(8),
213 other_bytes: Some(9),
214 free_loaned_bytes: Some(10),
215 cache_bytes: Some(11),
216 slab_bytes: Some(12),
217 zram_bytes: Some(13),
218 vmo_reclaim_total_bytes: Some(14),
219 vmo_reclaim_newest_bytes: Some(15),
220 vmo_reclaim_oldest_bytes: Some(16),
221 vmo_reclaim_disabled_bytes: Some(17),
222 vmo_discardable_locked_bytes: Some(18),
223 vmo_discardable_unlocked_bytes: Some(19),
224 ..Default::default()
225 })
226 .unwrap();
227 }
228 fkernel::StatsRequest::GetMemoryStatsExtended { responder: _ } => {
229 unimplemented!("Deprecated call, should not be used")
230 }
231 fkernel::StatsRequest::GetMemoryStatsCompression { responder } => {
232 responder
233 .send(&fkernel::MemoryStatsCompression {
234 uncompressed_storage_bytes: Some(20),
235 compressed_storage_bytes: Some(21),
236 compressed_fragmentation_bytes: Some(22),
237 compression_time: Some(23),
238 decompression_time: Some(24),
239 total_page_compression_attempts: Some(25),
240 failed_page_compression_attempts: Some(26),
241 total_page_decompressions: Some(27),
242 compressed_page_evictions: Some(28),
243 eager_page_compressions: Some(29),
244 memory_pressure_page_compressions: Some(30),
245 critical_memory_page_compressions: Some(31),
246 pages_decompressed_unit_ns: Some(32),
247 pages_decompressed_within_log_time: Some([
248 40, 41, 42, 43, 44, 45, 46, 47,
249 ]),
250 ..Default::default()
251 })
252 .unwrap();
253 }
254 fkernel::StatsRequest::GetCpuStats { responder: _ } => unimplemented!(),
255 fkernel::StatsRequest::GetCpuLoad { duration: _, responder: _ } => unimplemented!(),
256 }
257 }
258 Ok(())
259 }
260
261 #[derive(Clone)]
262 struct FakeStallProvider {}
263 impl StallProvider for FakeStallProvider {
264 fn get_stall_info(&self) -> Result<MemoryStallMetrics, anyhow::Error> {
265 Ok(MemoryStallMetrics {
266 some: Duration::from_millis(10),
267 full: Duration::from_millis(20),
268 })
269 }
270 }
271
272 #[derive(Clone)]
273 struct FakeRefaultProvider {}
274 impl RefaultProvider for FakeRefaultProvider {
275 fn get_count(&self) -> u64 {
276 1000
277 }
278 }
279
280 #[test]
281 fn test_build_inspect_tree() {
282 let mut exec = fasync::TestExecutor::new();
283 let (stats_provider, stats_request_stream) =
284 fidl::endpoints::create_proxy_and_stream::<fkernel::StatsMarker>();
285
286 fasync::Task::spawn(async move {
287 serve_kernel_stats(stats_request_stream).await.unwrap();
288 })
289 .detach();
290
291 let inspector = fuchsia_inspect::Inspector::default();
292
293 build_inspect_tree(
294 stats_provider,
295 FakeStallProvider {},
296 FakeRefaultProvider {},
297 &inspector,
298 );
299
300 let output = exec
301 .run_singlethreaded(fuchsia_inspect::reader::read(&inspector))
302 .expect("got hierarchy");
303
304 assert_data_tree!(@executor exec, output, root: {
305 kmem_stats: {
306 total_bytes: 1u64,
307 free_bytes: 2u64,
308 wired_bytes: 3u64,
309 total_heap_bytes: 4u64,
310 free_heap_bytes: 5u64,
311 vmo_bytes: 6u64,
312 mmu_overhead_bytes: 7u64,
313 ipc_bytes: 8u64,
314 other_bytes: 9u64,
315 free_loaned_bytes: 10u64,
316 cache_bytes: 11u64,
317 slab_bytes: 12u64,
318 zram_bytes: 13u64,
319 vmo_reclaim_total_bytes: 14u64,
320 vmo_reclaim_newest_bytes: 15u64,
321 vmo_reclaim_oldest_bytes: 16u64,
322 vmo_reclaim_disabled_bytes: 17u64,
323 vmo_discardable_locked_bytes: 18u64,
324 vmo_discardable_unlocked_bytes: 19u64
325 },
326 kmem_stats_compression: {
327 uncompressed_storage_bytes: 20u64,
328 compressed_storage_bytes: 21u64,
329 compressed_fragmentation_bytes: 22u64,
330 compression_time: 23i64,
331 decompression_time: 24i64,
332 total_page_compression_attempts: 25u64,
333 failed_page_compression_attempts: 26u64,
334 total_page_decompressions: 27u64,
335 compressed_page_evictions: 28u64,
336 eager_page_compressions: 29u64,
337 memory_pressure_page_compressions: 30u64,
338 critical_memory_page_compressions: 31u64,
339 pages_decompressed_unit_ns: 32u64,
340 pages_decompressed_within_log_time: vec![
341 40u64, 41u64, 42u64, 43u64, 44u64, 45u64, 46u64, 47u64,
342 ]
343 },
344 stalls: {
345 some_ms: 10u64,
346 full_ms: 20u64,
347 },
348 refaults: {
349 count: 1000u64,
350 },
351 });
352 }
353}