1use crate::{
3 shell::{output::format_bytes, ShellError},
4 shell_println,
5};
6use alloc::string::String;
7
8pub fn cmd_mem(args: &[String]) -> Result<(), ShellError> {
10 if args.is_empty() {
11 return cmd_mem_summary();
12 }
13
14 match args[0].as_str() {
15 "zones" => cmd_mem_zones(),
16 "diag" => cmd_mem_diag(),
17 _ => {
18 shell_println!("Usage: mem [zones|diag]");
19 Ok(())
20 }
21 }
22}
23
24fn cmd_mem_summary() -> Result<(), ShellError> {
26 let (total_pages, allocated_pages, reserved_pages, cached_pages, pressured_zones) = {
27 let allocator_guard = crate::memory::buddy::get_allocator().lock();
28 if let Some(ref allocator) = *allocator_guard {
29 let (total_pages, allocated_pages) = allocator.page_totals();
30 let mut zones =
31 [crate::memory::buddy::ZoneStats::empty(); crate::memory::zone::ZoneType::COUNT];
32 let zone_count = allocator.zone_snapshot(&mut zones);
33 let reserved_pages = zones
34 .iter()
35 .take(zone_count)
36 .map(|zone| zone.reserved_pages)
37 .sum::<usize>();
38 let cached_pages = zones
39 .iter()
40 .take(zone_count)
41 .map(|zone| zone.cached_pages)
42 .sum::<usize>();
43 let pressured_zones = zones
44 .iter()
45 .take(zone_count)
46 .filter(|zone| zone.pressure() != crate::memory::buddy::ZonePressure::Healthy)
47 .count();
48 (
49 total_pages,
50 allocated_pages,
51 reserved_pages,
52 cached_pages,
53 pressured_zones,
54 )
55 } else {
56 shell_println!(" Memory allocator not initialized");
57 return Ok(());
58 }
59 };
60
61 let total_bytes = total_pages * 4096;
62 let used_bytes = allocated_pages * 4096;
63 let free_bytes = total_bytes - used_bytes;
64 let reserved_bytes = reserved_pages * 4096;
65 let cached_bytes = cached_pages * 4096;
66
67 let (total_val, total_unit) = format_bytes(total_bytes);
68 let (used_val, used_unit) = format_bytes(used_bytes);
69 let (free_val, free_unit) = format_bytes(free_bytes);
70 let (reserved_val, reserved_unit) = format_bytes(reserved_bytes);
71 let (cached_val, cached_unit) = format_bytes(cached_bytes);
72
73 shell_println!("Memory status:");
74 shell_println!(
75 " Total: {} {} ({} pages)",
76 total_val,
77 total_unit,
78 total_pages
79 );
80 shell_println!(
81 " Used: {} {} ({} pages)",
82 used_val,
83 used_unit,
84 allocated_pages
85 );
86 shell_println!(
87 " Free: {} {} ({} pages)",
88 free_val,
89 free_unit,
90 total_pages - allocated_pages
91 );
92 shell_println!(
93 " Reserved: {} {} ({} pages)",
94 reserved_val,
95 reserved_unit,
96 reserved_pages
97 );
98 shell_println!(
99 " Cached: {} {} ({} pages)",
100 cached_val,
101 cached_unit,
102 cached_pages
103 );
104 shell_println!(
105 " Pressure: {} zone(s) below high watermark",
106 pressured_zones
107 );
108 shell_println!("");
109
110 Ok(())
111}
112
113fn cmd_mem_zones() -> Result<(), ShellError> {
115 const MAX_ZONES: usize = crate::memory::zone::ZoneType::COUNT;
116 let mut zones_info = [crate::memory::buddy::ZoneStats::empty(); MAX_ZONES];
117 let zone_count;
118
119 {
120 let allocator_guard = crate::memory::buddy::get_allocator().lock();
121 if let Some(ref allocator) = *allocator_guard {
122 zone_count = allocator.zone_snapshot(&mut zones_info);
123 } else {
124 shell_println!(" Memory allocator not initialized");
125 return Ok(());
126 }
127 }
128
129 shell_println!("Memory zones:");
130 for info in zones_info.iter().take(zone_count) {
131 let managed_bytes = info.managed_pages * 4096;
132 let present_bytes = info.present_pages * 4096;
133 let reserved_bytes = info.reserved_pages * 4096;
134 let free_bytes = info.free_pages * 4096;
135 let movable_bytes = info.movable_free_pages * 4096;
136 let unmovable_bytes = info.unmovable_free_pages * 4096;
137
138 let (managed_val, managed_unit) = format_bytes(managed_bytes);
139 let (present_val, present_unit) = format_bytes(present_bytes);
140 let (reserved_val, reserved_unit) = format_bytes(reserved_bytes);
141 let (free_val, free_unit) = format_bytes(free_bytes);
142 let (movable_val, movable_unit) = format_bytes(movable_bytes);
143 let (unmovable_val, unmovable_unit) = format_bytes(unmovable_bytes);
144
145 shell_println!(" Zone {:?}:", info.zone_type);
146 shell_println!(" Base: 0x{:016x}", info.base);
147 shell_println!(
148 " Managed: {} {} ({} pages)",
149 managed_val,
150 managed_unit,
151 info.managed_pages
152 );
153 shell_println!(
154 " Present: {} {} ({} pages)",
155 present_val,
156 present_unit,
157 info.present_pages
158 );
159 shell_println!(
160 " Reserved: {} {} ({} pages)",
161 reserved_val,
162 reserved_unit,
163 info.reserved_pages
164 );
165 shell_println!(
166 " Free: {} {} ({} pages)",
167 free_val,
168 free_unit,
169 info.free_pages
170 );
171 shell_println!(" Used: {} pages", info.allocated_pages);
172 shell_println!(" Cached: {} pages", info.cached_pages);
173 shell_println!(
174 " Cached(u/m): {} / {} pages",
175 info.cached_unmovable_pages,
176 info.cached_movable_pages
177 );
178 shell_println!(
179 " Segments: {}/{}",
180 info.segment_count,
181 info.segment_capacity
182 );
183 shell_println!(
184 " Pageblocks: {} (u/m = {} / {}, order={})",
185 info.pageblock_count,
186 info.unmovable_pageblocks,
187 info.movable_pageblocks,
188 crate::memory::zone::PAGEBLOCK_ORDER
189 );
190 shell_println!(
191 " Free(u/m): {} {} / {} {}",
192 unmovable_val,
193 unmovable_unit,
194 movable_val,
195 movable_unit
196 );
197 if let Some(order) = info.largest_free_order {
198 let largest_bytes = 4096usize << order;
199 let (largest_val, largest_unit) = format_bytes(largest_bytes);
200 shell_println!(
201 " Largest: order {} ({} {})",
202 order,
203 largest_val,
204 largest_unit
205 );
206 } else {
207 shell_println!(" Largest: none");
208 }
209 shell_println!(
210 " Policy: min={} low={} high={} reserve={}",
211 info.watermark_min,
212 info.watermark_low,
213 info.watermark_high,
214 info.lowmem_reserve_pages
215 );
216 shell_println!(
217 " State: {:?} (avail_after_reserve={} pages, holes={} pages)",
218 info.pressure(),
219 info.available_after_reserve_pages(),
220 info.hole_pages()
221 );
222 shell_println!("");
223 }
224
225 Ok(())
226}
227
228fn cmd_mem_diag() -> Result<(), ShellError> {
230 let quarantine = crate::memory::poison_quarantine_pages_snapshot();
232 let (q_val, q_unit) = format_bytes(quarantine.saturating_mul(4096));
233 shell_println!("Poison quarantine:");
234 shell_println!(" Quarantined pages: {} ({} {})", quarantine, q_val, q_unit);
235 shell_println!("");
236
237 let fail_counts = crate::memory::buddy::buddy_alloc_fail_counts_snapshot();
239 let any_fail = fail_counts.iter().any(|&c| c > 0);
240 if any_fail {
241 shell_println!("Buddy allocation failures (by order):");
242 for (order, &count) in fail_counts.iter().enumerate() {
243 if count > 0 {
244 let size = 4096usize << order;
245 let (v, u) = format_bytes(size);
246 shell_println!(" order {:>2} ({} {}): {} failures", order, v, u, count);
247 }
248 }
249 shell_println!("");
250 } else {
251 shell_println!("Buddy allocation failures: none");
252 shell_println!("");
253 }
254
255 {
257 let allocator_guard = crate::memory::buddy::get_allocator().lock();
258 if let Some(ref allocator) = *allocator_guard {
259 let mut zones =
260 [crate::memory::buddy::ZoneStats::empty(); crate::memory::zone::ZoneType::COUNT];
261 let zone_count = allocator.zone_snapshot(&mut zones);
262 shell_println!("Buddy zones:");
263 for (zone_idx, info) in zones.iter().take(zone_count).enumerate() {
264 let zone = allocator.get_zone(zone_idx);
265 let mut frag = String::new();
266 for order in 1..=crate::memory::zone::MAX_ORDER {
267 use core::fmt::Write;
268 let score = zone.fragmentation_score(order as u8, info.cached_pages);
269 let _ = write!(frag, "o{}={}% ", order, score);
270 }
271 match info.largest_free_order {
272 Some(order) => shell_println!(
273 " {:?}: state={:?} free={} used={} cached={} avail={} segments={}/{} pageblocks=u{}/m{} u/m={}/{} cu/cm={}/{} watermarks={}/{}/{} reserve={} largest=o{}",
274 info.zone_type,
275 info.pressure(),
276 info.free_pages,
277 info.allocated_pages,
278 info.cached_pages,
279 info.available_after_reserve_pages(),
280 info.segment_count,
281 info.segment_capacity,
282 info.unmovable_pageblocks,
283 info.movable_pageblocks,
284 info.unmovable_free_pages,
285 info.movable_free_pages,
286 info.cached_unmovable_pages,
287 info.cached_movable_pages,
288 info.watermark_min,
289 info.watermark_low,
290 info.watermark_high,
291 info.lowmem_reserve_pages,
292 order
293 ),
294 None => shell_println!(
295 " {:?}: state={:?} free={} used={} cached={} avail={} segments={}/{} pageblocks=u{}/m{} u/m={}/{} cu/cm={}/{} watermarks={}/{}/{} reserve={} largest=none",
296 info.zone_type,
297 info.pressure(),
298 info.free_pages,
299 info.allocated_pages,
300 info.cached_pages,
301 info.available_after_reserve_pages(),
302 info.segment_count,
303 info.segment_capacity,
304 info.unmovable_pageblocks,
305 info.movable_pageblocks,
306 info.unmovable_free_pages,
307 info.movable_free_pages,
308 info.cached_unmovable_pages,
309 info.cached_movable_pages,
310 info.watermark_min,
311 info.watermark_low,
312 info.watermark_high,
313 info.lowmem_reserve_pages
314 ),
315 }
316 shell_println!(" frag/order: {}", frag);
317 }
318 shell_println!("");
319 }
320 }
321
322 let slab = crate::memory::heap::slab_diag_snapshot();
324 let (sa_v, sa_u) = format_bytes(slab.pages_allocated.saturating_mul(4096));
325 let (sr_v, sr_u) = format_bytes(slab.pages_reclaimed.saturating_mul(4096));
326 let (sl_v, sl_u) = format_bytes(slab.pages_live.saturating_mul(4096));
327 shell_println!("Slab allocator:");
328 shell_println!(
329 " Pages allocated: {} ({} {})",
330 slab.pages_allocated,
331 sa_v,
332 sa_u
333 );
334 shell_println!(
335 " Pages reclaimed: {} ({} {})",
336 slab.pages_reclaimed,
337 sr_v,
338 sr_u
339 );
340 shell_println!(
341 " Pages live: {} ({} {})",
342 slab.pages_live,
343 sl_v,
344 sl_u
345 );
346 shell_println!("");
347
348 if let Some(fail) = crate::memory::heap::last_heap_failure_snapshot() {
350 shell_println!("Last heap allocation failure:");
351 shell_println!(" Backend: {:?}", fail.backend);
352 shell_println!(
353 " Request: size={} align={} effective={}",
354 fail.requested_size,
355 fail.align,
356 fail.effective_size
357 );
358 shell_println!(" Error: {:?}", fail.error);
359 shell_println!("");
360 }
361
362 shell_println!("Slab size classes:");
364 for ci in 0..crate::memory::heap::SLAB_NUM_CLASSES {
365 let block = crate::memory::heap::slab_class_size(ci);
366 let blocks = crate::memory::heap::slab_blocks_per_page(ci);
367 let waste = block.saturating_sub(1); shell_println!(
369 " class {:>2}: block={:>5}B blocks/page={:>3} max_waste={:>4}B",
370 ci,
371 block,
372 blocks,
373 waste
374 );
375 }
376
377 Ok(())
378}