Skip to main content

strat9_kernel/shell/commands/mem/
mod.rs

1//! Memory management commands
2use crate::{
3    shell::{output::format_bytes, ShellError},
4    shell_println,
5};
6use alloc::string::String;
7
8/// Display memory status
9pub 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
24/// Summary view (default for `mem` with no subcommand).
25fn 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
113/// Display detailed memory zone information
114fn 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
228/// Diagnostic view : poison quarantine, slab health, buddy alloc failures.
229fn cmd_mem_diag() -> Result<(), ShellError> {
230    //  Poison quarantine ======================================================================================================================================================
231    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    //  Buddy allocation failures ========================================================================================================================
238    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    //  Buddy zone policy / fragmentation view ================================================================================
256    {
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    //  Slab allocator ================================================================================================================================================================
323    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    //  Last heap failure ======================================================================================================================================================
349    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    //  Slab size classes ======================================================================================================================================================
363    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); // worst-case internal waste
368        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}