Skip to main content

sbpf_debugger/
adapter.rs

1use {
2    serde::{Deserialize, Serialize},
3    serde_json::{Value, json},
4    std::io::{self, BufRead, Write},
5};
6
7pub trait DebuggerInterface {
8    fn step(&mut self) -> Value;
9    fn next(&mut self) -> Value;
10    fn finish(&mut self) -> Value;
11    fn r#continue(&mut self) -> Value;
12    fn set_breakpoint(&mut self, file: String, line: usize) -> Value;
13    fn remove_breakpoint(&mut self, file: String, line: usize) -> Value;
14    fn get_stack_frames(&self) -> Value;
15    fn get_registers(&self) -> Value;
16    fn get_memory(&self, address: u64, size: usize) -> Value;
17    fn set_register(&mut self, index: usize, value: u64) -> Value;
18    fn get_rodata(&self) -> Value;
19    fn clear_breakpoints(&mut self, file: String) -> Value;
20    fn quit(&mut self) -> Value;
21    fn get_compute_units(&self) -> Value;
22    fn run_to_json(&mut self) -> Value;
23}
24
25#[derive(Deserialize)]
26struct AdapterCommand {
27    command: String,
28    args: Option<Value>,
29    #[serde(rename = "requestId")]
30    request_id: Option<Value>,
31}
32
33#[derive(Serialize)]
34struct AdapterResponse {
35    success: bool,
36    data: Option<Value>,
37    error: Option<String>,
38    #[serde(rename = "requestId")]
39    request_id: Option<Value>,
40}
41
42pub fn run_adapter_loop<T: DebuggerInterface>(debugger: &mut T) {
43    let stdin = io::stdin();
44    let mut stdout = io::stdout();
45    for line in stdin.lock().lines() {
46        let line = match line {
47            Ok(l) => l,
48            Err(_) => break,
49        };
50        if line.trim().is_empty() {
51            continue;
52        }
53        let cmd: Result<AdapterCommand, _> = serde_json::from_str(&line);
54        let mut response = AdapterResponse {
55            success: true,
56            data: None,
57            error: None,
58            request_id: None,
59        };
60        match cmd {
61            Ok(cmd) => {
62                response.request_id = cmd.request_id.clone();
63                let result = match cmd.command.as_str() {
64                    "step" => debugger.step(),
65                    "next" => debugger.next(),
66                    "finish" => debugger.finish(),
67                    "continue" => debugger.r#continue(),
68                    "setBreakpoint" => {
69                        if let Some(args) = cmd.args {
70                            let file = args
71                                .get(0)
72                                .and_then(Value::as_str)
73                                .unwrap_or("")
74                                .to_string();
75                            let line = args.get(1).and_then(Value::as_u64).unwrap_or(0) as usize;
76                            debugger.set_breakpoint(file, line)
77                        } else {
78                            json!({"type": "error", "message": "Missing args"})
79                        }
80                    }
81                    "removeBreakpoint" => {
82                        if let Some(args) = cmd.args {
83                            let file = args
84                                .get(0)
85                                .and_then(Value::as_str)
86                                .unwrap_or("")
87                                .to_string();
88                            let line = args.get(1).and_then(Value::as_u64).unwrap_or(0) as usize;
89                            debugger.remove_breakpoint(file, line)
90                        } else {
91                            json!({"type": "error", "message": "Missing args"})
92                        }
93                    }
94                    "getStackFrames" => debugger.get_stack_frames(),
95                    "getRegisters" => debugger.get_registers(),
96                    "getRodata" => debugger.get_rodata(),
97                    "clearBreakpoints" => {
98                        if let Some(args) = cmd.args {
99                            let file = args
100                                .get(0)
101                                .and_then(Value::as_str)
102                                .unwrap_or("")
103                                .to_string();
104                            debugger.clear_breakpoints(file)
105                        } else {
106                            json!({"type": "error", "message": "Missing args"})
107                        }
108                    }
109                    "getMemory" => {
110                        if let Some(args) = cmd.args {
111                            let address = args.get(0).and_then(Value::as_u64).unwrap_or(0);
112                            let size = args.get(1).and_then(Value::as_u64).unwrap_or(0) as usize;
113                            debugger.get_memory(address, size)
114                        } else {
115                            json!({"type": "error", "message": "Missing args"})
116                        }
117                    }
118                    "getComputeUnits" => debugger.get_compute_units(),
119                    "setRegister" => {
120                        if let Some(args) = cmd.args {
121                            let index = args.get(0).and_then(Value::as_u64).unwrap_or(0) as usize;
122                            let value = args.get(1).and_then(Value::as_u64).unwrap_or(0);
123                            debugger.set_register(index, value)
124                        } else {
125                            json!({"type": "error", "message": "Missing args"})
126                        }
127                    }
128                    "quit" => debugger.quit(),
129                    _ => json!({"type": "error", "message": "Unknown command"}),
130                };
131                // Check if the result contains an error
132                if let Some(result_obj) = result.as_object() {
133                    if result_obj.contains_key("error") {
134                        response.success = false;
135                        response.error = result_obj
136                            .get("error")
137                            .and_then(|v| v.as_str())
138                            .map(|s| s.to_string());
139                    } else if result_obj.get("type").and_then(|v| v.as_str()) == Some("error") {
140                        response.success = false;
141                        response.error = result_obj
142                            .get("message")
143                            .and_then(|v| v.as_str())
144                            .map(|s| s.to_string());
145                    }
146                }
147                response.data = Some(result);
148            }
149            Err(e) => {
150                response.success = false;
151                response.error = Some(format!("Invalid command: {}", e));
152            }
153        }
154        let resp_str = serde_json::to_string(&response).unwrap();
155        writeln!(stdout, "{}", resp_str).unwrap();
156        stdout.flush().unwrap();
157    }
158}