Skip to main content

sbpf_debugger/
repl.rs

1use {
2    crate::{
3        debugger::{DebugEvent, DebugMode},
4        runner::DebuggerSession,
5    },
6    std::io::{self, Write},
7};
8
9pub struct Repl {
10    pub session: DebuggerSession,
11}
12
13impl Repl {
14    pub fn new(session: DebuggerSession) -> Self {
15        Self { session }
16    }
17
18    pub fn start(&mut self) {
19        println!("\nsBPF Debugger REPL. Type 'help' for commands.");
20
21        // Print the first instruction.
22        if let Some(line) = self.session.debugger.get_current_line() {
23            let asm = self
24                .session
25                .debugger
26                .get_instruction_asm()
27                .unwrap_or_default();
28            println!("{}\t{}", line, asm);
29        }
30
31        let stdin = io::stdin();
32        loop {
33            print!("dbg> ");
34            io::stdout().flush().unwrap();
35            let mut input = String::new();
36            if stdin.read_line(&mut input).is_err() {
37                break;
38            }
39            let cmd = input.trim();
40            match cmd {
41                "next" | "n" => self.run_and_display(DebugMode::Next),
42                "continue" | "c" => self.run_and_display(DebugMode::Continue),
43                cmd if cmd.starts_with("break ") || cmd.starts_with("b ") => {
44                    if let Some(arg) = cmd.split_whitespace().nth(1) {
45                        if let Ok(line) = arg.parse::<usize>() {
46                            match self.session.debugger.set_breakpoint_at_line(line) {
47                                Ok(()) => println!("Breakpoint set at line {}", line),
48                                Err(e) => println!("Error: {}", e),
49                            }
50                        } else {
51                            println!("Error: Invalid line number.");
52                        }
53                    }
54                }
55                cmd if cmd.starts_with("delete ") || cmd.starts_with("d ") => {
56                    if let Some(arg) = cmd.split_whitespace().nth(1) {
57                        if let Ok(line) = arg.parse::<usize>() {
58                            match self.session.debugger.remove_breakpoint_at_line(line) {
59                                Ok(()) => println!("Breakpoint removed from line {}", line),
60                                Err(e) => println!("Error: {}", e),
61                            }
62                        } else {
63                            println!("Error: Invalid line number for delete command.");
64                        }
65                    }
66                }
67                "info breakpoints" | "info b" => {
68                    println!("{}", self.session.debugger.get_breakpoints_info());
69                }
70                "info line" => {
71                    if let Some(line) = self.session.debugger.get_current_line() {
72                        let asm = self
73                            .session
74                            .debugger
75                            .get_instruction_asm()
76                            .unwrap_or_default();
77                        println!("{}\t{}", line, asm);
78                    }
79                }
80                "quit" | "q" => break,
81                "regs" => {
82                    let regs = self.session.debugger.get_registers();
83                    println!("+------------+--------------------+");
84                    println!("| Register   | Value              |");
85                    println!("+------------+--------------------+");
86                    for (i, val) in regs.iter().enumerate() {
87                        println!(
88                            "| {:<10} | {:<18} |",
89                            format!("r{}", i),
90                            format!("0x{:016x}", val)
91                        );
92                    }
93                    println!("+------------+--------------------+");
94                }
95                cmd if cmd.starts_with("reg ") => {
96                    if let Some(arg) = cmd.split_whitespace().nth(1) {
97                        if let Ok(idx) = arg.parse::<usize>() {
98                            if let Some(val) = self.session.debugger.get_register(idx) {
99                                println!("+------------+--------------------+");
100                                println!("| Register   | Value              |");
101                                println!("+------------+--------------------+");
102                                println!(
103                                    "| {:<10} | {:<18} |",
104                                    format!("r{}", idx),
105                                    format!("0x{:016x}", val)
106                                );
107                                println!("+------------+--------------------+");
108                            } else {
109                                println!("Register index out of range");
110                            }
111                        } else {
112                            println!("Invalid register index");
113                        }
114                    } else {
115                        println!("Usage: reg <idx>");
116                    }
117                }
118                cmd if cmd.starts_with("setreg ") => {
119                    let mut parts = cmd.split_whitespace();
120                    parts.next();
121                    let idx_str = parts.next();
122                    let val_str = parts.next();
123                    if let (Some(idx_str), Some(val_str)) = (idx_str, val_str) {
124                        if let Ok(idx) = idx_str.parse::<usize>() {
125                            let value = if let Some(stripped) = val_str.strip_prefix("0x") {
126                                u64::from_str_radix(stripped, 16)
127                            } else {
128                                val_str.parse::<u64>()
129                            };
130                            match value {
131                                Ok(val) => match self.session.debugger.set_register_value(idx, val)
132                                {
133                                    Ok(()) => println!("Set r{} to 0x{:08x} ({})", idx, val, val),
134                                    Err(e) => println!("{}", e),
135                                },
136                                Err(_) => println!(
137                                    "Invalid value: must be a number (decimal or 0x... hex)"
138                                ),
139                            }
140                        } else {
141                            println!("Invalid register index");
142                        }
143                    } else {
144                        println!("Usage: setreg <idx> <value>");
145                    }
146                }
147                "compute" => {
148                    let cu_used = self.session.debugger.get_compute_units();
149                    let cu_total = self.session.debugger.initial_compute_budget;
150                    println!("Program consumed {} of {} compute units", cu_used, cu_total);
151                }
152                "help" => {
153                    println!("Commands:");
154                    println!("  next (n)                     - Execute one instruction");
155                    println!("  continue (c)                 - Continue execution");
156                    println!("  break (b) <line>             - Set breakpoint at line number");
157                    println!("  delete (d) <line>            - Remove breakpoint at line");
158                    println!("  info breakpoints (info b)    - Show all breakpoints");
159                    println!("  info line                    - Show current line info");
160                    println!("  regs                         - Show all registers");
161                    println!("  reg <idx>                    - Show single register");
162                    println!("  setreg <idx> <value>         - Set register value");
163                    println!("  compute                      - Show compute unit information");
164                    println!("  help                         - Show this help");
165                    println!("  quit (q)                     - Exit debugger");
166                }
167                _ => println!("Unknown command. Type 'help'."),
168            }
169        }
170    }
171
172    fn run_and_display(&mut self, mode: DebugMode) {
173        self.session.debugger.set_debug_mode(mode);
174        match self.session.debugger.run() {
175            Ok(event) => match event {
176                DebugEvent::Next(_pc, line) => {
177                    if let Some(line_num) = line {
178                        let asm = self
179                            .session
180                            .debugger
181                            .get_instruction_asm()
182                            .unwrap_or_default();
183                        println!("{}\t{}", line_num, asm);
184                    }
185                }
186                DebugEvent::Breakpoint(_pc, line) => {
187                    if let Some(line_num) = line {
188                        let asm = self
189                            .session
190                            .debugger
191                            .get_instruction_asm()
192                            .unwrap_or_default();
193                        println!("Breakpoint hit at line {}", line_num);
194                        println!("{}\t{}", line_num, asm);
195                    }
196                }
197                DebugEvent::Exit(code) => {
198                    println!("Program exited with code: {}", code);
199                }
200                DebugEvent::Error(msg) => {
201                    println!("Program error: {}", msg);
202                }
203            },
204            Err(e) => println!("Debugger error: {:?}", e),
205        }
206    }
207}