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