use std::collections::HashMap;
use std::io::{BufRead, BufReader, Read, Write};
use std::sync::{Arc, Mutex};
mod stream_output;
use stream_output::stream_output;
mod exec_result;
use exec_result::exec_result;
use log::{debug, trace, warn};
use crate::mi::{MIResponse, data_read_sp_bytes, parse_key_value_pairs, parse_mi_response};
use crate::{PtrSize, State, Written};
pub fn gdb_interact(gdb_stdout: BufReader<Box<dyn Read + Send>>, state: Arc<Mutex<State>>) {
let mut current_map = (None, String::new());
for line in gdb_stdout.lines() {
if let Ok(line) = line {
trace!("{:?}", line);
let mut state = state.lock().unwrap();
let response = parse_mi_response(&line);
trace!("response {:?}", response);
match &response {
MIResponse::AsyncRecord(reason, kv) => {
if reason == "stopped" {
async_record_stopped(&mut state, kv);
}
}
MIResponse::ExecResult(status, kv) => {
exec_result(&mut state, status, &mut current_map, kv);
}
MIResponse::StreamOutput(t, s) => {
stream_output(t, s, &mut state, &mut current_map);
}
MIResponse::Unknown(s) => {
state.stream_output_prompt = s.to_string();
}
_ => (),
}
}
}
}
fn async_record_stopped(state: &mut State, kv: &HashMap<String, String>) {
state.async_result.clear();
state.async_result.push_str("Status: ");
if kv.get("bkptno").is_some() {
if let Some(val) = kv.get("bkptno") {
state.async_result.push_str(&format!("bkptno={val}, "));
}
} else if kv.get("signal-name").is_some() {
if let Some(val) = kv.get("signal-name") {
state.async_result.push_str(&format!("signal-name={val}"));
}
if let Some(val) = kv.get("signal-meaning") {
state.async_result.push_str(&format!(", signal-meaning={val}, "));
}
}
if let Some(val) = kv.get("reason") {
state.async_result.push_str(&format!("reason={val}"));
}
if let Some(val) = kv.get("stopped-threads") {
state.async_result.push_str(&format!(", stopped-threads={val}"));
}
if let Some(val) = kv.get("thread-id") {
state.async_result.push_str(&format!(", thread-id={val}"));
}
if state.ptr_size == PtrSize::Auto {
state.next_write.push("-data-evaluate-expression \"sizeof(long)\"".to_string());
state.written.push_back(Written::SizeOfVoidStar);
}
state.next_write.push(r#"-interpreter-exec console "info proc mappings""#.to_string());
state.next_write.push(r#"-interpreter-exec console "show endian""#.to_string());
state.next_write.push("-data-list-register-names".to_string());
state.next_write.push("-data-list-register-values x".to_string());
state.next_write.push("-data-list-changed-registers".to_string());
state.next_write.push("-stack-list-frames".to_string());
if let (Some(fullname), Some(line)) = (kv.get("fullname"), kv.get("line")) {
debug!("Source location from stopped event: {}:{}", fullname, line);
if let Ok(line_num) = line.parse::<u32>() {
state.current_source_file = Some(fullname.clone());
state.current_source_line = Some(line_num);
if let Ok(content) = std::fs::read_to_string(std::path::Path::new(fullname)) {
state.source_lines = content.lines().map(|s| s.to_string()).collect();
debug!("Read {} lines from source file", state.source_lines.len());
} else {
warn!("Could not read source file: {}", fullname);
state.source_lines.clear();
}
}
} else if let (Some(file), Some(line)) = (kv.get("file"), kv.get("line")) {
debug!("Source location from stopped event (fallback): {}:{}", file, line);
if let Ok(line_num) = line.parse::<u32>() {
state.current_source_file = Some(file.clone());
state.current_source_line = Some(line_num);
if let Ok(content) = std::fs::read_to_string(std::path::Path::new(file)) {
state.source_lines = content.lines().map(|s| s.to_string()).collect();
debug!("Read {} lines from source file", state.source_lines.len());
} else {
warn!("Could not read source file: {}", file);
state.source_lines.clear();
}
}
} else {
debug!("No source location information in stopped event");
state.current_source_file = None;
state.current_source_line = None;
state.source_lines.clear();
}
}
fn read_memory(memory: &String) -> (HashMap<String, String>, String) {
let mem_str = memory.strip_prefix(r#"[{"#).unwrap();
let mem_str = mem_str.strip_suffix(r#"}]"#).unwrap();
let data = parse_key_value_pairs(mem_str);
let begin = data["begin"].to_string();
let begin = begin.strip_prefix("0x").unwrap().to_string();
(data, begin)
}
fn dump_sp_bytes(state: &mut State, size: u64, amt: u64) {
let mut curr_offset = 0;
for _ in 0..amt {
state.next_write.push(data_read_sp_bytes(curr_offset, size));
state.written.push_back(Written::Stack(None));
curr_offset += size;
}
}
pub fn write_mi(gdb_stdin_arc: &Arc<Mutex<dyn Write + Send>>, w: &str) {
let mut stdin = gdb_stdin_arc.lock().unwrap();
debug!("writing {}", w);
writeln!(stdin, "{}", w).expect("Failed to send command");
}