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:
parent
bb9e2b0403
commit
fe1b36671d
1 changed files with 190 additions and 148 deletions
|
@ -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();
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue