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 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}