Skip to content

Commit

Permalink
gdb: improve reliability and error messages during disconnects
Browse files Browse the repository at this point in the history
If the device goes away (for example, during a reboot), reconnect to it.

The gdb client shouldn't need to care that this has happened, but it'd
be a good idea to print out messages on the console.

Signed-off-by: Sean Cross <sean@xobs.io>
  • Loading branch information
xobs committed Jul 25, 2019
1 parent 439870a commit 505cdfa
Show file tree
Hide file tree
Showing 3 changed files with 60 additions and 34 deletions.
2 changes: 1 addition & 1 deletion wishbone-tool/src/gdb.rs
Original file line number Diff line number Diff line change
Expand Up @@ -382,7 +382,7 @@ impl GdbServer {
} else if pkt == "vMustReplyEmpty" {
Ok(GdbCommand::MustReplyEmpty)
} else {
info!("Unrecognized GDB command: {}", pkt);
info!("unrecognized GDB command: {}", pkt);
Ok(GdbCommand::Unknown(pkt))
}
}
Expand Down
33 changes: 20 additions & 13 deletions wishbone-tool/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ impl std::convert::From<riscv::RiscvCpuError> for ServerError {
fn list_usb() -> Result<(), libusb::Error> {
let usb_ctx = libusb::Context::new().unwrap();
let devices = usb_ctx.devices().unwrap();
println!("Devices:");
println!("devices:");
for device in devices.iter() {
let device_desc = device.device_descriptor().unwrap();
let mut line = format!(
Expand Down Expand Up @@ -107,47 +107,54 @@ fn gdb_server(cfg: Config, bridge: Bridge) -> Result<(), ServerError> {
let connection = {
let listener = match TcpListener::bind(format!("{}:{}", cfg.bind_addr, cfg.bind_port)){
Ok(o) => o,
Err(e) => { error!("Couldn't bind to address: {:?}", e); return Err(ServerError::IoError(e));},
Err(e) => { error!("couldn't bind to address: {:?}", e); return Err(ServerError::IoError(e));},
};

// accept connections and process them serially
info!(
"Accepting connections on {}:{}",
"accepting connections on {}:{}",
cfg.bind_addr, cfg.bind_port
);
let (connection, _sockaddr) = match listener.accept() {
Ok(o) => o,
Err(e) => {error!("Couldn't accept connection: {:?}", e); return Err(ServerError::IoError(e));},
Err(e) => {error!("couldn't accept connection: {:?}", e); return Err(ServerError::IoError(e));},
};
let peer_addr = match connection.peer_addr() {
Ok(o) => o,
Err(e) => {error!("Couldn't get remote address: {:?}", e); return Err(ServerError::IoError(e)); },
Err(e) => {error!("couldn't get remote address: {:?}", e); return Err(ServerError::IoError(e)); },
};
info!("Connection from {}", peer_addr);
info!("connection from {}", peer_addr);
connection
};

let mut gdb = gdb::GdbServer::new(connection).unwrap();
let cpu_controller = cpu.get_controller();
let mut gdb_controller = gdb.get_controller();
if let Err(e) = cpu.halt(&bridge) {
error!("Couldn't halt CPU: {:?}", e);
error!("couldn't halt CPU: {:?}", e);
continue;
}

let poll_bridge = bridge.clone();
thread::spawn(move || loop {
if let Err(e) = cpu_controller.poll(&poll_bridge, &mut gdb_controller) {
error!("Error while polling bridge: {:?}", e);
return;
let mut had_error = false;
loop {
if let Err(e) = cpu_controller.poll(&poll_bridge, &mut gdb_controller) {
if ! had_error {
error!("error while polling bridge: {:?}", e);
had_error = true;
}
} else {
had_error = false;
}
thread::park_timeout(Duration::from_millis(200));
}
thread::park_timeout(Duration::from_millis(200));
});

loop {
let cmd = match gdb.get_command() {
Err(e) => {
error!("Unable to read command from GDB client: {:?}", e);
error!("unable to read command from GDB client: {:?}", e);
break;
}
Ok(o) => o
Expand All @@ -156,7 +163,7 @@ fn gdb_server(cfg: Config, bridge: Bridge) -> Result<(), ServerError> {
if let Err(e) = gdb.process(cmd, &cpu, &bridge) {
match e {
gdb::GdbServerError::ConnectionClosed => (),
e => error!("Error in GDB server: {:?}", e),
e => error!("error in GDB server: {:?}", e),
}
break;
}
Expand Down
59 changes: 39 additions & 20 deletions wishbone-tool/src/usb_bridge.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,10 @@ use std::sync::{Arc, Mutex, Condvar};
use std::thread;
use std::time::Duration;

use log::error;
use log::{debug, error, info};

use super::bridge::BridgeError;
use super::config::Config;
// use log::debug;

#[derive(Clone)]
pub struct UsbBridge {
Expand Down Expand Up @@ -109,30 +108,36 @@ impl UsbBridge {
) {
let mut pid = pid;
let mut vid = vid;
let mut print_waiting_message = true;
let &(ref response, ref cvar) = &*tx;
loop {
let devices = usb_ctx.devices().unwrap();
for device in devices.iter() {
let device_desc = device.device_descriptor().unwrap();
if Self::device_matches(&device_desc, &pid, &vid) {
// println!(
// "Opening device {:03} on bus {:03}",
// device.bus_number(),
// device.address()
// );
let usb = device.open().expect("Unable to open USB device");
{
*response.lock().unwrap() = Some(ConnectThreadResponses::OpenedDevice);
cvar.notify_one();
}
let usb = match device.open() {
Ok(o) => {
info!("opened USB device device {:03} on bus {:03}",
device.address(),
device.bus_number());
*response.lock().unwrap() = Some(ConnectThreadResponses::OpenedDevice);
cvar.notify_one();
print_waiting_message = true;
o
},
Err(e) => {
error!("unable to open usb device: {:?}", e);
continue;
},
};
let mut keep_going = true;
while keep_going {
let var = rx.recv();
match var {
Err(e) => panic!("error in connect thread: {}", e),
Ok(o) => match o {
ConnectThreadRequests::Exit => {
// println!("usb_connect_thread requested exit");
debug!("usb_connect_thread requested exit");
return;
}
ConnectThreadRequests::StartPolling(p, v) => {
Expand All @@ -156,15 +161,25 @@ impl UsbBridge {
}
}
}
println!("No device available, pausing");

// Only print out the message the first time.
// This value gets re-set to `true` whenever there
// is a successful USB connection.
if print_waiting_message {
info!("waiting for target device");
print_waiting_message = false;
}
thread::park_timeout(Duration::from_millis(500));

// Respond to any messages in the buffer with NotConnected. As soon
// as the channel is empty, loop back to the start of this function.
loop {
match rx.try_recv() {
Err(TryRecvError::Empty) => break,
Err(TryRecvError::Disconnected) => panic!("main thread disconnected"),
Ok(m) => match m {
ConnectThreadRequests::Exit => {
println!("main thread requested exit");
debug!("main thread requested exit");
return;
}
ConnectThreadRequests::Peek(_addr) => {
Expand Down Expand Up @@ -286,10 +301,14 @@ impl UsbBridge {

impl Drop for UsbBridge {
fn drop(&mut self) {
let &(ref lock, ref _cvar) = &*self.main_rx;
let mut _mtx = lock.lock().unwrap();
self.main_tx
.send(ConnectThreadRequests::Exit)
.expect("Unable to send Exit request to thread");
// If this is the last reference to the bridge, tell the control thread
// to exit.
if Arc::strong_count(&self.main_rx) + Arc::weak_count(&self.main_rx) <= 1 {
let &(ref lock, ref _cvar) = &*self.main_rx;
let mut _mtx = lock.lock().unwrap();
self.main_tx
.send(ConnectThreadRequests::Exit)
.expect("Unable to send Exit request to thread");
}
}
}

0 comments on commit 505cdfa

Please sign in to comment.