Skip to content

Commit

Permalink
Merge branch 'repl'
Browse files Browse the repository at this point in the history
  • Loading branch information
simonrw committed Jan 25, 2025
2 parents fbeda79 + 21d24b4 commit 468db45
Show file tree
Hide file tree
Showing 14 changed files with 963 additions and 338 deletions.
666 changes: 458 additions & 208 deletions Cargo.lock

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ members = [
"launch_configuration",
"dap-codec",
"gui2",
"repl",
]

[workspace.dependencies]
Expand Down
12 changes: 10 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,18 @@ run-attach:
run: ## Run the debugger
cargo run --bin dap-gui-ui -- $(RUN_ARGS)

.PHONY: repl
repl: python-develop ## Open ipython repl with debugger loaded
.PHONY: pyrepl
pyrepl: python-develop ## Open ipython repl with debugger loaded
pythondap -b 9 -f ./attach.py launch_configuration/testdata/vscode/localstack.code-workspace -n "Remote Attach (ext)"

.PHOHY: python-develop
python-develop: ## Compile and install a development version of the debugger
maturin develop --manifest-path pythondap/Cargo.toml

.PHONY: repl-attach
repl-attach:
cargo r -p repl -- launch.json -n Attach -b attach.py:29

.PHONY: repl-launch
repl-launch:
cargo r -p repl -- launch.json -n Launch -b test.py:4
1 change: 1 addition & 0 deletions debugger/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ transport = { path = "../transport" }
retry = "2.0.0"
dirs.workspace = true
launch_configuration = { path = "../launch_configuration" }
uuid = { version = "1.12.0", features = ["v4"] }

[dev-dependencies]
color-eyre.workspace = true
Expand Down
196 changes: 116 additions & 80 deletions debugger/src/debugger.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,13 @@ use transport::{
types::{BreakpointLocation, StackFrameId},
DEFAULT_DAP_PORT,
};
use uuid::Uuid;

use crate::{
internals::DebuggerInternals,
state::{self, DebuggerState},
types::{self, EvaluateResult},
Event, LaunchArguments,
AttachArguments, Event, Language, LaunchArguments,
};

/// How to launch a debugging session
Expand Down Expand Up @@ -56,6 +57,12 @@ impl From<LaunchConfiguration> for InitialiseArguments {
working_directory: None,
language: crate::Language::DebugPy,
}),
"attach" => InitialiseArguments::Attach(AttachArguments {
port: debugpy.connect.map(|c| c.port),
language: Language::DebugPy,
path_mappings: debugpy.path_mappings,
working_directory: debugpy.cwd.expect("TODO: cwd must be specified"),
}),
other => todo!("{other}"),
},
}
Expand Down Expand Up @@ -150,7 +157,16 @@ impl Debugger {
let background_events = events.clone();
thread::spawn(move || loop {
let event = background_events.recv().unwrap();
background_internals.lock().unwrap().on_event(event);
let lock_id = Uuid::new_v4().to_string();
let span = tracing::trace_span!("", %lock_id);
let _guard = span.enter();

tracing::trace!(is_poisoned = %background_internals.is_poisoned(), "trying to unlock background internals");
let mut b = background_internals.lock().unwrap();
tracing::trace!(?event, "handling event");
b.on_event(event);
drop(b);
tracing::trace!("locked background internals");
});

Ok(Self {
Expand Down Expand Up @@ -190,47 +206,50 @@ impl Debugger {
}

/// Add a breakpoint for the current debugging session
#[tracing::instrument(skip(self))]
pub fn add_breakpoint(
&self,
breakpoint: &types::Breakpoint,
) -> eyre::Result<types::BreakpointId> {
let mut internals = self.internals.lock().unwrap();
internals.add_breakpoint(breakpoint)
self.with_internals(|internals| {
internals
.add_breakpoint(breakpoint)
.context("adding breakpoint")
})
}

pub fn get_breakpoint_locations(
&self,
path: impl Into<PathBuf>,
) -> eyre::Result<Vec<BreakpointLocation>> {
let locations = self
.internals
.lock()
.unwrap()
.get_breakpoint_locations(path)
.with_internals(|internals| internals.get_breakpoint_locations(path))
.context("getting breakpoint locations")?;
Ok(locations)
}

/// Return the list of breakpoints configured
pub fn breakpoints(&self) -> Vec<types::Breakpoint> {
self.internals
.lock()
.unwrap()
.breakpoints
.clone()
.values()
.cloned()
.collect()
self.with_internals(|internals| {
Ok(internals.breakpoints.clone().values().cloned().collect())
})
.unwrap()
}

/// Launch a debugging session
pub fn start(&self) -> eyre::Result<()> {
let mut internals = self.internals.lock().unwrap();
let _ = internals
.client
.send(requests::RequestBody::ConfigurationDone)
.context("completing configuration")?;
internals.set_state(DebuggerState::Running);
self.with_internals(|internals| {
internals
.client
.send(requests::RequestBody::ConfigurationDone)
})
.context("completing configuration")?;

self.with_internals(|internals| {
internals.set_state(DebuggerState::Running);
Ok(())
})
.context("completing configuration")?;
Ok(())
}

Expand All @@ -240,15 +259,13 @@ impl Debugger {
input: &str,
frame_id: StackFrameId,
) -> eyre::Result<Option<EvaluateResult>> {
let internals = self.internals.lock().unwrap();
let req = requests::RequestBody::Evaluate(requests::Evaluate {
expression: input.to_string(),
frame_id: Some(frame_id),
context: Some("repl".to_string()),
});
let res = internals
.client
.send(req)
let res = self
.with_internals(|internals| internals.client.send(req))
.context("sending evaluate request")?;
match res {
responses::Response {
Expand Down Expand Up @@ -278,74 +295,79 @@ impl Debugger {
}

/// Resume execution of the debugee
#[tracing::instrument(skip(self))]
pub fn r#continue(&self) -> eyre::Result<()> {
let internals = self.internals.lock().unwrap();
match internals.current_thread_id {
Some(thread_id) => {
internals
.client
.execute(requests::RequestBody::Continue(requests::Continue {
thread_id,
single_thread: false,
}))
.context("sending continue request")?;
self.with_internals(|internals| {
match internals.current_thread_id {
Some(thread_id) => {
internals
.client
.execute(requests::RequestBody::Continue(requests::Continue {
thread_id,
single_thread: false,
}))
.context("sending continue request")?;
}
None => eyre::bail!("logic error: no current thread id"),
}
None => eyre::bail!("logic error: no current thread id"),
}
Ok(())
Ok(())
})
}

/// Step over a statement
pub fn step_over(&self) -> eyre::Result<()> {
let internals = self.internals.lock().unwrap();
match internals.current_thread_id {
Some(thread_id) => {
internals
.client
.execute(requests::RequestBody::Next(requests::Next { thread_id }))
.context("sending step_over request")?;
self.with_internals(|internals| {
match internals.current_thread_id {
Some(thread_id) => {
internals
.client
.execute(requests::RequestBody::Next(requests::Next { thread_id }))
.context("sending step_over request")?;
}
None => eyre::bail!("logic error: no current thread id"),
}
None => eyre::bail!("logic error: no current thread id"),
}
Ok(())
Ok(())
})
}

/// Step into a statement
pub fn step_in(&self) -> eyre::Result<()> {
let internals = self.internals.lock().unwrap();
match internals.current_thread_id {
Some(thread_id) => {
internals
.client
.execute(requests::RequestBody::StepIn(requests::StepIn {
thread_id,
}))
.context("sending step_in` request")?;
self.with_internals(|internals| {
match internals.current_thread_id {
Some(thread_id) => {
internals
.client
.execute(requests::RequestBody::StepIn(requests::StepIn {
thread_id,
}))
.context("sending step_in` request")?;
}
None => eyre::bail!("logic error: no current thread id"),
}
None => eyre::bail!("logic error: no current thread id"),
}
Ok(())
Ok(())
})
}

/// Step out of a statement
pub fn step_out(&self) -> eyre::Result<()> {
let internals = self.internals.lock().unwrap();
match internals.current_thread_id {
Some(thread_id) => {
internals
.client
.execute(requests::RequestBody::StepOut(requests::StepOut {
thread_id,
}))
.context("sending `step_out` request")?;
self.with_internals(|internals| {
match internals.current_thread_id {
Some(thread_id) => {
internals
.client
.execute(requests::RequestBody::StepOut(requests::StepOut {
thread_id,
}))
.context("sending `step_out` request")?;
}
None => eyre::bail!("logic error: no current thread id"),
}
None => eyre::bail!("logic error: no current thread id"),
}
Ok(())
Ok(())
})
}

fn execute(&self, body: requests::RequestBody) -> eyre::Result<()> {
self.internals.lock().unwrap().client.execute(body)
self.with_internals(|internals| internals.client.execute(body))
}

/// Pause the debugging session waiting for a specific event, where the predicate returns true
Expand All @@ -372,12 +394,26 @@ impl Debugger {

/// Change the current scope to a new stack frame
pub fn change_scope(&self, stack_frame_id: StackFrameId) -> eyre::Result<()> {
self.internals
.lock()
.unwrap()
.change_scope(stack_frame_id)
.wrap_err("changing scope")?;
Ok(())
self.with_internals(|internals| {
internals
.change_scope(stack_frame_id)
.wrap_err("changing scope")?;
Ok(())
})
}

#[tracing::instrument(skip_all, fields(lock_id = Uuid::new_v4().to_string()))]
fn with_internals<F, T>(&self, f: F) -> eyre::Result<T>
where
F: FnOnce(&mut DebuggerInternals) -> eyre::Result<T>,
{
tracing::trace!(poisoned = %self.internals.is_poisoned(), "trying to lock internals");
let mut internals = self.internals.lock().unwrap();
tracing::trace!("executing operation");
let res = f(&mut internals);
drop(internals);
tracing::trace!("unlocked internals");
res
}
}

Expand Down
6 changes: 5 additions & 1 deletion debugger/src/internals.rs
Original file line number Diff line number Diff line change
Expand Up @@ -282,9 +282,10 @@ impl DebuggerInternals {
tracing::debug!(?event, "unknown event");
}
}
tracing::debug!(?event, "event handled");
}

#[tracing::instrument(skip(self), level = "trace")]
#[tracing::instrument(skip(self), level = "trace", ret)]
pub(crate) fn add_breakpoint(&mut self, breakpoint: &Breakpoint) -> eyre::Result<BreakpointId> {
tracing::debug!("adding breakpoint");
let id = self.next_id();
Expand All @@ -303,6 +304,7 @@ impl DebuggerInternals {
}

fn broadcast_breakpoints(&mut self) -> eyre::Result<()> {
tracing::debug!("broadcasting breakpoints");
// TODO: don't assume the breakpoints are for the same file
if self.breakpoints.is_empty() {
return Ok(());
Expand Down Expand Up @@ -331,10 +333,12 @@ impl DebuggerInternals {
..Default::default()
});

tracing::debug!("sending broadcast breakpoints message");
let _ = self
.client
.send(req)
.context("broadcasting breakpoints to debugee")?;
tracing::debug!("broadcast breakpoints message sent");
}
Ok(())
}
Expand Down
Loading

0 comments on commit 468db45

Please sign in to comment.