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