Skip to main content

sbpf_debugger/
debugger.rs

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        // Print collected logs.
173        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                // If call depth increases after step, run until it returns to previous depth.
185                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                // If call depth > 0, run until it decreases by one level.
198                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(&current_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    // Execute a single step.
246    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(&current_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    // Step through instructions until the current call depth reaches the target depth.
294    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        // Resolve rodata label.
354        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        // Current frame
413        let current_pc = self.get_pc();
414        frames.push(self.make_stack_frame(0, current_pc));
415
416        // Call stack frames
417        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}