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