1use {
2 crate::{
3 adapter::DebuggerInterface,
4 error::DebuggerResult,
5 parser::{LineMap, RODataSymbol},
6 },
7 either::Either,
8 sbpf_common::{inst_param::Number, instruction::Instruction, opcode::Opcode},
9 sbpf_vm::{syscalls::SyscallHandler, vm::SbpfVm},
10 serde_json::{Value, json},
11 std::collections::HashSet,
12};
13
14pub struct StackFrame<'a> {
15 pub index: usize,
16 pub pc: u64,
17 pub file: Option<&'a str>,
18 pub line: Option<usize>,
19 pub column: Option<usize>,
20}
21
22#[derive(Debug)]
23pub enum DebugMode {
24 Next,
25 Continue,
26}
27
28#[derive(Debug)]
29pub enum DebugEvent {
30 Breakpoint(u64, Option<usize>),
31 Next(u64, Option<usize>),
32 Exit(u64),
33 Error(String),
34}
35
36pub struct Debugger<H: SyscallHandler> {
37 pub vm: SbpfVm<H>,
38 pub breakpoints: HashSet<u64>,
39 pub line_breakpoints: HashSet<usize>,
40 pub dwarf_line_map: Option<LineMap>,
41 pub rodata: Option<Vec<RODataSymbol>>,
42 pub last_breakpoint: Option<u64>,
43 pub debug_mode: DebugMode,
44 pub stopped: bool,
45 pub exit_code: u64,
46 pub at_breakpoint: bool,
47 pub last_breakpoint_pc: Option<u64>,
48 pub initial_compute_budget: u64,
49 pub instruction_offsets: Vec<u64>,
50}
51
52impl<H: SyscallHandler> Debugger<H> {
53 pub fn new(vm: SbpfVm<H>) -> Self {
54 let initial_compute_budget = vm.config.compute_unit_limit;
55
56 let instruction_offsets: Vec<u64> = vm
57 .program
58 .iter()
59 .scan(0u64, |offset, inst| {
60 let current = *offset;
61 *offset += inst.get_size();
62 Some(current)
63 })
64 .collect();
65
66 Self {
67 vm,
68 breakpoints: HashSet::new(),
69 line_breakpoints: HashSet::new(),
70 dwarf_line_map: None,
71 rodata: None,
72 last_breakpoint: None,
73 debug_mode: DebugMode::Continue,
74 stopped: false,
75 exit_code: 0,
76 at_breakpoint: false,
77 last_breakpoint_pc: None,
78 initial_compute_budget,
79 instruction_offsets,
80 }
81 }
82
83 pub fn set_dwarf_line_map(&mut self, dwarf_map: LineMap) {
84 self.dwarf_line_map = Some(dwarf_map);
85 }
86
87 pub fn set_rodata(&mut self, rodata: Vec<RODataSymbol>) {
88 self.rodata = Some(rodata);
89 }
90
91 pub fn set_breakpoint(&mut self, pc: u64) {
92 self.breakpoints.insert(pc);
93 }
94
95 pub fn set_breakpoint_at_line(&mut self, line: usize) -> Result<(), String> {
96 if let Some(dwarf_map) = &self.dwarf_line_map {
97 let pcs = dwarf_map.get_pcs_for_line(line);
98 if pcs.is_empty() {
99 return Err(format!("No code at line {}", line));
100 }
101 self.line_breakpoints.insert(line);
102 for &pc in &pcs {
103 self.breakpoints.insert(pc);
104 }
105 Ok(())
106 } else {
107 Err("No debug info available".to_string())
108 }
109 }
110
111 pub fn remove_breakpoint_at_line(&mut self, line: usize) -> Result<(), String> {
112 if let Some(dwarf_map) = &self.dwarf_line_map {
113 let pcs = dwarf_map.get_pcs_for_line(line);
114 if !pcs.is_empty() {
115 self.line_breakpoints.remove(&line);
116 for &pc in &pcs {
117 self.breakpoints.remove(&pc);
118 }
119 }
120 }
121 Ok(())
122 }
123
124 pub fn get_current_line(&self) -> Option<usize> {
125 let pc = self.get_pc();
126 self.get_line_for_pc(pc)
127 }
128
129 pub fn get_line_for_pc(&self, pc: u64) -> Option<usize> {
130 if let Some(dwarf_map) = &self.dwarf_line_map {
131 dwarf_map.get_line_for_pc(pc)
132 } else {
133 None
134 }
135 }
136
137 pub fn get_pcs_for_line(&self, line: usize) -> Vec<u64> {
138 if let Some(dwarf_map) = &self.dwarf_line_map {
139 dwarf_map.get_pcs_for_line(line)
140 } else {
141 Vec::new()
142 }
143 }
144
145 pub fn get_breakpoints_info(&self) -> String {
146 if self.line_breakpoints.is_empty() {
147 return "No breakpoints set".to_string();
148 }
149
150 let mut lines: Vec<_> = self.line_breakpoints.iter().copied().collect();
151 lines.sort();
152 let lines_str = lines
153 .iter()
154 .map(|l| l.to_string())
155 .collect::<Vec<_>>()
156 .join(", ");
157 format!("Breakpoints: {}", lines_str)
158 }
159
160 pub fn set_debug_mode(&mut self, debug_mode: DebugMode) {
161 self.debug_mode = debug_mode;
162 }
163
164 pub fn run(&mut self) -> DebuggerResult<DebugEvent> {
165 match self.debug_mode {
166 DebugMode::Next => {
167 let current_pc = self.get_pc();
168
169 if self.at_breakpoint {
170 match self.vm.step() {
171 Ok(()) => {
172 self.at_breakpoint = false;
173 self.last_breakpoint_pc = None;
174
175 if self.vm.halted {
176 let exit_code = self.vm.exit_code.unwrap_or(0);
177 return Ok(DebugEvent::Exit(exit_code));
178 }
179
180 let new_pc = self.get_pc();
181 if self.breakpoints.contains(&new_pc) {
182 self.at_breakpoint = true;
183 self.last_breakpoint_pc = Some(new_pc);
184 let line_number = self.get_line_for_pc(new_pc);
185 return Ok(DebugEvent::Breakpoint(new_pc, line_number));
186 }
187 let line_number = self.get_line_for_pc(new_pc);
188 return Ok(DebugEvent::Next(new_pc, line_number));
189 }
190 Err(e) => return Ok(DebugEvent::Error(format!("{e}"))),
191 }
192 }
193
194 if self.breakpoints.contains(¤t_pc)
195 && self.last_breakpoint_pc != Some(current_pc)
196 {
197 self.at_breakpoint = true;
198 self.last_breakpoint_pc = Some(current_pc);
199 let line_number = self.get_line_for_pc(current_pc);
200 return Ok(DebugEvent::Breakpoint(current_pc, line_number));
201 }
202
203 let event = match self.vm.step() {
204 Ok(()) => {
205 if self.vm.halted {
206 let exit_code = self.vm.exit_code.unwrap_or(0);
207 DebugEvent::Exit(exit_code)
208 } else {
209 let new_pc = self.get_pc();
210 let line_number = self.get_line_for_pc(new_pc);
211 DebugEvent::Next(new_pc, line_number)
212 }
213 }
214 Err(e) => DebugEvent::Error(format!("{e}")),
215 };
216 Ok(event)
217 }
218 DebugMode::Continue => loop {
219 let current_pc = self.get_pc();
220
221 if self.at_breakpoint {
222 match self.vm.step() {
223 Ok(()) => {
224 self.at_breakpoint = false;
225 self.last_breakpoint_pc = None;
226
227 if self.vm.halted {
228 let exit_code = self.vm.exit_code.unwrap_or(0);
229 return Ok(DebugEvent::Exit(exit_code));
230 }
231 }
232 Err(e) => return Ok(DebugEvent::Error(format!("{e}"))),
233 }
234 continue;
235 }
236
237 if self.breakpoints.contains(¤t_pc)
238 && self.last_breakpoint_pc != Some(current_pc)
239 {
240 self.at_breakpoint = true;
241 self.last_breakpoint_pc = Some(current_pc);
242 let line_number = self.get_line_for_pc(current_pc);
243 return Ok(DebugEvent::Breakpoint(current_pc, line_number));
244 }
245
246 match self.vm.step() {
247 Ok(()) => {
248 if self.vm.halted {
249 let exit_code = self.vm.exit_code.unwrap_or(0);
250 return Ok(DebugEvent::Exit(exit_code));
251 }
252 }
253 Err(e) => return Ok(DebugEvent::Error(format!("{e}"))),
254 }
255 },
256 }
257 }
258
259 pub fn get_pc(&self) -> u64 {
260 self.instruction_offsets
261 .get(self.vm.pc)
262 .copied()
263 .unwrap_or(self.vm.pc as u64)
264 }
265
266 fn instruction_index_to_byte_offset(&self, idx: usize) -> u64 {
267 self.instruction_offsets
268 .get(idx)
269 .copied()
270 .unwrap_or(idx as u64)
271 }
272
273 pub fn get_registers(&self) -> &[u64] {
274 &self.vm.registers
275 }
276
277 pub fn get_register(&self, idx: usize) -> Option<u64> {
278 self.vm.registers.get(idx).copied()
279 }
280
281 pub fn set_register_value(&mut self, idx: usize, value: u64) -> Result<(), String> {
282 if let Some(reg) = self.vm.registers.get_mut(idx) {
283 *reg = value;
284 Ok(())
285 } else {
286 Err(format!("Register index {} out of range", idx))
287 }
288 }
289
290 pub fn get_rodata(&self) -> Option<&Vec<RODataSymbol>> {
291 self.rodata.as_ref()
292 }
293
294 pub fn get_compute_units(&self) -> u64 {
295 self.vm.compute_meter.get_consumed()
296 }
297
298 pub fn get_instruction(&self) -> Option<&Instruction> {
299 self.vm.program.get(self.vm.pc)
300 }
301
302 pub fn get_instruction_asm(&self) -> Option<String> {
303 let inst = self.get_instruction()?;
304 let mut asm = inst.to_asm().ok()?;
305
306 if inst.opcode == Opcode::Lddw
308 && let Some(Either::Right(Number::Int(imm))) = &inst.imm
309 && let Some(ref rodata_symbols) = self.rodata
310 {
311 let addr = *imm as u64;
312 for sym in rodata_symbols {
313 if sym.address == addr {
314 asm = asm.replace(&imm.to_string(), &sym.name);
315 break;
316 }
317 }
318 }
319
320 Some(asm)
321 }
322
323 pub fn get_source_location(&self, pc: u64) -> Option<(&str, usize, usize)> {
324 if let Some(dwarf_map) = &self.dwarf_line_map
325 && let Some(loc) = dwarf_map.get_source_location(pc)
326 {
327 return Some((&loc.file, loc.line as usize, loc.column as usize));
328 }
329 None
330 }
331
332 pub fn clear_breakpoints(&mut self) {
333 if let Some(dwarf_map) = &self.dwarf_line_map {
334 let lines: Vec<usize> = self.line_breakpoints.iter().copied().collect();
335 for line in lines {
336 let pcs = dwarf_map.get_pcs_for_line(line);
337 for pc in pcs {
338 self.breakpoints.remove(&pc);
339 }
340 self.line_breakpoints.remove(&line);
341 }
342 } else {
343 self.breakpoints.clear();
344 self.line_breakpoints.clear();
345 }
346 }
347
348 pub fn get_memory(&self, address: u64, size: usize) -> Option<Vec<u8>> {
349 self.vm
350 .memory
351 .read_bytes(address, size)
352 .map(|slice| slice.to_vec())
353 .ok()
354 }
355
356 fn make_stack_frame(&self, index: usize, pc: u64) -> StackFrame<'_> {
357 let loc = self.get_source_location(pc);
358 StackFrame {
359 index,
360 pc,
361 file: loc.map(|(f, _, _)| f),
362 line: loc.map(|(_, l, _)| l),
363 column: loc.map(|(_, _, c)| c),
364 }
365 }
366
367 pub fn get_stack_frames(&self) -> Vec<StackFrame<'_>> {
368 let mut frames = Vec::new();
369
370 let current_pc = self.get_pc();
372 frames.push(self.make_stack_frame(0, current_pc));
373
374 for (i, frame) in self.vm.call_stack.iter().rev().enumerate() {
376 let pc = self.instruction_index_to_byte_offset(frame.return_pc);
377 frames.push(self.make_stack_frame(i + 1, pc));
378 }
379
380 frames
381 }
382}
383
384impl<H: SyscallHandler> DebuggerInterface for Debugger<H> {
385 fn next(&mut self) -> Value {
386 self.set_debug_mode(DebugMode::Next);
387 self.run_to_json()
388 }
389
390 fn r#continue(&mut self) -> Value {
391 self.set_debug_mode(DebugMode::Continue);
392 self.run_to_json()
393 }
394
395 fn set_breakpoint(&mut self, file: String, line: usize) -> Value {
396 match self.set_breakpoint_at_line(line) {
397 Ok(()) => json!({
398 "type": "setBreakpoint",
399 "file": file,
400 "line": line,
401 "verified": true
402 }),
403 Err(e) => json!({
404 "type": "setBreakpoint",
405 "file": file,
406 "line": line,
407 "verified": false,
408 "error": e
409 }),
410 }
411 }
412
413 fn remove_breakpoint(&mut self, file: String, line: usize) -> Value {
414 match self.remove_breakpoint_at_line(line) {
415 Ok(()) => json!({
416 "type": "removeBreakpoint",
417 "file": file,
418 "line": line,
419 "success": true
420 }),
421 Err(e) => json!({
422 "type": "removeBreakpoint",
423 "file": file,
424 "line": line,
425 "success": false,
426 "error": e
427 }),
428 }
429 }
430
431 fn get_stack_frames(&self) -> Value {
432 let frames: Vec<Value> = self
433 .get_stack_frames()
434 .iter()
435 .map(|frame| {
436 let name = frame.file.unwrap_or("?").to_string();
437 let file = frame.file.unwrap_or("?").to_string();
438 let line = frame.line.unwrap_or(0);
439 let column = frame.column.unwrap_or(0);
440 json!({
441 "index": frame.index,
442 "name": name,
443 "file": file,
444 "line": line,
445 "column": column,
446 "instruction": frame.pc
447 })
448 })
449 .collect();
450 json!({ "frames": frames })
451 }
452
453 fn get_registers(&self) -> Value {
454 let regs: Vec<Value> = self
455 .get_registers()
456 .iter()
457 .enumerate()
458 .map(|(i, &value)| {
459 json!({
460 "name": format!("r{}", i),
461 "value": format!("0x{:016x}", value),
462 "type": "u64"
463 })
464 })
465 .collect();
466 json!({ "registers": regs })
467 }
468
469 fn get_memory(&self, address: u64, size: usize) -> Value {
470 match self.get_memory(address, size) {
471 Some(data) => json!({
472 "address": address,
473 "size": size,
474 "data": data
475 }),
476 None => json!({
477 "address": address,
478 "size": size,
479 "data": []
480 }),
481 }
482 }
483
484 fn set_register(&mut self, index: usize, value: u64) -> Value {
485 match self.set_register_value(index, value) {
486 Ok(()) => json!({
487 "type": "setRegister",
488 "index": index,
489 "value": value,
490 "success": true
491 }),
492 Err(e) => json!({
493 "type": "setRegister",
494 "index": index,
495 "value": value,
496 "success": false,
497 "error": e
498 }),
499 }
500 }
501
502 fn get_rodata(&self) -> Value {
503 match self.get_rodata() {
504 Some(symbols) => {
505 let arr: Vec<Value> = symbols
506 .iter()
507 .map(|sym| {
508 json!({
509 "name": sym.name,
510 "address": format!("0x{:016x}", sym.address),
511 "value": sym.content
512 })
513 })
514 .collect();
515 json!({ "rodata": arr })
516 }
517 None => json!({ "rodata": [] }),
518 }
519 }
520
521 fn clear_breakpoints(&mut self, _file: String) -> Value {
522 self.clear_breakpoints();
523 json!({"result": "ok"})
524 }
525
526 fn quit(&mut self) -> Value {
527 json!({ "type": "quit" })
528 }
529
530 fn get_compute_units(&self) -> Value {
531 let used = self.get_compute_units();
532 let total = self.initial_compute_budget;
533 let remaining = total.saturating_sub(used);
534 json!({
535 "total": total,
536 "used": used,
537 "remaining": remaining
538 })
539 }
540
541 fn run_to_json(&mut self) -> Value {
542 match self.run() {
543 Ok(event) => match event {
544 DebugEvent::Next(pc, line) => json!({
545 "type": "next",
546 "pc": pc,
547 "line": line
548 }),
549 DebugEvent::Breakpoint(pc, line) => json!({
550 "type": "breakpoint",
551 "pc": pc,
552 "line": line
553 }),
554 DebugEvent::Exit(code) => {
555 let cu = DebuggerInterface::get_compute_units(self);
556 json!({
557 "type": "exit",
558 "code": code,
559 "compute_units": cu
560 })
561 }
562 DebugEvent::Error(msg) => json!({
563 "type": "error",
564 "message": msg
565 }),
566 },
567 Err(e) => json!({
568 "type": "error",
569 "message": format!("{:?}", e)
570 }),
571 }
572 }
573}