Skip to main content

ruvix_shell/
lib.rs

1//! # RuVix In-Kernel Debug Shell
2//!
3//! This crate provides an in-kernel debug shell for the RuVix Cognition Kernel
4//! as specified in ADR-087. It enables runtime inspection of kernel state,
5//! memory statistics, task information, and proof subsystem status.
6//!
7//! ## Design Principles
8//!
9//! - **`#![no_std]` compatible**: Uses only `alloc` for dynamic allocation
10//! - **Line-based parsing**: Simple command parser suitable for serial consoles
11//! - **Trait-based backend**: `ShellBackend` trait for kernel integration
12//! - **Modular commands**: Each command in its own module for maintainability
13//!
14//! ## Available Commands
15//!
16//! | Command | Description |
17//! |---------|-------------|
18//! | `help` | Show available commands |
19//! | `info` | Kernel version, boot time, uptime |
20//! | `mem` | Memory statistics |
21//! | `tasks` | Task listing |
22//! | `caps` | Capability table dump |
23//! | `queues` | Queue statistics |
24//! | `vectors` | Vector store info |
25//! | `proofs` | Proof statistics |
26//! | `cpu` | CPU info for SMP |
27//! | `witness` | Witness log viewer |
28//! | `perf` | Performance counters |
29//! | `trace` | Syscall tracing toggle |
30//! | `reboot` | Trigger reboot |
31//!
32//! ## Example
33//!
34//! ```rust,ignore
35//! use ruvix_shell::{Shell, ShellBackend, ShellConfig};
36//!
37//! struct MyKernel { /* kernel state */ }
38//!
39//! impl ShellBackend for MyKernel {
40//!     // Implement trait methods...
41//! }
42//!
43//! let config = ShellConfig::default();
44//! let mut shell = Shell::new(config);
45//! let output = shell.execute_line("help", &kernel);
46//! ```
47
48#![no_std]
49#![deny(missing_docs)]
50#![deny(clippy::all)]
51#![warn(clippy::pedantic)]
52#![allow(clippy::module_name_repetitions)]
53
54extern crate alloc;
55
56pub mod commands;
57mod parser;
58
59pub use parser::{Command, ParseError, Parser};
60
61use alloc::string::String;
62use alloc::vec::Vec;
63use alloc::format;
64
65/// Shell configuration options.
66#[derive(Debug, Clone)]
67pub struct ShellConfig {
68    /// Maximum command history size.
69    pub max_history: usize,
70    /// Whether to echo commands.
71    pub echo: bool,
72    /// Shell prompt string.
73    pub prompt: &'static str,
74}
75
76impl Default for ShellConfig {
77    fn default() -> Self {
78        Self {
79            max_history: 32,
80            echo: true,
81            prompt: "ruvix> ",
82        }
83    }
84}
85
86/// Memory statistics returned by the shell backend.
87#[derive(Debug, Clone, Default)]
88pub struct MemoryStats {
89    /// Total physical memory in bytes.
90    pub total_bytes: u64,
91    /// Used memory in bytes.
92    pub used_bytes: u64,
93    /// Free memory in bytes.
94    pub free_bytes: u64,
95    /// Number of allocated regions.
96    pub region_count: u32,
97    /// Number of slab allocations.
98    pub slab_count: u32,
99    /// Peak memory usage in bytes.
100    pub peak_bytes: u64,
101}
102
103/// Task information returned by the shell backend.
104#[derive(Debug, Clone)]
105pub struct TaskInfo {
106    /// Task handle/ID.
107    pub id: u32,
108    /// Task name (if available).
109    pub name: [u8; 16],
110    /// Task state (running, blocked, etc.).
111    pub state: TaskState,
112    /// Priority level.
113    pub priority: u8,
114    /// Partition ID.
115    pub partition: u8,
116    /// CPU affinity mask.
117    pub cpu_affinity: u8,
118    /// Capability count.
119    pub cap_count: u16,
120}
121
122/// Task state enumeration.
123#[derive(Debug, Clone, Copy, PartialEq, Eq)]
124#[repr(u8)]
125pub enum TaskState {
126    /// Task is runnable.
127    Ready = 0,
128    /// Task is currently running.
129    Running = 1,
130    /// Task is blocked on a queue.
131    Blocked = 2,
132    /// Task is waiting on a timer.
133    Sleeping = 3,
134    /// Task has exited.
135    Exited = 4,
136}
137
138/// CPU information for SMP.
139#[derive(Debug, Clone)]
140pub struct CpuInfo {
141    /// CPU ID (0-255).
142    pub id: u8,
143    /// Whether CPU is online.
144    pub online: bool,
145    /// Whether CPU is the boot CPU.
146    pub is_primary: bool,
147    /// CPU frequency in MHz (if available).
148    pub freq_mhz: u32,
149    /// Current CPU load percentage (0-100).
150    pub load_percent: u8,
151}
152
153/// Queue statistics.
154#[derive(Debug, Clone, Default)]
155pub struct QueueStats {
156    /// Total number of queues.
157    pub queue_count: u32,
158    /// Total messages pending across all queues.
159    pub pending_messages: u64,
160    /// Total messages sent.
161    pub messages_sent: u64,
162    /// Total messages received.
163    pub messages_received: u64,
164    /// Total zero-copy transfers.
165    pub zero_copy_count: u64,
166}
167
168/// Vector store statistics.
169#[derive(Debug, Clone, Default)]
170pub struct VectorStats {
171    /// Number of vector stores.
172    pub store_count: u32,
173    /// Total vectors stored.
174    pub vector_count: u64,
175    /// Total dimensions across all stores.
176    pub total_dimensions: u32,
177    /// Total memory used by vectors in bytes.
178    pub memory_bytes: u64,
179    /// Total vector reads.
180    pub reads: u64,
181    /// Total vector writes.
182    pub writes: u64,
183}
184
185/// Proof subsystem statistics.
186#[derive(Debug, Clone, Default)]
187pub struct ProofStats {
188    /// Total proofs generated.
189    pub generated: u64,
190    /// Total proofs verified.
191    pub verified: u64,
192    /// Total proofs rejected.
193    pub rejected: u64,
194    /// Current cache entries.
195    pub cache_entries: u32,
196    /// Cache hit count.
197    pub cache_hits: u64,
198    /// Cache miss count.
199    pub cache_misses: u64,
200    /// Reflex tier proofs.
201    pub tier0_count: u64,
202    /// Standard tier proofs.
203    pub tier1_count: u64,
204    /// Deep tier proofs.
205    pub tier2_count: u64,
206}
207
208/// Capability table entry information.
209#[derive(Debug, Clone)]
210pub struct CapEntry {
211    /// Capability handle.
212    pub handle: u32,
213    /// Object ID.
214    pub object_id: u64,
215    /// Object type.
216    pub object_type: u8,
217    /// Rights bitmap.
218    pub rights: u32,
219    /// Badge value.
220    pub badge: u64,
221    /// Delegation depth.
222    pub depth: u8,
223}
224
225/// Witness log entry.
226#[derive(Debug, Clone)]
227pub struct WitnessEntry {
228    /// Sequence number.
229    pub seq: u64,
230    /// Timestamp in nanoseconds.
231    pub timestamp_ns: u64,
232    /// Operation type.
233    pub operation: u8,
234    /// Associated object ID.
235    pub object_id: u64,
236    /// Witness hash (first 8 bytes).
237    pub hash_prefix: [u8; 8],
238}
239
240/// Performance counter data.
241#[derive(Debug, Clone, Default)]
242pub struct PerfCounters {
243    /// Syscall count.
244    pub syscalls: u64,
245    /// Context switches.
246    pub context_switches: u64,
247    /// Interrupts handled.
248    pub interrupts: u64,
249    /// Page faults.
250    pub page_faults: u64,
251    /// IPI messages sent.
252    pub ipi_sent: u64,
253    /// Total CPU cycles (if available).
254    pub cpu_cycles: u64,
255}
256
257/// Kernel information.
258#[derive(Debug, Clone)]
259pub struct KernelInfo {
260    /// Kernel version string.
261    pub version: &'static str,
262    /// Build timestamp.
263    pub build_time: &'static str,
264    /// Boot time in nanoseconds since epoch.
265    pub boot_time_ns: u64,
266    /// Current time in nanoseconds since epoch.
267    pub current_time_ns: u64,
268    /// Number of online CPUs.
269    pub cpu_count: u8,
270}
271
272/// Trait for kernel integration with the debug shell.
273///
274/// Implementors provide access to kernel state for each command.
275pub trait ShellBackend {
276    /// Get kernel information.
277    fn kernel_info(&self) -> KernelInfo;
278
279    /// Get memory statistics.
280    fn memory_stats(&self) -> MemoryStats;
281
282    /// Get task list.
283    fn task_list(&self) -> Vec<TaskInfo>;
284
285    /// Get CPU information for all CPUs.
286    fn cpu_info(&self) -> Vec<CpuInfo>;
287
288    /// Get queue statistics.
289    fn queue_stats(&self) -> QueueStats;
290
291    /// Get vector store statistics.
292    fn vector_stats(&self) -> VectorStats;
293
294    /// Get proof subsystem statistics.
295    fn proof_stats(&self) -> ProofStats;
296
297    /// Get capability table entries for a task.
298    fn capability_entries(&self, task_id: Option<u32>) -> Vec<CapEntry>;
299
300    /// Get recent witness log entries.
301    fn witness_entries(&self, count: usize) -> Vec<WitnessEntry>;
302
303    /// Get performance counters.
304    fn perf_counters(&self) -> PerfCounters;
305
306    /// Check if syscall tracing is enabled.
307    fn trace_enabled(&self) -> bool;
308
309    /// Toggle syscall tracing.
310    fn set_trace(&mut self, enabled: bool);
311
312    /// Trigger system reboot.
313    fn reboot(&mut self);
314}
315
316/// The debug shell processor.
317#[derive(Debug)]
318pub struct Shell {
319    config: ShellConfig,
320    parser: Parser,
321    history: Vec<String>,
322}
323
324impl Shell {
325    /// Create a new shell with the given configuration.
326    #[must_use]
327    pub fn new(config: ShellConfig) -> Self {
328        Self {
329            config,
330            parser: Parser::new(),
331            history: Vec::new(),
332        }
333    }
334
335    /// Create a shell with default configuration.
336    #[must_use]
337    pub fn default_shell() -> Self {
338        Self::new(ShellConfig::default())
339    }
340
341    /// Get the shell prompt.
342    #[must_use]
343    pub fn prompt(&self) -> &'static str {
344        self.config.prompt
345    }
346
347    /// Execute a command line and return the output.
348    pub fn execute_line<B: ShellBackend>(&mut self, line: &str, backend: &mut B) -> String {
349        let trimmed = line.trim();
350
351        if trimmed.is_empty() {
352            return String::new();
353        }
354
355        // Add to history
356        if self.history.len() >= self.config.max_history {
357            self.history.remove(0);
358        }
359        self.history.push(String::from(trimmed));
360
361        // Parse the command
362        match self.parser.parse(trimmed) {
363            Ok(cmd) => self.dispatch_command(cmd, backend),
364            Err(e) => format!("Error: {}\nType 'help' for available commands.", e),
365        }
366    }
367
368    /// Dispatch a parsed command to the appropriate handler.
369    fn dispatch_command<B: ShellBackend>(&self, cmd: Command, backend: &mut B) -> String {
370        match cmd {
371            Command::Help => commands::help::execute(),
372            Command::Info => commands::info::execute(backend),
373            Command::Mem => commands::mem::execute(backend),
374            Command::Tasks => commands::tasks::execute(backend),
375            Command::Caps { task_id } => commands::caps::execute(backend, task_id),
376            Command::Queues => commands::queues::execute(backend),
377            Command::Vectors => commands::vectors::execute(backend),
378            Command::Proofs => commands::proofs::execute(backend),
379            Command::Cpu => commands::cpu::execute(backend),
380            Command::Witness { count } => commands::witness::execute(backend, count),
381            Command::Perf => commands::perf::execute(backend),
382            Command::Trace { enable } => {
383                if let Some(on) = enable {
384                    backend.set_trace(on);
385                    if on {
386                        String::from("Syscall tracing enabled.")
387                    } else {
388                        String::from("Syscall tracing disabled.")
389                    }
390                } else {
391                    if backend.trace_enabled() {
392                        String::from("Syscall tracing: ENABLED")
393                    } else {
394                        String::from("Syscall tracing: DISABLED")
395                    }
396                }
397            }
398            Command::Reboot => {
399                backend.reboot();
400                String::from("Rebooting...")
401            }
402        }
403    }
404
405    /// Get command history.
406    #[must_use]
407    pub fn history(&self) -> &[String] {
408        &self.history
409    }
410
411    /// Clear command history.
412    pub fn clear_history(&mut self) {
413        self.history.clear();
414    }
415}
416
417#[cfg(test)]
418mod tests {
419    use super::*;
420    use alloc::vec;
421
422    /// Mock backend for testing.
423    struct MockBackend {
424        trace_on: bool,
425        rebooted: bool,
426    }
427
428    impl MockBackend {
429        fn new() -> Self {
430            Self {
431                trace_on: false,
432                rebooted: false,
433            }
434        }
435    }
436
437    impl ShellBackend for MockBackend {
438        fn kernel_info(&self) -> KernelInfo {
439            KernelInfo {
440                version: "0.1.0-test",
441                build_time: "2024-01-01T00:00:00Z",
442                boot_time_ns: 1_000_000_000,
443                current_time_ns: 2_000_000_000,
444                cpu_count: 4,
445            }
446        }
447
448        fn memory_stats(&self) -> MemoryStats {
449            MemoryStats {
450                total_bytes: 1024 * 1024 * 1024, // 1 GiB
451                used_bytes: 256 * 1024 * 1024,   // 256 MiB
452                free_bytes: 768 * 1024 * 1024,   // 768 MiB
453                region_count: 42,
454                slab_count: 128,
455                peak_bytes: 512 * 1024 * 1024,
456            }
457        }
458
459        fn task_list(&self) -> Vec<TaskInfo> {
460            vec![
461                TaskInfo {
462                    id: 0,
463                    name: *b"idle\0\0\0\0\0\0\0\0\0\0\0\0",
464                    state: TaskState::Running,
465                    priority: 0,
466                    partition: 0,
467                    cpu_affinity: 0xFF,
468                    cap_count: 4,
469                },
470                TaskInfo {
471                    id: 1,
472                    name: *b"init\0\0\0\0\0\0\0\0\0\0\0\0",
473                    state: TaskState::Ready,
474                    priority: 100,
475                    partition: 0,
476                    cpu_affinity: 0xFF,
477                    cap_count: 16,
478                },
479            ]
480        }
481
482        fn cpu_info(&self) -> Vec<CpuInfo> {
483            vec![
484                CpuInfo {
485                    id: 0,
486                    online: true,
487                    is_primary: true,
488                    freq_mhz: 1800,
489                    load_percent: 25,
490                },
491            ]
492        }
493
494        fn queue_stats(&self) -> QueueStats {
495            QueueStats {
496                queue_count: 8,
497                pending_messages: 12,
498                messages_sent: 1000,
499                messages_received: 988,
500                zero_copy_count: 500,
501            }
502        }
503
504        fn vector_stats(&self) -> VectorStats {
505            VectorStats {
506                store_count: 2,
507                vector_count: 10000,
508                total_dimensions: 768,
509                memory_bytes: 30 * 1024 * 1024,
510                reads: 50000,
511                writes: 10000,
512            }
513        }
514
515        fn proof_stats(&self) -> ProofStats {
516            ProofStats {
517                generated: 15000,
518                verified: 14950,
519                rejected: 50,
520                cache_entries: 64,
521                cache_hits: 12000,
522                cache_misses: 3000,
523                tier0_count: 10000,
524                tier1_count: 4500,
525                tier2_count: 500,
526            }
527        }
528
529        fn capability_entries(&self, _task_id: Option<u32>) -> Vec<CapEntry> {
530            vec![
531                CapEntry {
532                    handle: 0,
533                    object_id: 0x1000,
534                    object_type: 1,
535                    rights: 0x07,
536                    badge: 0,
537                    depth: 0,
538                },
539            ]
540        }
541
542        fn witness_entries(&self, count: usize) -> Vec<WitnessEntry> {
543            (0..count.min(5))
544                .map(|i| WitnessEntry {
545                    seq: i as u64,
546                    timestamp_ns: 1_000_000_000 + i as u64 * 1000,
547                    operation: 1,
548                    object_id: 0x1000 + i as u64,
549                    hash_prefix: [0xAB; 8],
550                })
551                .collect()
552        }
553
554        fn perf_counters(&self) -> PerfCounters {
555            PerfCounters {
556                syscalls: 100000,
557                context_switches: 5000,
558                interrupts: 250000,
559                page_faults: 100,
560                ipi_sent: 500,
561                cpu_cycles: 1_000_000_000_000,
562            }
563        }
564
565        fn trace_enabled(&self) -> bool {
566            self.trace_on
567        }
568
569        fn set_trace(&mut self, enabled: bool) {
570            self.trace_on = enabled;
571        }
572
573        fn reboot(&mut self) {
574            self.rebooted = true;
575        }
576    }
577
578    #[test]
579    fn test_shell_creation() {
580        let shell = Shell::default_shell();
581        assert_eq!(shell.prompt(), "ruvix> ");
582    }
583
584    #[test]
585    fn test_help_command() {
586        let mut shell = Shell::default_shell();
587        let mut backend = MockBackend::new();
588        let output = shell.execute_line("help", &mut backend);
589        assert!(output.contains("help"));
590        assert!(output.contains("info"));
591        assert!(output.contains("mem"));
592    }
593
594    #[test]
595    fn test_info_command() {
596        let mut shell = Shell::default_shell();
597        let mut backend = MockBackend::new();
598        let output = shell.execute_line("info", &mut backend);
599        assert!(output.contains("0.1.0-test"));
600        assert!(output.contains("CPU"));
601    }
602
603    #[test]
604    fn test_trace_toggle() {
605        let mut shell = Shell::default_shell();
606        let mut backend = MockBackend::new();
607
608        // Check initial state
609        let output = shell.execute_line("trace", &mut backend);
610        assert!(output.contains("DISABLED"));
611
612        // Enable tracing
613        let output = shell.execute_line("trace on", &mut backend);
614        assert!(output.contains("enabled"));
615        assert!(backend.trace_enabled());
616
617        // Disable tracing
618        let output = shell.execute_line("trace off", &mut backend);
619        assert!(output.contains("disabled"));
620        assert!(!backend.trace_enabled());
621    }
622
623    #[test]
624    fn test_unknown_command() {
625        let mut shell = Shell::default_shell();
626        let mut backend = MockBackend::new();
627        let output = shell.execute_line("unknown_cmd", &mut backend);
628        assert!(output.contains("Error"));
629    }
630
631    #[test]
632    fn test_history() {
633        let mut shell = Shell::default_shell();
634        let mut backend = MockBackend::new();
635
636        shell.execute_line("help", &mut backend);
637        shell.execute_line("info", &mut backend);
638
639        let history = shell.history();
640        assert_eq!(history.len(), 2);
641        assert_eq!(history[0], "help");
642        assert_eq!(history[1], "info");
643    }
644
645    #[test]
646    fn test_empty_line() {
647        let mut shell = Shell::default_shell();
648        let mut backend = MockBackend::new();
649        let output = shell.execute_line("", &mut backend);
650        assert!(output.is_empty());
651
652        let output = shell.execute_line("   ", &mut backend);
653        assert!(output.is_empty());
654    }
655}