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