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