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