Do not error on debugger server connection close (#31795)

Start to show islands of logs in a successful debugger runs for Rust:

<img width="1728" alt="1"
src="https://github.com/user-attachments/assets/7400201b-b900-4b20-8adf-21850ae59671"
/>

<img width="1728" alt="2"
src="https://github.com/user-attachments/assets/e75cc5f1-1f74-41d6-b7aa-697a4b2f055b"
/>

Release Notes:

- N/A
This commit is contained in:
Kirill Bulatov 2025-05-31 01:37:40 +03:00 committed by GitHub
parent bb9e2b0403
commit fe1b36671d
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -4,7 +4,7 @@ use dap_types::{
messages::{Message, Response}, messages::{Message, Response},
}; };
use futures::{AsyncRead, AsyncReadExt as _, AsyncWrite, FutureExt as _, channel::oneshot, select}; use futures::{AsyncRead, AsyncReadExt as _, AsyncWrite, FutureExt as _, channel::oneshot, select};
use gpui::AsyncApp; use gpui::{AppContext as _, AsyncApp, Task};
use settings::Settings as _; use settings::Settings as _;
use smallvec::SmallVec; use smallvec::SmallVec;
use smol::{ use smol::{
@ -22,7 +22,7 @@ use std::{
time::Duration, time::Duration,
}; };
use task::TcpArgumentsTemplate; use task::TcpArgumentsTemplate;
use util::{ResultExt as _, TryFutureExt}; use util::{ConnectionResult, ResultExt as _};
use crate::{adapters::DebugAdapterBinary, debugger_settings::DebuggerSettings}; use crate::{adapters::DebugAdapterBinary, debugger_settings::DebuggerSettings};
@ -126,7 +126,7 @@ pub(crate) struct TransportDelegate {
pending_requests: Requests, pending_requests: Requests,
transport: Transport, transport: Transport,
server_tx: Arc<Mutex<Option<Sender<Message>>>>, server_tx: Arc<Mutex<Option<Sender<Message>>>>,
_tasks: Vec<gpui::Task<Option<()>>>, _tasks: Vec<Task<()>>,
} }
impl TransportDelegate { impl TransportDelegate {
@ -141,7 +141,7 @@ impl TransportDelegate {
log_handlers: Default::default(), log_handlers: Default::default(),
current_requests: Default::default(), current_requests: Default::default(),
pending_requests: Default::default(), pending_requests: Default::default(),
_tasks: Default::default(), _tasks: Vec::new(),
}; };
let messages = this.start_handlers(transport_pipes, cx).await?; let messages = this.start_handlers(transport_pipes, cx).await?;
Ok((messages, this)) Ok((messages, this))
@ -166,45 +166,76 @@ impl TransportDelegate {
None None
}; };
let adapter_log_handler = log_handler.clone();
cx.update(|cx| { cx.update(|cx| {
if let Some(stdout) = params.stdout.take() { if let Some(stdout) = params.stdout.take() {
self._tasks.push( self._tasks.push(cx.background_spawn(async move {
cx.background_executor() match Self::handle_adapter_log(stdout, adapter_log_handler).await {
.spawn(Self::handle_adapter_log(stdout, log_handler.clone()).log_err()), ConnectionResult::Timeout => {
); log::error!("Timed out when handling debugger log");
}
ConnectionResult::ConnectionReset => {
log::info!("Debugger logs connection closed");
}
ConnectionResult::Result(Ok(())) => {}
ConnectionResult::Result(Err(e)) => {
log::error!("Error handling debugger log: {e}");
}
}
}));
} }
self._tasks.push( let pending_requests = self.pending_requests.clone();
cx.background_executor().spawn( let output_log_handler = log_handler.clone();
Self::handle_output( self._tasks.push(cx.background_spawn(async move {
match Self::handle_output(
params.output, params.output,
client_tx, client_tx,
self.pending_requests.clone(), pending_requests,
log_handler.clone(), output_log_handler,
) )
.log_err(), .await
), {
); Ok(()) => {}
Err(e) => log::error!("Error handling debugger output: {e}"),
}
}));
if let Some(stderr) = params.stderr.take() { if let Some(stderr) = params.stderr.take() {
self._tasks.push( let log_handlers = self.log_handlers.clone();
cx.background_executor() self._tasks.push(cx.background_spawn(async move {
.spawn(Self::handle_error(stderr, self.log_handlers.clone()).log_err()), match Self::handle_error(stderr, log_handlers).await {
); ConnectionResult::Timeout => {
log::error!("Timed out reading debugger error stream")
}
ConnectionResult::ConnectionReset => {
log::info!("Debugger closed its error stream")
}
ConnectionResult::Result(Ok(())) => {}
ConnectionResult::Result(Err(e)) => {
log::error!("Error handling debugger error: {e}")
}
}
}));
} }
self._tasks.push( let current_requests = self.current_requests.clone();
cx.background_executor().spawn( let pending_requests = self.pending_requests.clone();
Self::handle_input( let log_handler = log_handler.clone();
self._tasks.push(cx.background_spawn(async move {
match Self::handle_input(
params.input, params.input,
client_rx, client_rx,
self.current_requests.clone(), current_requests,
self.pending_requests.clone(), pending_requests,
log_handler.clone(), log_handler,
) )
.log_err(), .await
), {
); Ok(()) => {}
Err(e) => log::error!("Error handling debugger input: {e}"),
}
}));
})?; })?;
{ {
@ -235,7 +266,7 @@ impl TransportDelegate {
async fn handle_adapter_log<Stdout>( async fn handle_adapter_log<Stdout>(
stdout: Stdout, stdout: Stdout,
log_handlers: Option<LogHandlers>, log_handlers: Option<LogHandlers>,
) -> Result<()> ) -> ConnectionResult<()>
where where
Stdout: AsyncRead + Unpin + Send + 'static, Stdout: AsyncRead + Unpin + Send + 'static,
{ {
@ -245,13 +276,14 @@ impl TransportDelegate {
let result = loop { let result = loop {
line.truncate(0); line.truncate(0);
let bytes_read = match reader.read_line(&mut line).await { match reader
Ok(bytes_read) => bytes_read, .read_line(&mut line)
Err(e) => break Err(e.into()), .await
}; .context("reading adapter log line")
{
if bytes_read == 0 { Ok(0) => break ConnectionResult::ConnectionReset,
anyhow::bail!("Debugger log stream closed"); Ok(_) => {}
Err(e) => break ConnectionResult::Result(Err(e)),
} }
if let Some(log_handlers) = log_handlers.as_ref() { if let Some(log_handlers) = log_handlers.as_ref() {
@ -337,35 +369,35 @@ impl TransportDelegate {
let mut reader = BufReader::new(server_stdout); let mut reader = BufReader::new(server_stdout);
let result = loop { let result = loop {
let message = match Self::receive_server_message(&mut reader, &mut recv_buffer, log_handlers.as_ref())
Self::receive_server_message(&mut reader, &mut recv_buffer, log_handlers.as_ref()) .await
.await; {
ConnectionResult::Timeout => anyhow::bail!("Timed out when connecting to debugger"),
match message { ConnectionResult::ConnectionReset => {
Ok(Message::Response(res)) => { log::info!("Debugger closed the connection");
return Ok(());
}
ConnectionResult::Result(Ok(Message::Response(res))) => {
if let Some(tx) = pending_requests.lock().await.remove(&res.request_seq) { if let Some(tx) = pending_requests.lock().await.remove(&res.request_seq) {
if let Err(e) = tx.send(Self::process_response(res)) { if let Err(e) = tx.send(Self::process_response(res)) {
log::trace!("Did not send response `{:?}` for a cancelled", e); log::trace!("Did not send response `{:?}` for a cancelled", e);
} }
} else { } else {
client_tx.send(Message::Response(res)).await?; client_tx.send(Message::Response(res)).await?;
};
} }
Ok(message) => {
client_tx.send(message).await?;
} }
Err(e) => break Err(e), ConnectionResult::Result(Ok(message)) => client_tx.send(message).await?,
ConnectionResult::Result(Err(e)) => break Err(e),
} }
}; };
drop(client_tx); drop(client_tx);
log::debug!("Handle adapter output dropped"); log::debug!("Handle adapter output dropped");
result result
} }
async fn handle_error<Stderr>(stderr: Stderr, log_handlers: LogHandlers) -> Result<()> async fn handle_error<Stderr>(stderr: Stderr, log_handlers: LogHandlers) -> ConnectionResult<()>
where where
Stderr: AsyncRead + Unpin + Send + 'static, Stderr: AsyncRead + Unpin + Send + 'static,
{ {
@ -375,8 +407,12 @@ impl TransportDelegate {
let mut reader = BufReader::new(stderr); let mut reader = BufReader::new(stderr);
let result = loop { let result = loop {
match reader.read_line(&mut buffer).await { match reader
Ok(0) => anyhow::bail!("debugger error stream closed"), .read_line(&mut buffer)
.await
.context("reading error log line")
{
Ok(0) => break ConnectionResult::ConnectionReset,
Ok(_) => { Ok(_) => {
for (kind, log_handler) in log_handlers.lock().iter_mut() { for (kind, log_handler) in log_handlers.lock().iter_mut() {
if matches!(kind, LogKind::Adapter) { if matches!(kind, LogKind::Adapter) {
@ -386,7 +422,7 @@ impl TransportDelegate {
buffer.truncate(0); buffer.truncate(0);
} }
Err(error) => break Err(error.into()), Err(error) => break ConnectionResult::Result(Err(error)),
} }
}; };
@ -420,7 +456,7 @@ impl TransportDelegate {
reader: &mut BufReader<Stdout>, reader: &mut BufReader<Stdout>,
buffer: &mut String, buffer: &mut String,
log_handlers: Option<&LogHandlers>, log_handlers: Option<&LogHandlers>,
) -> Result<Message> ) -> ConnectionResult<Message>
where where
Stdout: AsyncRead + Unpin + Send + 'static, Stdout: AsyncRead + Unpin + Send + 'static,
{ {
@ -428,48 +464,58 @@ impl TransportDelegate {
loop { loop {
buffer.truncate(0); buffer.truncate(0);
if reader match reader
.read_line(buffer) .read_line(buffer)
.await .await
.with_context(|| "reading a message from server")? .with_context(|| "reading a message from server")
== 0
{ {
anyhow::bail!("debugger reader stream closed, last string output: '{buffer}'"); Ok(0) => return ConnectionResult::ConnectionReset,
Ok(_) => {}
Err(e) => return ConnectionResult::Result(Err(e)),
}; };
if buffer == "\r\n" { if buffer == "\r\n" {
break; break;
} }
let parts = buffer.trim().split_once(": "); if let Some(("Content-Length", value)) = buffer.trim().split_once(": ") {
match value.parse().context("invalid content length") {
match parts { Ok(length) => content_length = Some(length),
Some(("Content-Length", value)) => { Err(e) => return ConnectionResult::Result(Err(e)),
content_length = Some(value.parse().context("invalid content length")?);
} }
_ => {}
} }
} }
let content_length = content_length.context("missing content length")?; let content_length = match content_length.context("missing content length") {
Ok(length) => length,
Err(e) => return ConnectionResult::Result(Err(e)),
};
let mut content = vec![0; content_length]; let mut content = vec![0; content_length];
reader if let Err(e) = reader
.read_exact(&mut content) .read_exact(&mut content)
.await .await
.with_context(|| "reading after a loop")?; .with_context(|| "reading after a loop")
{
return ConnectionResult::Result(Err(e));
}
let message = std::str::from_utf8(&content).context("invalid utf8 from server")?; let message_str = match std::str::from_utf8(&content).context("invalid utf8 from server") {
Ok(str) => str,
Err(e) => return ConnectionResult::Result(Err(e)),
};
if let Some(log_handlers) = log_handlers { if let Some(log_handlers) = log_handlers {
for (kind, log_handler) in log_handlers.lock().iter_mut() { for (kind, log_handler) in log_handlers.lock().iter_mut() {
if matches!(kind, LogKind::Rpc) { if matches!(kind, LogKind::Rpc) {
log_handler(IoKind::StdOut, &message); log_handler(IoKind::StdOut, message_str);
} }
} }
} }
Ok(serde_json::from_str::<Message>(message)?) ConnectionResult::Result(
serde_json::from_str::<Message>(message_str).context("deserializing server message"),
)
} }
pub async fn shutdown(&self) -> Result<()> { pub async fn shutdown(&self) -> Result<()> {
@ -777,21 +823,23 @@ impl FakeTransport {
let response_handlers = this.response_handlers.clone(); let response_handlers = this.response_handlers.clone();
let stdout_writer = Arc::new(Mutex::new(stdout_writer)); let stdout_writer = Arc::new(Mutex::new(stdout_writer));
cx.background_executor() cx.background_spawn(async move {
.spawn(async move {
let mut reader = BufReader::new(stdin_reader); let mut reader = BufReader::new(stdin_reader);
let mut buffer = String::new(); let mut buffer = String::new();
loop { loop {
let message = match TransportDelegate::receive_server_message(&mut reader, &mut buffer, None)
TransportDelegate::receive_server_message(&mut reader, &mut buffer, None) .await
.await; {
ConnectionResult::Timeout => {
match message { anyhow::bail!("Timed out when connecting to debugger");
Err(error) => {
break anyhow::anyhow!(error);
} }
Ok(message) => { ConnectionResult::ConnectionReset => {
log::info!("Debugger closed the connection");
break Ok(());
}
ConnectionResult::Result(Err(e)) => break Err(e),
ConnectionResult::Result(Ok(message)) => {
match message { match message {
Message::Request(request) => { Message::Request(request) => {
// redirect reverse requests to stdout writer/reader // redirect reverse requests to stdout writer/reader
@ -799,8 +847,7 @@ impl FakeTransport {
|| request.command == StartDebugging::COMMAND || request.command == StartDebugging::COMMAND
{ {
let message = let message =
serde_json::to_string(&Message::Request(request)) serde_json::to_string(&Message::Request(request)).unwrap();
.unwrap();
let mut writer = stdout_writer.lock().await; let mut writer = stdout_writer.lock().await;
writer writer
@ -812,14 +859,10 @@ impl FakeTransport {
.unwrap(); .unwrap();
writer.flush().await.unwrap(); writer.flush().await.unwrap();
} else { } else {
let response = if let Some(handle) = request_handlers let response = if let Some(handle) =
.lock() request_handlers.lock().get_mut(request.command.as_str())
.get_mut(request.command.as_str())
{ {
handle( handle(request.seq, request.arguments.unwrap_or(json!({})))
request.seq,
request.arguments.unwrap_or(json!({})),
)
} else { } else {
panic!("No request handler for {}", request.command); panic!("No request handler for {}", request.command);
}; };
@ -846,8 +889,7 @@ impl FakeTransport {
let mut writer = stdout_writer.lock().await; let mut writer = stdout_writer.lock().await;
writer writer
.write_all( .write_all(
TransportDelegate::build_rpc_message(message) TransportDelegate::build_rpc_message(message).as_bytes(),
.as_bytes(),
) )
.await .await
.unwrap(); .unwrap();