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::PageFaultProvider;
13use std::future::Future;
14
15pub fn serve(
17 kernel_stats_proxy: fkernel::StatsProxy,
18 stall_provider: impl StallProvider,
19 page_fault_provider: impl PageFaultProvider,
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, page_fault_provider, inspector);
33 Ok(inspect_controller)
34}
35
36fn build_inspect_tree(
37 kernel_stats_proxy: fkernel::StatsProxy,
38 stall_provider: impl StallProvider,
39 page_fault_provider: impl PageFaultProvider,
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 let page_fault_provider = page_fault_provider.clone();
175 inspector.root().record_lazy_child("faults", move || {
176 let page_fault_provider = page_fault_provider.clone();
177 async move {
178 let fault_info = page_fault_provider.get_fault_count();
179 let inspector = Inspector::default();
180 inspector.root().record_uint("count", fault_info);
181 Ok(inspector)
182 }
183 .boxed()
184 });
185 }
186
187 {
188 inspector.root().record_lazy_child("refaults", move || {
189 let page_fault_provider = page_fault_provider.clone();
190 async move {
191 let refault_info = page_fault_provider.get_refault_count();
192 let inspector = Inspector::default();
193 inspector.root().record_uint("count", refault_info);
194 Ok(inspector)
195 }
196 .boxed()
197 });
198 }
199}
200
201#[cfg(test)]
202mod tests {
203 #![allow(clippy::unwrap_used)]
204 use super::*;
205 use diagnostics_assertions::assert_data_tree;
206 use fuchsia_async as fasync;
207 use futures::TryStreamExt;
208 use stalls::MemoryStallMetrics;
209 use std::time::Duration;
210
211 async fn serve_kernel_stats(
212 mut request_stream: fkernel::StatsRequestStream,
213 ) -> Result<(), fidl::Error> {
214 while let Some(request) = request_stream.try_next().await? {
215 match request {
216 fkernel::StatsRequest::GetMemoryStats { responder } => {
217 responder
218 .send(&fkernel::MemoryStats {
219 total_bytes: Some(1),
220 free_bytes: Some(2),
221 wired_bytes: Some(3),
222 total_heap_bytes: Some(4),
223 free_heap_bytes: Some(5),
224 vmo_bytes: Some(6),
225 mmu_overhead_bytes: Some(7),
226 ipc_bytes: Some(8),
227 other_bytes: Some(9),
228 free_loaned_bytes: Some(10),
229 cache_bytes: Some(11),
230 slab_bytes: Some(12),
231 zram_bytes: Some(13),
232 vmo_reclaim_total_bytes: Some(14),
233 vmo_reclaim_newest_bytes: Some(15),
234 vmo_reclaim_oldest_bytes: Some(16),
235 vmo_reclaim_disabled_bytes: Some(17),
236 vmo_discardable_locked_bytes: Some(18),
237 vmo_discardable_unlocked_bytes: Some(19),
238 ..Default::default()
239 })
240 .unwrap();
241 }
242 fkernel::StatsRequest::GetMemoryStatsExtended { responder: _ } => {
243 unimplemented!("Deprecated call, should not be used")
244 }
245 fkernel::StatsRequest::GetMemoryStatsCompression { responder } => {
246 responder
247 .send(&fkernel::MemoryStatsCompression {
248 uncompressed_storage_bytes: Some(20),
249 compressed_storage_bytes: Some(21),
250 compressed_fragmentation_bytes: Some(22),
251 compression_time: Some(23),
252 decompression_time: Some(24),
253 total_page_compression_attempts: Some(25),
254 failed_page_compression_attempts: Some(26),
255 total_page_decompressions: Some(27),
256 compressed_page_evictions: Some(28),
257 eager_page_compressions: Some(29),
258 memory_pressure_page_compressions: Some(30),
259 critical_memory_page_compressions: Some(31),
260 pages_decompressed_unit_ns: Some(32),
261 pages_decompressed_within_log_time: Some([
262 40, 41, 42, 43, 44, 45, 46, 47,
263 ]),
264 ..Default::default()
265 })
266 .unwrap();
267 }
268 fkernel::StatsRequest::GetCpuStats { responder: _ } => unimplemented!(),
269 fkernel::StatsRequest::GetCpuLoad { duration: _, responder: _ } => unimplemented!(),
270 }
271 }
272 Ok(())
273 }
274
275 #[derive(Clone)]
276 struct FakeStallProvider {}
277 impl StallProvider for FakeStallProvider {
278 fn get_stall_info(&self) -> Result<MemoryStallMetrics, anyhow::Error> {
279 Ok(MemoryStallMetrics {
280 some: Duration::from_millis(10),
281 full: Duration::from_millis(20),
282 })
283 }
284 }
285
286 #[derive(Clone)]
287 struct FakePageFaultProvider {}
288 impl PageFaultProvider for FakePageFaultProvider {
289 fn get_refault_count(&self) -> u64 {
290 1000
291 }
292
293 fn get_fault_count(&self) -> u64 {
294 2000
295 }
296 }
297
298 #[test]
299 fn test_build_inspect_tree() {
300 let mut exec = fasync::TestExecutor::new();
301 let (stats_provider, stats_request_stream) =
302 fidl::endpoints::create_proxy_and_stream::<fkernel::StatsMarker>();
303
304 fasync::Task::spawn(async move {
305 serve_kernel_stats(stats_request_stream).await.unwrap();
306 })
307 .detach();
308
309 let inspector = fuchsia_inspect::Inspector::default();
310
311 build_inspect_tree(
312 stats_provider,
313 FakeStallProvider {},
314 FakePageFaultProvider {},
315 &inspector,
316 );
317
318 let output = exec
319 .run_singlethreaded(fuchsia_inspect::reader::read(&inspector))
320 .expect("got hierarchy");
321
322 assert_data_tree!(@executor exec, output, root: {
323 kmem_stats: {
324 total_bytes: 1u64,
325 free_bytes: 2u64,
326 wired_bytes: 3u64,
327 total_heap_bytes: 4u64,
328 free_heap_bytes: 5u64,
329 vmo_bytes: 6u64,
330 mmu_overhead_bytes: 7u64,
331 ipc_bytes: 8u64,
332 other_bytes: 9u64,
333 free_loaned_bytes: 10u64,
334 cache_bytes: 11u64,
335 slab_bytes: 12u64,
336 zram_bytes: 13u64,
337 vmo_reclaim_total_bytes: 14u64,
338 vmo_reclaim_newest_bytes: 15u64,
339 vmo_reclaim_oldest_bytes: 16u64,
340 vmo_reclaim_disabled_bytes: 17u64,
341 vmo_discardable_locked_bytes: 18u64,
342 vmo_discardable_unlocked_bytes: 19u64
343 },
344 kmem_stats_compression: {
345 uncompressed_storage_bytes: 20u64,
346 compressed_storage_bytes: 21u64,
347 compressed_fragmentation_bytes: 22u64,
348 compression_time: 23i64,
349 decompression_time: 24i64,
350 total_page_compression_attempts: 25u64,
351 failed_page_compression_attempts: 26u64,
352 total_page_decompressions: 27u64,
353 compressed_page_evictions: 28u64,
354 eager_page_compressions: 29u64,
355 memory_pressure_page_compressions: 30u64,
356 critical_memory_page_compressions: 31u64,
357 pages_decompressed_unit_ns: 32u64,
358 pages_decompressed_within_log_time: vec![
359 40u64, 41u64, 42u64, 43u64, 44u64, 45u64, 46u64, 47u64,
360 ]
361 },
362 stalls: {
363 some_ms: 10u64,
364 full_ms: 20u64,
365 },
366 faults: {
367 count: 2000u64,
368 },
369 refaults: {
370 count: 1000u64,
371 },
372 });
373 }
374}