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