From 2cc7bfc5cdf9b0dedcae68a81d8a44ea288551a1 Mon Sep 17 00:00:00 2001 From: Dariusz Doktorski Date: Thu, 27 Nov 2025 15:55:15 +0100 Subject: [PATCH 1/5] Support pause and continue requests --- src/debugger.rs | 23 ++++++++++++++++++++--- src/debugger/handler.rs | 14 ++++++++++---- 2 files changed, 30 insertions(+), 7 deletions(-) diff --git a/src/debugger.rs b/src/debugger.rs index 4c5e1df..ca80d67 100644 --- a/src/debugger.rs +++ b/src/debugger.rs @@ -1,4 +1,4 @@ -use anyhow::Result; +use anyhow::{Result, bail}; use cairo_vm::vm::vm_core::VirtualMachine; use tracing::debug; @@ -35,8 +35,25 @@ impl CairoDebugger { } fn sync_with_vm(&self, _vm: &VirtualMachine) -> Result<()> { - while let Some(request) = self.connection.try_next_request()? { - self.handle_request(request)?; + while let Some(request) = self.connection.try_next_request()? + && let HandleResult::Trigger(NextAction::Stop) = self.handle_request(request)? + { + self.process_until_resume()?; + } + + Ok(()) + } + + fn process_until_resume(&self) -> Result<()> { + loop { + let request = self.connection.next_request()?; + match self.handle_request(request)? { + HandleResult::Trigger(NextAction::Resume) => break, + HandleResult::Trigger(NextAction::FinishInit) => { + bail!("Unexpected request received during execution"); + } + HandleResult::Handled | HandleResult::Trigger(NextAction::Stop) => {} + } } Ok(()) diff --git a/src/debugger/handler.rs b/src/debugger/handler.rs index 6540fff..a864dab 100644 --- a/src/debugger/handler.rs +++ b/src/debugger/handler.rs @@ -2,8 +2,8 @@ use anyhow::{Result, bail}; use dap::events::{Event, StoppedEventBody}; use dap::prelude::{Command, Request, ResponseBody}; use dap::responses::{ - EvaluateResponse, ScopesResponse, SetBreakpointsResponse, StackTraceResponse, ThreadsResponse, - VariablesResponse, + ContinueResponse, EvaluateResponse, ScopesResponse, SetBreakpointsResponse, StackTraceResponse, + ThreadsResponse, VariablesResponse, }; use dap::types::{Breakpoint, Capabilities, Source, StackFrame, StoppedEventReason, Thread}; use tracing::trace; @@ -16,6 +16,8 @@ pub enum HandleResult { } pub enum NextAction { + Resume, + Stop, FinishInit, } @@ -94,7 +96,11 @@ impl CairoDebugger { Ok(HandleResult::Trigger(NextAction::FinishInit)) } Command::Continue(_) => { - todo!() + self.connection.send_success( + request, + ResponseBody::Continue(ContinueResponse { all_threads_continued: Some(true) }), + )?; + Ok(HandleResult::Trigger(NextAction::Resume)) } Command::Launch(_) => { // Start running the Cairo program here. @@ -115,7 +121,7 @@ impl CairoDebugger { hit_breakpoint_ids: None, }))?; self.connection.send_success(request, ResponseBody::Pause)?; - Ok(HandleResult::Handled) + Ok(HandleResult::Trigger(NextAction::Stop)) } Command::SetBreakpoints(args) => { let mut response_bps = Vec::new(); From d58b5673a2457582993768cf906d998df240bdc9 Mon Sep 17 00:00:00 2001 From: Dariusz Doktorski Date: Thu, 27 Nov 2025 16:00:24 +0100 Subject: [PATCH 2/5] Reorder commands --- src/debugger/handler.rs | 111 ++++++++++++++++++++++------------------ 1 file changed, 60 insertions(+), 51 deletions(-) diff --git a/src/debugger/handler.rs b/src/debugger/handler.rs index a864dab..d512ad7 100644 --- a/src/debugger/handler.rs +++ b/src/debugger/handler.rs @@ -30,7 +30,6 @@ impl CairoDebugger { | Command::Completions(_) | Command::DataBreakpointInfo(_) | Command::Disassemble(_) - | Command::Disconnect(_) | Command::Goto(_) | Command::ExceptionInfo(_) | Command::GotoTargets(_) @@ -54,6 +53,12 @@ impl CairoDebugger { bail!("Unsupported request"); } + // It makes no sense to send `attach` in the current architecture. + Command::Attach(_) => { + self.connection.send_error(request, "Attach is not supported")?; + bail!("Unsupported request"); + } + // These may be supported after the MVP. // Nonetheless, if we receive these with current capabilities, // it is the client's fault. @@ -71,13 +76,9 @@ impl CairoDebugger { bail!("Set function breakpoints is not yet supported"); } - // It makes no sense to send `attach` in the current architecture. - Command::Attach(_) => { - self.connection.send_error(request, "Attach is not supported")?; - bail!("Unsupported request"); - } - // Supported requests. + + // Initialize flow requests. Command::Initialize(args) => { trace!("Initialized a client: {:?}", args.client_name); self.connection.send_success( @@ -90,26 +91,17 @@ impl CairoDebugger { self.connection.send_event(Event::Initialized)?; Ok(HandleResult::Handled) } - Command::ConfigurationDone => { - trace!("Configuration done"); - self.connection.send_success(request, ResponseBody::ConfigurationDone)?; - Ok(HandleResult::Trigger(NextAction::FinishInit)) - } - Command::Continue(_) => { - self.connection.send_success( - request, - ResponseBody::Continue(ContinueResponse { all_threads_continued: Some(true) }), - )?; - Ok(HandleResult::Trigger(NextAction::Resume)) - } Command::Launch(_) => { - // Start running the Cairo program here. self.connection.send_success(request, ResponseBody::Launch)?; Ok(HandleResult::Handled) } - Command::Next(_) => { - todo!() + Command::ConfigurationDone => { + // Start running the Cairo program here. + trace!("Configuration done"); + self.connection.send_success(request, ResponseBody::ConfigurationDone)?; + Ok(HandleResult::Trigger(NextAction::FinishInit)) } + Command::Pause(_) => { self.connection.send_event(Event::Stopped(StoppedEventBody { reason: StoppedEventReason::Pause, @@ -123,6 +115,14 @@ impl CairoDebugger { self.connection.send_success(request, ResponseBody::Pause)?; Ok(HandleResult::Trigger(NextAction::Stop)) } + Command::Continue(_) => { + self.connection.send_success( + request, + ResponseBody::Continue(ContinueResponse { all_threads_continued: Some(true) }), + )?; + Ok(HandleResult::Trigger(NextAction::Resume)) + } + Command::SetBreakpoints(args) => { let mut response_bps = Vec::new(); if let Some(requested_bps) = &args.breakpoints { @@ -144,8 +144,16 @@ impl CairoDebugger { )?; Ok(HandleResult::Handled) } - Command::Source(_) => { - todo!() + + Command::Threads => { + self.connection.send_success( + request, + ResponseBody::Threads(ThreadsResponse { + // Return a single thread. + threads: vec![Thread { id: 0, name: "".to_string() }], + }), + )?; + Ok(HandleResult::Handled) } Command::StackTrace(_) => { self.connection.send_success( @@ -166,12 +174,37 @@ impl CairoDebugger { )?; Ok(HandleResult::Handled) } + Command::Scopes(_) => { + // Return no scopes. + self.connection.send_success( + request, + ResponseBody::Scopes(ScopesResponse { scopes: vec![] }), + )?; + Ok(HandleResult::Handled) + } + Command::Variables(_) => { + self.connection.send_success( + request, + ResponseBody::Variables(VariablesResponse { + // Return no variables. + variables: vec![], + }), + )?; + Ok(HandleResult::Handled) + } + + Command::Next(_) => { + todo!() + } Command::StepIn(_) => { todo!() } Command::StepOut(_) => { todo!() } + Command::Source(_) => { + todo!() + } Command::Evaluate(_) => { self.connection.send_success( @@ -189,33 +222,9 @@ impl CairoDebugger { )?; Ok(HandleResult::Handled) } - Command::Threads => { - self.connection.send_success( - request, - ResponseBody::Threads(ThreadsResponse { - // Return a single thread. - threads: vec![Thread { id: 0, name: "".to_string() }], - }), - )?; - Ok(HandleResult::Handled) - } - Command::Variables(_) => { - self.connection.send_success( - request, - ResponseBody::Variables(VariablesResponse { - // Return no variables. - variables: vec![], - }), - )?; - Ok(HandleResult::Handled) - } - Command::Scopes(_) => { - // Return no scopes. - self.connection.send_success( - request, - ResponseBody::Scopes(ScopesResponse { scopes: vec![] }), - )?; - Ok(HandleResult::Handled) + + Command::Disconnect(_) => { + todo!() } } } From 6e899c73d4d81c57e8c5b21b85db8b70451f6fb1 Mon Sep 17 00:00:00 2001 From: Dariusz Doktorski Date: Fri, 28 Nov 2025 09:26:40 +0100 Subject: [PATCH 3/5] Implement drop for `CairoDebugger` --- src/debugger.rs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/debugger.rs b/src/debugger.rs index ca80d67..fc632db 100644 --- a/src/debugger.rs +++ b/src/debugger.rs @@ -1,5 +1,7 @@ use anyhow::{Result, bail}; use cairo_vm::vm::vm_core::VirtualMachine; +use dap::events::ExitedEventBody; +use dap::prelude::Event::{Exited, Terminated}; use tracing::debug; use crate::connection::Connection; @@ -59,3 +61,12 @@ impl CairoDebugger { Ok(()) } } + +impl Drop for CairoDebugger { + fn drop(&mut self) { + // TODO: Add error tracing + // TODO: Send correct exit code + self.connection.send_event(Terminated(None)).ok(); + self.connection.send_event(Exited(ExitedEventBody { exit_code: 0 })).ok(); + } +} From 0081952f50a677d3621e583d37116689f90f241e Mon Sep 17 00:00:00 2001 From: Dariusz Doktorski Date: Mon, 1 Dec 2025 17:12:40 +0100 Subject: [PATCH 4/5] Trace errors in drop --- src/connection.rs | 3 ++- src/debugger.rs | 14 +++++++++----- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/src/connection.rs b/src/connection.rs index 8686298..1c49bb6 100644 --- a/src/connection.rs +++ b/src/connection.rs @@ -11,6 +11,7 @@ use dap::base_message::Sendable; use dap::errors::ServerError; use dap::prelude::{Event, Request, ResponseBody, Server}; use dap::server::{ServerReader, ServerWriter}; +use tracing::error; pub struct Connection { inbound_rx: mpsc::Receiver, @@ -107,7 +108,7 @@ fn spawn_reader_thread( thread::spawn(move || { while let Ok(Some(request)) = server_reader.poll_request() { if inbound_tx.send(request).is_err() { - // TODO: Add error tracing + error!("Inbound channel closed"); break; } } diff --git a/src/debugger.rs b/src/debugger.rs index fc632db..88a0d54 100644 --- a/src/debugger.rs +++ b/src/debugger.rs @@ -2,7 +2,7 @@ use anyhow::{Result, bail}; use cairo_vm::vm::vm_core::VirtualMachine; use dap::events::ExitedEventBody; use dap::prelude::Event::{Exited, Terminated}; -use tracing::debug; +use tracing::{debug, error}; use crate::connection::Connection; use crate::debugger::handler::{HandleResult, NextAction}; @@ -64,9 +64,13 @@ impl CairoDebugger { impl Drop for CairoDebugger { fn drop(&mut self) { - // TODO: Add error tracing - // TODO: Send correct exit code - self.connection.send_event(Terminated(None)).ok(); - self.connection.send_event(Exited(ExitedEventBody { exit_code: 0 })).ok(); + if let Err(err) = self.connection.send_event(Terminated(None)) { + error!("Sending terminated event failed: {}", err); + } + + // TODO(#34): Send correct exit code + if let Err(err) = self.connection.send_event(Exited(ExitedEventBody { exit_code: 0 })) { + error!("Sending exit event failed: {}", err); + } } } From 11f3cb06ad3affc660b632cd03d9fce42f142643 Mon Sep 17 00:00:00 2001 From: Dariusz Doktorski Date: Thu, 4 Dec 2025 12:27:48 +0100 Subject: [PATCH 5/5] Replace channel error with trace --- src/connection.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/connection.rs b/src/connection.rs index 1c49bb6..a7e8681 100644 --- a/src/connection.rs +++ b/src/connection.rs @@ -11,7 +11,7 @@ use dap::base_message::Sendable; use dap::errors::ServerError; use dap::prelude::{Event, Request, ResponseBody, Server}; use dap::server::{ServerReader, ServerWriter}; -use tracing::error; +use tracing::trace; pub struct Connection { inbound_rx: mpsc::Receiver, @@ -108,7 +108,7 @@ fn spawn_reader_thread( thread::spawn(move || { while let Ok(Some(request)) = server_reader.poll_request() { if inbound_tx.send(request).is_err() { - error!("Inbound channel closed"); + trace!("Inbound channel closed"); break; } }