1use crate::debugger::breakpoint_store::BreakpointSessionState;
2use crate::debugger::dap_command::{DataBreakpointContext, ReadMemory};
3use crate::debugger::memory::{self, Memory, MemoryIterator, MemoryPageBuilder, PageAddress};
4
5use super::breakpoint_store::{
6 BreakpointStore, BreakpointStoreEvent, BreakpointUpdatedReason, SourceBreakpoint,
7};
8use super::dap_command::{
9 self, Attach, ConfigurationDone, ContinueCommand, DataBreakpointInfoCommand, DisconnectCommand,
10 EvaluateCommand, Initialize, Launch, LoadedSourcesCommand, LocalDapCommand, LocationsCommand,
11 ModulesCommand, NextCommand, PauseCommand, RestartCommand, RestartStackFrameCommand,
12 ScopesCommand, SetDataBreakpointsCommand, SetExceptionBreakpoints, SetVariableValueCommand,
13 StackTraceCommand, StepBackCommand, StepCommand, StepInCommand, StepOutCommand,
14 TerminateCommand, TerminateThreadsCommand, ThreadsCommand, VariablesCommand,
15};
16use super::dap_store::DapStore;
17use anyhow::{Context as _, Result, anyhow};
18use base64::Engine;
19use collections::{HashMap, HashSet, IndexMap};
20use dap::adapters::{DebugAdapterBinary, DebugAdapterName};
21use dap::messages::Response;
22use dap::requests::{Request, RunInTerminal, StartDebugging};
23use dap::{
24 Capabilities, ContinueArguments, EvaluateArgumentsContext, Module, Source, StackFrameId,
25 SteppingGranularity, StoppedEvent, VariableReference,
26 client::{DebugAdapterClient, SessionId},
27 messages::{Events, Message},
28};
29use dap::{
30 ExceptionBreakpointsFilter, ExceptionFilterOptions, OutputEvent, OutputEventCategory,
31 RunInTerminalRequestArguments, StackFramePresentationHint, StartDebuggingRequestArguments,
32 StartDebuggingRequestArgumentsRequest, VariablePresentationHint, WriteMemoryArguments,
33};
34use futures::SinkExt;
35use futures::channel::mpsc::UnboundedSender;
36use futures::channel::{mpsc, oneshot};
37use futures::{FutureExt, future::Shared};
38use gpui::{
39 App, AppContext, AsyncApp, BackgroundExecutor, Context, Entity, EventEmitter, SharedString,
40 Task, WeakEntity,
41};
42
43use rpc::ErrorExt;
44use serde_json::Value;
45use smol::stream::StreamExt;
46use std::any::TypeId;
47use std::collections::BTreeMap;
48use std::ops::RangeInclusive;
49use std::u64;
50use std::{
51 any::Any,
52 collections::hash_map::Entry,
53 hash::{Hash, Hasher},
54 path::Path,
55 sync::Arc,
56};
57use task::TaskContext;
58use text::{PointUtf16, ToPointUtf16};
59use util::{ResultExt, maybe};
60use worktree::Worktree;
61
62#[derive(Debug, Copy, Clone, Hash, PartialEq, PartialOrd, Ord, Eq)]
63#[repr(transparent)]
64pub struct ThreadId(pub i64);
65
66impl From<i64> for ThreadId {
67 fn from(id: i64) -> Self {
68 Self(id)
69 }
70}
71
72#[derive(Clone, Debug)]
73pub struct StackFrame {
74 pub dap: dap::StackFrame,
75 pub scopes: Vec<dap::Scope>,
76}
77
78impl From<dap::StackFrame> for StackFrame {
79 fn from(stack_frame: dap::StackFrame) -> Self {
80 Self {
81 scopes: vec![],
82 dap: stack_frame,
83 }
84 }
85}
86
87#[derive(Copy, Clone, Debug, Default, PartialEq, Eq)]
88pub enum ThreadStatus {
89 #[default]
90 Running,
91 Stopped,
92 Stepping,
93 Exited,
94 Ended,
95}
96
97impl ThreadStatus {
98 pub fn label(&self) -> &'static str {
99 match self {
100 ThreadStatus::Running => "Running",
101 ThreadStatus::Stopped => "Stopped",
102 ThreadStatus::Stepping => "Stepping",
103 ThreadStatus::Exited => "Exited",
104 ThreadStatus::Ended => "Ended",
105 }
106 }
107}
108
109#[derive(Debug)]
110pub struct Thread {
111 dap: dap::Thread,
112 stack_frames: Vec<StackFrame>,
113 stack_frames_error: Option<anyhow::Error>,
114 _has_stopped: bool,
115}
116
117impl From<dap::Thread> for Thread {
118 fn from(dap: dap::Thread) -> Self {
119 Self {
120 dap,
121 stack_frames: Default::default(),
122 stack_frames_error: None,
123 _has_stopped: false,
124 }
125 }
126}
127
128#[derive(Debug, Clone, PartialEq)]
129pub struct Watcher {
130 pub expression: SharedString,
131 pub value: SharedString,
132 pub variables_reference: u64,
133 pub presentation_hint: Option<VariablePresentationHint>,
134}
135
136#[derive(Debug, Clone, PartialEq)]
137pub struct DataBreakpointState {
138 pub dap: dap::DataBreakpoint,
139 pub is_enabled: bool,
140 pub context: Arc<DataBreakpointContext>,
141}
142
143pub enum SessionState {
144 Building(Option<Task<Result<()>>>),
145 Running(RunningMode),
146}
147
148#[derive(Clone)]
149pub struct RunningMode {
150 client: Arc<DebugAdapterClient>,
151 binary: DebugAdapterBinary,
152 tmp_breakpoint: Option<SourceBreakpoint>,
153 worktree: WeakEntity<Worktree>,
154 executor: BackgroundExecutor,
155 is_started: bool,
156 has_ever_stopped: bool,
157 messages_tx: UnboundedSender<Message>,
158}
159
160#[derive(Clone, Copy, Debug, PartialEq, Eq, Default)]
161pub struct SessionQuirks {
162 pub compact: bool,
163 pub prefer_thread_name: bool,
164}
165
166fn client_source(abs_path: &Path) -> dap::Source {
167 dap::Source {
168 name: abs_path
169 .file_name()
170 .map(|filename| filename.to_string_lossy().to_string()),
171 path: Some(abs_path.to_string_lossy().to_string()),
172 source_reference: None,
173 presentation_hint: None,
174 origin: None,
175 sources: None,
176 adapter_data: None,
177 checksums: None,
178 }
179}
180
181impl RunningMode {
182 async fn new(
183 session_id: SessionId,
184 parent_session: Option<Entity<Session>>,
185 worktree: WeakEntity<Worktree>,
186 binary: DebugAdapterBinary,
187 messages_tx: futures::channel::mpsc::UnboundedSender<Message>,
188 cx: &mut AsyncApp,
189 ) -> Result<Self> {
190 let message_handler = Box::new({
191 let messages_tx = messages_tx.clone();
192 move |message| {
193 messages_tx.unbounded_send(message).ok();
194 }
195 });
196
197 let client = if let Some(client) = parent_session
198 .and_then(|session| cx.update(|cx| session.read(cx).adapter_client()).ok())
199 .flatten()
200 {
201 client
202 .create_child_connection(session_id, binary.clone(), message_handler, cx)
203 .await?
204 } else {
205 DebugAdapterClient::start(session_id, binary.clone(), message_handler, cx).await?
206 };
207
208 Ok(Self {
209 client: Arc::new(client),
210 worktree,
211 tmp_breakpoint: None,
212 binary,
213 executor: cx.background_executor().clone(),
214 is_started: false,
215 has_ever_stopped: false,
216 messages_tx,
217 })
218 }
219
220 pub(crate) fn worktree(&self) -> &WeakEntity<Worktree> {
221 &self.worktree
222 }
223
224 fn unset_breakpoints_from_paths(&self, paths: &Vec<Arc<Path>>, cx: &mut App) -> Task<()> {
225 let tasks: Vec<_> = paths
226 .into_iter()
227 .map(|path| {
228 self.request(dap_command::SetBreakpoints {
229 source: client_source(path),
230 source_modified: None,
231 breakpoints: vec![],
232 })
233 })
234 .collect();
235
236 cx.background_spawn(async move {
237 futures::future::join_all(tasks)
238 .await
239 .iter()
240 .for_each(|res| match res {
241 Ok(_) => {}
242 Err(err) => {
243 log::warn!("Set breakpoints request failed: {}", err);
244 }
245 });
246 })
247 }
248
249 fn send_breakpoints_from_path(
250 &self,
251 abs_path: Arc<Path>,
252 reason: BreakpointUpdatedReason,
253 breakpoint_store: &Entity<BreakpointStore>,
254 cx: &mut App,
255 ) -> Task<()> {
256 let breakpoints =
257 breakpoint_store
258 .read(cx)
259 .source_breakpoints_from_path(&abs_path, cx)
260 .into_iter()
261 .filter(|bp| bp.state.is_enabled())
262 .chain(self.tmp_breakpoint.iter().filter_map(|breakpoint| {
263 breakpoint.path.eq(&abs_path).then(|| breakpoint.clone())
264 }))
265 .map(Into::into)
266 .collect();
267
268 let raw_breakpoints = breakpoint_store
269 .read(cx)
270 .breakpoints_from_path(&abs_path)
271 .into_iter()
272 .filter(|bp| bp.bp.state.is_enabled())
273 .collect::<Vec<_>>();
274
275 let task = self.request(dap_command::SetBreakpoints {
276 source: client_source(&abs_path),
277 source_modified: Some(matches!(reason, BreakpointUpdatedReason::FileSaved)),
278 breakpoints,
279 });
280 let session_id = self.client.id();
281 let breakpoint_store = breakpoint_store.downgrade();
282 cx.spawn(async move |cx| match cx.background_spawn(task).await {
283 Ok(breakpoints) => {
284 let breakpoints =
285 breakpoints
286 .into_iter()
287 .zip(raw_breakpoints)
288 .filter_map(|(dap_bp, zed_bp)| {
289 Some((
290 zed_bp,
291 BreakpointSessionState {
292 id: dap_bp.id?,
293 verified: dap_bp.verified,
294 },
295 ))
296 });
297 breakpoint_store
298 .update(cx, |this, _| {
299 this.mark_breakpoints_verified(session_id, &abs_path, breakpoints);
300 })
301 .ok();
302 }
303 Err(err) => log::warn!("Set breakpoints request failed for path: {}", err),
304 })
305 }
306
307 fn send_exception_breakpoints(
308 &self,
309 filters: Vec<ExceptionBreakpointsFilter>,
310 supports_filter_options: bool,
311 ) -> Task<Result<Vec<dap::Breakpoint>>> {
312 let arg = if supports_filter_options {
313 SetExceptionBreakpoints::WithOptions {
314 filters: filters
315 .into_iter()
316 .map(|filter| ExceptionFilterOptions {
317 filter_id: filter.filter,
318 condition: None,
319 mode: None,
320 })
321 .collect(),
322 }
323 } else {
324 SetExceptionBreakpoints::Plain {
325 filters: filters.into_iter().map(|filter| filter.filter).collect(),
326 }
327 };
328 self.request(arg)
329 }
330
331 fn send_source_breakpoints(
332 &self,
333 ignore_breakpoints: bool,
334 breakpoint_store: &Entity<BreakpointStore>,
335 cx: &App,
336 ) -> Task<HashMap<Arc<Path>, anyhow::Error>> {
337 let mut breakpoint_tasks = Vec::new();
338 let breakpoints = breakpoint_store.read(cx).all_source_breakpoints(cx);
339 let mut raw_breakpoints = breakpoint_store.read_with(cx, |this, _| this.all_breakpoints());
340 debug_assert_eq!(raw_breakpoints.len(), breakpoints.len());
341 let session_id = self.client.id();
342 for (path, breakpoints) in breakpoints {
343 let breakpoints = if ignore_breakpoints {
344 vec![]
345 } else {
346 breakpoints
347 .into_iter()
348 .filter(|bp| bp.state.is_enabled())
349 .map(Into::into)
350 .collect()
351 };
352
353 let raw_breakpoints = raw_breakpoints
354 .remove(&path)
355 .unwrap_or_default()
356 .into_iter()
357 .filter(|bp| bp.bp.state.is_enabled());
358 let error_path = path.clone();
359 let send_request = self
360 .request(dap_command::SetBreakpoints {
361 source: client_source(&path),
362 source_modified: Some(false),
363 breakpoints,
364 })
365 .map(|result| result.map_err(move |e| (error_path, e)));
366
367 let task = cx.spawn({
368 let breakpoint_store = breakpoint_store.downgrade();
369 async move |cx| {
370 let breakpoints = cx.background_spawn(send_request).await?;
371
372 let breakpoints = breakpoints.into_iter().zip(raw_breakpoints).filter_map(
373 |(dap_bp, zed_bp)| {
374 Some((
375 zed_bp,
376 BreakpointSessionState {
377 id: dap_bp.id?,
378 verified: dap_bp.verified,
379 },
380 ))
381 },
382 );
383 breakpoint_store
384 .update(cx, |this, _| {
385 this.mark_breakpoints_verified(session_id, &path, breakpoints);
386 })
387 .ok();
388
389 Ok(())
390 }
391 });
392 breakpoint_tasks.push(task);
393 }
394
395 cx.background_spawn(async move {
396 futures::future::join_all(breakpoint_tasks)
397 .await
398 .into_iter()
399 .filter_map(Result::err)
400 .collect::<HashMap<_, _>>()
401 })
402 }
403
404 fn initialize_sequence(
405 &self,
406 capabilities: &Capabilities,
407 initialized_rx: oneshot::Receiver<()>,
408 dap_store: WeakEntity<DapStore>,
409 cx: &mut Context<Session>,
410 ) -> Task<Result<()>> {
411 let raw = self.binary.request_args.clone();
412
413 // Of relevance: https://github.com/microsoft/vscode/issues/4902#issuecomment-368583522
414 let launch = match raw.request {
415 dap::StartDebuggingRequestArgumentsRequest::Launch => self.request(Launch {
416 raw: raw.configuration,
417 }),
418 dap::StartDebuggingRequestArgumentsRequest::Attach => self.request(Attach {
419 raw: raw.configuration,
420 }),
421 };
422
423 let configuration_done_supported = ConfigurationDone::is_supported(capabilities);
424 // From spec (on initialization sequence):
425 // client sends a setExceptionBreakpoints request if one or more exceptionBreakpointFilters have been defined (or if supportsConfigurationDoneRequest is not true)
426 //
427 // Thus we should send setExceptionBreakpoints even if `exceptionFilters` variable is empty (as long as there were some options in the first place).
428 let should_send_exception_breakpoints = capabilities
429 .exception_breakpoint_filters
430 .as_ref()
431 .map_or(false, |filters| !filters.is_empty())
432 || !configuration_done_supported;
433 let supports_exception_filters = capabilities
434 .supports_exception_filter_options
435 .unwrap_or_default();
436 let this = self.clone();
437 let worktree = self.worktree().clone();
438 let mut filters = capabilities
439 .exception_breakpoint_filters
440 .clone()
441 .unwrap_or_default();
442 let configuration_sequence = cx.spawn({
443 async move |session, cx| {
444 let adapter_name = session.read_with(cx, |this, _| this.adapter())?;
445 let (breakpoint_store, adapter_defaults) =
446 dap_store.read_with(cx, |dap_store, _| {
447 (
448 dap_store.breakpoint_store().clone(),
449 dap_store.adapter_options(&adapter_name),
450 )
451 })?;
452 initialized_rx.await?;
453 let errors_by_path = cx
454 .update(|cx| this.send_source_breakpoints(false, &breakpoint_store, cx))?
455 .await;
456
457 dap_store.update(cx, |_, cx| {
458 let Some(worktree) = worktree.upgrade() else {
459 return;
460 };
461
462 for (path, error) in &errors_by_path {
463 log::error!("failed to set breakpoints for {path:?}: {error}");
464 }
465
466 if let Some(failed_path) = errors_by_path.keys().next() {
467 let failed_path = failed_path
468 .strip_prefix(worktree.read(cx).abs_path())
469 .unwrap_or(failed_path)
470 .display();
471 let message = format!(
472 "Failed to set breakpoints for {failed_path}{}",
473 match errors_by_path.len() {
474 0 => unreachable!(),
475 1 => "".into(),
476 2 => " and 1 other path".into(),
477 n => format!(" and {} other paths", n - 1),
478 }
479 );
480 cx.emit(super::dap_store::DapStoreEvent::Notification(message));
481 }
482 })?;
483
484 if should_send_exception_breakpoints {
485 _ = session.update(cx, |this, _| {
486 filters.retain(|filter| {
487 let is_enabled = if let Some(defaults) = adapter_defaults.as_ref() {
488 defaults
489 .exception_breakpoints
490 .get(&filter.filter)
491 .map(|options| options.enabled)
492 .unwrap_or_else(|| filter.default.unwrap_or_default())
493 } else {
494 filter.default.unwrap_or_default()
495 };
496 this.exception_breakpoints
497 .entry(filter.filter.clone())
498 .or_insert_with(|| (filter.clone(), is_enabled));
499 is_enabled
500 });
501 });
502
503 this.send_exception_breakpoints(filters, supports_exception_filters)
504 .await
505 .ok();
506 }
507
508 let ret = if configuration_done_supported {
509 this.request(ConfigurationDone {})
510 } else {
511 Task::ready(Ok(()))
512 }
513 .await;
514 ret
515 }
516 });
517
518 let task = cx.background_spawn(futures::future::try_join(launch, configuration_sequence));
519
520 cx.spawn(async move |this, cx| {
521 let result = task.await;
522
523 this.update(cx, |this, cx| {
524 if let Some(this) = this.as_running_mut() {
525 this.is_started = true;
526 cx.notify();
527 }
528 })
529 .ok();
530
531 result?;
532 anyhow::Ok(())
533 })
534 }
535
536 fn reconnect_for_ssh(&self, cx: &mut AsyncApp) -> Option<Task<Result<()>>> {
537 let client = self.client.clone();
538 let messages_tx = self.messages_tx.clone();
539 let message_handler = Box::new(move |message| {
540 messages_tx.unbounded_send(message).ok();
541 });
542 if client.should_reconnect_for_ssh() {
543 Some(cx.spawn(async move |cx| {
544 client.connect(message_handler, cx).await?;
545 anyhow::Ok(())
546 }))
547 } else {
548 None
549 }
550 }
551
552 fn request<R: LocalDapCommand>(&self, request: R) -> Task<Result<R::Response>>
553 where
554 <R::DapRequest as dap::requests::Request>::Response: 'static,
555 <R::DapRequest as dap::requests::Request>::Arguments: 'static + Send,
556 {
557 let request = Arc::new(request);
558
559 let request_clone = request.clone();
560 let connection = self.client.clone();
561 self.executor.spawn(async move {
562 let args = request_clone.to_dap();
563 let response = connection.request::<R::DapRequest>(args).await?;
564 request.response_from_dap(response)
565 })
566 }
567}
568
569impl SessionState {
570 pub(super) fn request_dap<R: LocalDapCommand>(&self, request: R) -> Task<Result<R::Response>>
571 where
572 <R::DapRequest as dap::requests::Request>::Response: 'static,
573 <R::DapRequest as dap::requests::Request>::Arguments: 'static + Send,
574 {
575 match self {
576 SessionState::Running(debug_adapter_client) => debug_adapter_client.request(request),
577 SessionState::Building(_) => Task::ready(Err(anyhow!(
578 "no adapter running to send request: {request:?}"
579 ))),
580 }
581 }
582
583 /// Did this debug session stop at least once?
584 pub(crate) fn has_ever_stopped(&self) -> bool {
585 match self {
586 SessionState::Building(_) => false,
587 SessionState::Running(running_mode) => running_mode.has_ever_stopped,
588 }
589 }
590
591 fn stopped(&mut self) {
592 if let SessionState::Running(running) = self {
593 running.has_ever_stopped = true;
594 }
595 }
596}
597
598#[derive(Default)]
599struct ThreadStates {
600 global_state: Option<ThreadStatus>,
601 known_thread_states: IndexMap<ThreadId, ThreadStatus>,
602}
603
604impl ThreadStates {
605 fn stop_all_threads(&mut self) {
606 self.global_state = Some(ThreadStatus::Stopped);
607 self.known_thread_states.clear();
608 }
609
610 fn exit_all_threads(&mut self) {
611 self.global_state = Some(ThreadStatus::Exited);
612 self.known_thread_states.clear();
613 }
614
615 fn continue_all_threads(&mut self) {
616 self.global_state = Some(ThreadStatus::Running);
617 self.known_thread_states.clear();
618 }
619
620 fn stop_thread(&mut self, thread_id: ThreadId) {
621 self.known_thread_states
622 .insert(thread_id, ThreadStatus::Stopped);
623 }
624
625 fn continue_thread(&mut self, thread_id: ThreadId) {
626 self.known_thread_states
627 .insert(thread_id, ThreadStatus::Running);
628 }
629
630 fn process_step(&mut self, thread_id: ThreadId) {
631 self.known_thread_states
632 .insert(thread_id, ThreadStatus::Stepping);
633 }
634
635 fn thread_status(&self, thread_id: ThreadId) -> ThreadStatus {
636 self.thread_state(thread_id)
637 .unwrap_or(ThreadStatus::Running)
638 }
639
640 fn thread_state(&self, thread_id: ThreadId) -> Option<ThreadStatus> {
641 self.known_thread_states
642 .get(&thread_id)
643 .copied()
644 .or(self.global_state)
645 }
646
647 fn exit_thread(&mut self, thread_id: ThreadId) {
648 self.known_thread_states
649 .insert(thread_id, ThreadStatus::Exited);
650 }
651
652 fn any_stopped_thread(&self) -> bool {
653 self.global_state
654 .is_some_and(|state| state == ThreadStatus::Stopped)
655 || self
656 .known_thread_states
657 .values()
658 .any(|status| *status == ThreadStatus::Stopped)
659 }
660}
661const MAX_TRACKED_OUTPUT_EVENTS: usize = 5000;
662
663type IsEnabled = bool;
664
665#[derive(Copy, Clone, Default, Debug, PartialEq, PartialOrd, Eq, Ord)]
666pub struct OutputToken(pub usize);
667/// Represents a current state of a single debug adapter and provides ways to mutate it.
668pub struct Session {
669 pub mode: SessionState,
670 id: SessionId,
671 label: Option<SharedString>,
672 adapter: DebugAdapterName,
673 pub(super) capabilities: Capabilities,
674 child_session_ids: HashSet<SessionId>,
675 parent_session: Option<Entity<Session>>,
676 modules: Vec<dap::Module>,
677 loaded_sources: Vec<dap::Source>,
678 output_token: OutputToken,
679 output: Box<circular_buffer::CircularBuffer<MAX_TRACKED_OUTPUT_EVENTS, dap::OutputEvent>>,
680 threads: IndexMap<ThreadId, Thread>,
681 thread_states: ThreadStates,
682 watchers: HashMap<SharedString, Watcher>,
683 variables: HashMap<VariableReference, Vec<dap::Variable>>,
684 stack_frames: IndexMap<StackFrameId, StackFrame>,
685 locations: HashMap<u64, dap::LocationsResponse>,
686 is_session_terminated: bool,
687 requests: HashMap<TypeId, HashMap<RequestSlot, Shared<Task<Option<()>>>>>,
688 pub(crate) breakpoint_store: Entity<BreakpointStore>,
689 ignore_breakpoints: bool,
690 exception_breakpoints: BTreeMap<String, (ExceptionBreakpointsFilter, IsEnabled)>,
691 data_breakpoints: BTreeMap<String, DataBreakpointState>,
692 background_tasks: Vec<Task<()>>,
693 restart_task: Option<Task<()>>,
694 task_context: TaskContext,
695 memory: memory::Memory,
696 quirks: SessionQuirks,
697}
698
699trait CacheableCommand: Any + Send + Sync {
700 fn dyn_eq(&self, rhs: &dyn CacheableCommand) -> bool;
701 fn dyn_hash(&self, hasher: &mut dyn Hasher);
702 fn as_any_arc(self: Arc<Self>) -> Arc<dyn Any + Send + Sync>;
703}
704
705impl<T> CacheableCommand for T
706where
707 T: LocalDapCommand + PartialEq + Eq + Hash,
708{
709 fn dyn_eq(&self, rhs: &dyn CacheableCommand) -> bool {
710 (rhs as &dyn Any)
711 .downcast_ref::<Self>()
712 .map_or(false, |rhs| self == rhs)
713 }
714
715 fn dyn_hash(&self, mut hasher: &mut dyn Hasher) {
716 T::hash(self, &mut hasher);
717 }
718
719 fn as_any_arc(self: Arc<Self>) -> Arc<dyn Any + Send + Sync> {
720 self
721 }
722}
723
724pub(crate) struct RequestSlot(Arc<dyn CacheableCommand>);
725
726impl<T: LocalDapCommand + PartialEq + Eq + Hash> From<T> for RequestSlot {
727 fn from(request: T) -> Self {
728 Self(Arc::new(request))
729 }
730}
731
732impl PartialEq for RequestSlot {
733 fn eq(&self, other: &Self) -> bool {
734 self.0.dyn_eq(other.0.as_ref())
735 }
736}
737
738impl Eq for RequestSlot {}
739
740impl Hash for RequestSlot {
741 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
742 self.0.dyn_hash(state);
743 (&*self.0 as &dyn Any).type_id().hash(state)
744 }
745}
746
747#[derive(Debug, Clone, Hash, PartialEq, Eq)]
748pub struct CompletionsQuery {
749 pub query: String,
750 pub column: u64,
751 pub line: Option<u64>,
752 pub frame_id: Option<u64>,
753}
754
755impl CompletionsQuery {
756 pub fn new(
757 buffer: &language::Buffer,
758 cursor_position: language::Anchor,
759 frame_id: Option<u64>,
760 ) -> Self {
761 let PointUtf16 { row, column } = cursor_position.to_point_utf16(&buffer.snapshot());
762 Self {
763 query: buffer.text(),
764 column: column as u64,
765 frame_id,
766 line: Some(row as u64),
767 }
768 }
769}
770
771#[derive(Debug)]
772pub enum SessionEvent {
773 Modules,
774 LoadedSources,
775 Stopped(Option<ThreadId>),
776 StackTrace,
777 Variables,
778 Watchers,
779 Threads,
780 InvalidateInlineValue,
781 CapabilitiesLoaded,
782 RunInTerminal {
783 request: RunInTerminalRequestArguments,
784 sender: mpsc::Sender<Result<u32>>,
785 },
786 DataBreakpointInfo,
787 ConsoleOutput,
788}
789
790#[derive(Clone, Debug, PartialEq, Eq)]
791pub enum SessionStateEvent {
792 Running,
793 Shutdown,
794 Restart,
795 SpawnChildSession {
796 request: StartDebuggingRequestArguments,
797 },
798}
799
800impl EventEmitter<SessionEvent> for Session {}
801impl EventEmitter<SessionStateEvent> for Session {}
802
803// local session will send breakpoint updates to DAP for all new breakpoints
804// remote side will only send breakpoint updates when it is a breakpoint created by that peer
805// BreakpointStore notifies session on breakpoint changes
806impl Session {
807 pub(crate) fn new(
808 breakpoint_store: Entity<BreakpointStore>,
809 session_id: SessionId,
810 parent_session: Option<Entity<Session>>,
811 label: Option<SharedString>,
812 adapter: DebugAdapterName,
813 task_context: TaskContext,
814 quirks: SessionQuirks,
815 cx: &mut App,
816 ) -> Entity<Self> {
817 cx.new::<Self>(|cx| {
818 cx.subscribe(&breakpoint_store, |this, store, event, cx| match event {
819 BreakpointStoreEvent::BreakpointsUpdated(path, reason) => {
820 if let Some(local) = (!this.ignore_breakpoints)
821 .then(|| this.as_running_mut())
822 .flatten()
823 {
824 local
825 .send_breakpoints_from_path(path.clone(), *reason, &store, cx)
826 .detach();
827 };
828 }
829 BreakpointStoreEvent::BreakpointsCleared(paths) => {
830 if let Some(local) = (!this.ignore_breakpoints)
831 .then(|| this.as_running_mut())
832 .flatten()
833 {
834 local.unset_breakpoints_from_paths(paths, cx).detach();
835 }
836 }
837 BreakpointStoreEvent::SetDebugLine | BreakpointStoreEvent::ClearDebugLines => {}
838 })
839 .detach();
840
841 let this = Self {
842 mode: SessionState::Building(None),
843 id: session_id,
844 child_session_ids: HashSet::default(),
845 parent_session,
846 capabilities: Capabilities::default(),
847 watchers: HashMap::default(),
848 variables: Default::default(),
849 stack_frames: Default::default(),
850 thread_states: ThreadStates::default(),
851 output_token: OutputToken(0),
852 output: circular_buffer::CircularBuffer::boxed(),
853 requests: HashMap::default(),
854 modules: Vec::default(),
855 loaded_sources: Vec::default(),
856 threads: IndexMap::default(),
857 background_tasks: Vec::default(),
858 restart_task: None,
859 locations: Default::default(),
860 is_session_terminated: false,
861 ignore_breakpoints: false,
862 breakpoint_store,
863 data_breakpoints: Default::default(),
864 exception_breakpoints: Default::default(),
865 label,
866 adapter,
867 task_context,
868 memory: memory::Memory::new(),
869 quirks,
870 };
871
872 this
873 })
874 }
875
876 pub fn task_context(&self) -> &TaskContext {
877 &self.task_context
878 }
879
880 pub fn worktree(&self) -> Option<Entity<Worktree>> {
881 match &self.mode {
882 SessionState::Building(_) => None,
883 SessionState::Running(local_mode) => local_mode.worktree.upgrade(),
884 }
885 }
886
887 pub fn boot(
888 &mut self,
889 binary: DebugAdapterBinary,
890 worktree: Entity<Worktree>,
891 dap_store: WeakEntity<DapStore>,
892 cx: &mut Context<Self>,
893 ) -> Task<Result<()>> {
894 let (message_tx, mut message_rx) = futures::channel::mpsc::unbounded();
895 let (initialized_tx, initialized_rx) = futures::channel::oneshot::channel();
896
897 let background_tasks = vec![cx.spawn(async move |this: WeakEntity<Session>, cx| {
898 let mut initialized_tx = Some(initialized_tx);
899 while let Some(message) = message_rx.next().await {
900 if let Message::Event(event) = message {
901 if let Events::Initialized(_) = *event {
902 if let Some(tx) = initialized_tx.take() {
903 tx.send(()).ok();
904 }
905 } else {
906 let Ok(_) = this.update(cx, |session, cx| {
907 session.handle_dap_event(event, cx);
908 }) else {
909 break;
910 };
911 }
912 } else if let Message::Request(request) = message {
913 let Ok(_) = this.update(cx, |this, cx| {
914 if request.command == StartDebugging::COMMAND {
915 this.handle_start_debugging_request(request, cx)
916 .detach_and_log_err(cx);
917 } else if request.command == RunInTerminal::COMMAND {
918 this.handle_run_in_terminal_request(request, cx)
919 .detach_and_log_err(cx);
920 }
921 }) else {
922 break;
923 };
924 }
925 }
926 })];
927 self.background_tasks = background_tasks;
928 let id = self.id;
929 let parent_session = self.parent_session.clone();
930
931 cx.spawn(async move |this, cx| {
932 let mode = RunningMode::new(
933 id,
934 parent_session,
935 worktree.downgrade(),
936 binary.clone(),
937 message_tx,
938 cx,
939 )
940 .await?;
941 this.update(cx, |this, cx| {
942 match &mut this.mode {
943 SessionState::Building(task) if task.is_some() => {
944 task.take().unwrap().detach_and_log_err(cx);
945 }
946 _ => {
947 debug_assert!(
948 this.parent_session.is_some(),
949 "Booting a root debug session without a boot task"
950 );
951 }
952 };
953 this.mode = SessionState::Running(mode);
954 cx.emit(SessionStateEvent::Running);
955 })?;
956
957 this.update(cx, |session, cx| session.request_initialize(cx))?
958 .await?;
959
960 let result = this
961 .update(cx, |session, cx| {
962 session.initialize_sequence(initialized_rx, dap_store.clone(), cx)
963 })?
964 .await;
965
966 if result.is_err() {
967 let mut console = this.update(cx, |session, cx| session.console_output(cx))?;
968
969 console
970 .send(format!(
971 "Tried to launch debugger with: {}",
972 serde_json::to_string_pretty(&binary.request_args.configuration)
973 .unwrap_or_default(),
974 ))
975 .await
976 .ok();
977 }
978
979 result
980 })
981 }
982
983 pub fn session_id(&self) -> SessionId {
984 self.id
985 }
986
987 pub fn child_session_ids(&self) -> HashSet<SessionId> {
988 self.child_session_ids.clone()
989 }
990
991 pub fn add_child_session_id(&mut self, session_id: SessionId) {
992 self.child_session_ids.insert(session_id);
993 }
994
995 pub fn remove_child_session_id(&mut self, session_id: SessionId) {
996 self.child_session_ids.remove(&session_id);
997 }
998
999 pub fn parent_id(&self, cx: &App) -> Option<SessionId> {
1000 self.parent_session
1001 .as_ref()
1002 .map(|session| session.read(cx).id)
1003 }
1004
1005 pub fn parent_session(&self) -> Option<&Entity<Self>> {
1006 self.parent_session.as_ref()
1007 }
1008
1009 pub fn on_app_quit(&mut self, cx: &mut Context<Self>) -> Task<()> {
1010 let Some(client) = self.adapter_client() else {
1011 return Task::ready(());
1012 };
1013
1014 let supports_terminate = self
1015 .capabilities
1016 .support_terminate_debuggee
1017 .unwrap_or(false);
1018
1019 cx.background_spawn(async move {
1020 if supports_terminate {
1021 client
1022 .request::<dap::requests::Terminate>(dap::TerminateArguments {
1023 restart: Some(false),
1024 })
1025 .await
1026 .ok();
1027 } else {
1028 client
1029 .request::<dap::requests::Disconnect>(dap::DisconnectArguments {
1030 restart: Some(false),
1031 terminate_debuggee: Some(true),
1032 suspend_debuggee: Some(false),
1033 })
1034 .await
1035 .ok();
1036 }
1037 })
1038 }
1039
1040 pub fn capabilities(&self) -> &Capabilities {
1041 &self.capabilities
1042 }
1043
1044 pub fn binary(&self) -> Option<&DebugAdapterBinary> {
1045 match &self.mode {
1046 SessionState::Building(_) => None,
1047 SessionState::Running(running_mode) => Some(&running_mode.binary),
1048 }
1049 }
1050
1051 pub fn adapter(&self) -> DebugAdapterName {
1052 self.adapter.clone()
1053 }
1054
1055 pub fn label(&self) -> Option<SharedString> {
1056 self.label.clone()
1057 }
1058
1059 pub fn is_terminated(&self) -> bool {
1060 self.is_session_terminated
1061 }
1062
1063 pub fn console_output(&mut self, cx: &mut Context<Self>) -> mpsc::UnboundedSender<String> {
1064 let (tx, mut rx) = mpsc::unbounded();
1065
1066 cx.spawn(async move |this, cx| {
1067 while let Some(output) = rx.next().await {
1068 this.update(cx, |this, _| {
1069 let event = dap::OutputEvent {
1070 category: None,
1071 output,
1072 group: None,
1073 variables_reference: None,
1074 source: None,
1075 line: None,
1076 column: None,
1077 data: None,
1078 location_reference: None,
1079 };
1080 this.push_output(event);
1081 })?;
1082 }
1083 anyhow::Ok(())
1084 })
1085 .detach();
1086
1087 return tx;
1088 }
1089
1090 pub fn is_started(&self) -> bool {
1091 match &self.mode {
1092 SessionState::Building(_) => false,
1093 SessionState::Running(running) => running.is_started,
1094 }
1095 }
1096
1097 pub fn is_building(&self) -> bool {
1098 matches!(self.mode, SessionState::Building(_))
1099 }
1100
1101 pub fn as_running_mut(&mut self) -> Option<&mut RunningMode> {
1102 match &mut self.mode {
1103 SessionState::Running(local_mode) => Some(local_mode),
1104 SessionState::Building(_) => None,
1105 }
1106 }
1107
1108 pub fn as_running(&self) -> Option<&RunningMode> {
1109 match &self.mode {
1110 SessionState::Running(local_mode) => Some(local_mode),
1111 SessionState::Building(_) => None,
1112 }
1113 }
1114
1115 fn handle_start_debugging_request(
1116 &mut self,
1117 request: dap::messages::Request,
1118 cx: &mut Context<Self>,
1119 ) -> Task<Result<()>> {
1120 let request_seq = request.seq;
1121
1122 let launch_request: Option<Result<StartDebuggingRequestArguments, _>> = request
1123 .arguments
1124 .as_ref()
1125 .map(|value| serde_json::from_value(value.clone()));
1126
1127 let mut success = true;
1128 if let Some(Ok(request)) = launch_request {
1129 cx.emit(SessionStateEvent::SpawnChildSession { request });
1130 } else {
1131 log::error!(
1132 "Failed to parse launch request arguments: {:?}",
1133 request.arguments
1134 );
1135 success = false;
1136 }
1137
1138 cx.spawn(async move |this, cx| {
1139 this.update(cx, |this, cx| {
1140 this.respond_to_client(
1141 request_seq,
1142 success,
1143 StartDebugging::COMMAND.to_string(),
1144 None,
1145 cx,
1146 )
1147 })?
1148 .await
1149 })
1150 }
1151
1152 fn handle_run_in_terminal_request(
1153 &mut self,
1154 request: dap::messages::Request,
1155 cx: &mut Context<Self>,
1156 ) -> Task<Result<()>> {
1157 let request_args = match serde_json::from_value::<RunInTerminalRequestArguments>(
1158 request.arguments.unwrap_or_default(),
1159 ) {
1160 Ok(args) => args,
1161 Err(error) => {
1162 return cx.spawn(async move |session, cx| {
1163 let error = serde_json::to_value(dap::ErrorResponse {
1164 error: Some(dap::Message {
1165 id: request.seq,
1166 format: error.to_string(),
1167 variables: None,
1168 send_telemetry: None,
1169 show_user: None,
1170 url: None,
1171 url_label: None,
1172 }),
1173 })
1174 .ok();
1175
1176 session
1177 .update(cx, |this, cx| {
1178 this.respond_to_client(
1179 request.seq,
1180 false,
1181 StartDebugging::COMMAND.to_string(),
1182 error,
1183 cx,
1184 )
1185 })?
1186 .await?;
1187
1188 Err(anyhow!("Failed to parse RunInTerminalRequestArguments"))
1189 });
1190 }
1191 };
1192
1193 let seq = request.seq;
1194
1195 let (tx, mut rx) = mpsc::channel::<Result<u32>>(1);
1196 cx.emit(SessionEvent::RunInTerminal {
1197 request: request_args,
1198 sender: tx,
1199 });
1200 cx.notify();
1201
1202 cx.spawn(async move |session, cx| {
1203 let result = util::maybe!(async move {
1204 rx.next().await.ok_or_else(|| {
1205 anyhow!("failed to receive response from spawn terminal".to_string())
1206 })?
1207 })
1208 .await;
1209 let (success, body) = match result {
1210 Ok(pid) => (
1211 true,
1212 serde_json::to_value(dap::RunInTerminalResponse {
1213 process_id: None,
1214 shell_process_id: Some(pid as u64),
1215 })
1216 .ok(),
1217 ),
1218 Err(error) => (
1219 false,
1220 serde_json::to_value(dap::ErrorResponse {
1221 error: Some(dap::Message {
1222 id: seq,
1223 format: error.to_string(),
1224 variables: None,
1225 send_telemetry: None,
1226 show_user: None,
1227 url: None,
1228 url_label: None,
1229 }),
1230 })
1231 .ok(),
1232 ),
1233 };
1234
1235 session
1236 .update(cx, |session, cx| {
1237 session.respond_to_client(
1238 seq,
1239 success,
1240 RunInTerminal::COMMAND.to_string(),
1241 body,
1242 cx,
1243 )
1244 })?
1245 .await
1246 })
1247 }
1248
1249 pub(super) fn request_initialize(&mut self, cx: &mut Context<Self>) -> Task<Result<()>> {
1250 let adapter_id = self.adapter().to_string();
1251 let request = Initialize { adapter_id };
1252
1253 let SessionState::Running(running) = &self.mode else {
1254 return Task::ready(Err(anyhow!(
1255 "Cannot send initialize request, task still building"
1256 )));
1257 };
1258 let mut response = running.request(request.clone());
1259
1260 cx.spawn(async move |this, cx| {
1261 loop {
1262 let capabilities = response.await;
1263 match capabilities {
1264 Err(e) => {
1265 let Ok(Some(reconnect)) = this.update(cx, |this, cx| {
1266 this.as_running()
1267 .and_then(|running| running.reconnect_for_ssh(&mut cx.to_async()))
1268 }) else {
1269 return Err(e);
1270 };
1271 log::info!("Failed to connect to debug adapter: {}, retrying...", e);
1272 reconnect.await?;
1273
1274 let Ok(Some(r)) = this.update(cx, |this, _| {
1275 this.as_running()
1276 .map(|running| running.request(request.clone()))
1277 }) else {
1278 return Err(e);
1279 };
1280 response = r
1281 }
1282 Ok(capabilities) => {
1283 this.update(cx, |session, cx| {
1284 session.capabilities = capabilities;
1285
1286 cx.emit(SessionEvent::CapabilitiesLoaded);
1287 })?;
1288 return Ok(());
1289 }
1290 }
1291 }
1292 })
1293 }
1294
1295 pub(super) fn initialize_sequence(
1296 &mut self,
1297 initialize_rx: oneshot::Receiver<()>,
1298 dap_store: WeakEntity<DapStore>,
1299 cx: &mut Context<Self>,
1300 ) -> Task<Result<()>> {
1301 match &self.mode {
1302 SessionState::Running(local_mode) => {
1303 local_mode.initialize_sequence(&self.capabilities, initialize_rx, dap_store, cx)
1304 }
1305 SessionState::Building(_) => {
1306 Task::ready(Err(anyhow!("cannot initialize, still building")))
1307 }
1308 }
1309 }
1310
1311 pub fn run_to_position(
1312 &mut self,
1313 breakpoint: SourceBreakpoint,
1314 active_thread_id: ThreadId,
1315 cx: &mut Context<Self>,
1316 ) {
1317 match &mut self.mode {
1318 SessionState::Running(local_mode) => {
1319 if !matches!(
1320 self.thread_states.thread_state(active_thread_id),
1321 Some(ThreadStatus::Stopped)
1322 ) {
1323 return;
1324 };
1325 let path = breakpoint.path.clone();
1326 local_mode.tmp_breakpoint = Some(breakpoint);
1327 let task = local_mode.send_breakpoints_from_path(
1328 path,
1329 BreakpointUpdatedReason::Toggled,
1330 &self.breakpoint_store,
1331 cx,
1332 );
1333
1334 cx.spawn(async move |this, cx| {
1335 task.await;
1336 this.update(cx, |this, cx| {
1337 this.continue_thread(active_thread_id, cx);
1338 })
1339 })
1340 .detach();
1341 }
1342 SessionState::Building(_) => {}
1343 }
1344 }
1345
1346 pub fn has_new_output(&self, last_update: OutputToken) -> bool {
1347 self.output_token.0.checked_sub(last_update.0).unwrap_or(0) != 0
1348 }
1349
1350 pub fn output(
1351 &self,
1352 since: OutputToken,
1353 ) -> (impl Iterator<Item = &dap::OutputEvent>, OutputToken) {
1354 if self.output_token.0 == 0 {
1355 return (self.output.range(0..0), OutputToken(0));
1356 };
1357
1358 let events_since = self.output_token.0.checked_sub(since.0).unwrap_or(0);
1359
1360 let clamped_events_since = events_since.clamp(0, self.output.len());
1361 (
1362 self.output
1363 .range(self.output.len() - clamped_events_since..),
1364 self.output_token,
1365 )
1366 }
1367
1368 pub fn respond_to_client(
1369 &self,
1370 request_seq: u64,
1371 success: bool,
1372 command: String,
1373 body: Option<serde_json::Value>,
1374 cx: &mut Context<Self>,
1375 ) -> Task<Result<()>> {
1376 let Some(local_session) = self.as_running() else {
1377 unreachable!("Cannot respond to remote client");
1378 };
1379 let client = local_session.client.clone();
1380
1381 cx.background_spawn(async move {
1382 client
1383 .send_message(Message::Response(Response {
1384 body,
1385 success,
1386 command,
1387 seq: request_seq + 1,
1388 request_seq,
1389 message: None,
1390 }))
1391 .await
1392 })
1393 }
1394
1395 fn handle_stopped_event(&mut self, event: StoppedEvent, cx: &mut Context<Self>) {
1396 self.mode.stopped();
1397 // todo(debugger): Find a clean way to get around the clone
1398 let breakpoint_store = self.breakpoint_store.clone();
1399 if let Some((local, path)) = self.as_running_mut().and_then(|local| {
1400 let breakpoint = local.tmp_breakpoint.take()?;
1401 let path = breakpoint.path.clone();
1402 Some((local, path))
1403 }) {
1404 local
1405 .send_breakpoints_from_path(
1406 path,
1407 BreakpointUpdatedReason::Toggled,
1408 &breakpoint_store,
1409 cx,
1410 )
1411 .detach();
1412 };
1413
1414 if event.all_threads_stopped.unwrap_or_default() || event.thread_id.is_none() {
1415 self.thread_states.stop_all_threads();
1416 self.invalidate_command_type::<StackTraceCommand>();
1417 }
1418
1419 // Event if we stopped all threads we still need to insert the thread_id
1420 // to our own data
1421 if let Some(thread_id) = event.thread_id {
1422 self.thread_states.stop_thread(ThreadId(thread_id));
1423
1424 self.invalidate_state(
1425 &StackTraceCommand {
1426 thread_id,
1427 start_frame: None,
1428 levels: None,
1429 }
1430 .into(),
1431 );
1432 }
1433
1434 self.invalidate_generic();
1435 self.threads.clear();
1436 self.variables.clear();
1437 cx.emit(SessionEvent::Stopped(
1438 event
1439 .thread_id
1440 .map(Into::into)
1441 .filter(|_| !event.preserve_focus_hint.unwrap_or(false)),
1442 ));
1443 cx.emit(SessionEvent::InvalidateInlineValue);
1444 cx.notify();
1445 }
1446
1447 pub(crate) fn handle_dap_event(&mut self, event: Box<Events>, cx: &mut Context<Self>) {
1448 match *event {
1449 Events::Initialized(_) => {
1450 debug_assert!(
1451 false,
1452 "Initialized event should have been handled in LocalMode"
1453 );
1454 }
1455 Events::Stopped(event) => self.handle_stopped_event(event, cx),
1456 Events::Continued(event) => {
1457 if event.all_threads_continued.unwrap_or_default() {
1458 self.thread_states.continue_all_threads();
1459 self.breakpoint_store.update(cx, |store, cx| {
1460 store.remove_active_position(Some(self.session_id()), cx)
1461 });
1462 } else {
1463 self.thread_states
1464 .continue_thread(ThreadId(event.thread_id));
1465 }
1466 // todo(debugger): We should be able to get away with only invalidating generic if all threads were continued
1467 self.invalidate_generic();
1468 }
1469 Events::Exited(_event) => {
1470 self.clear_active_debug_line(cx);
1471 }
1472 Events::Terminated(_) => {
1473 self.shutdown(cx).detach();
1474 }
1475 Events::Thread(event) => {
1476 let thread_id = ThreadId(event.thread_id);
1477
1478 match event.reason {
1479 dap::ThreadEventReason::Started => {
1480 self.thread_states.continue_thread(thread_id);
1481 }
1482 dap::ThreadEventReason::Exited => {
1483 self.thread_states.exit_thread(thread_id);
1484 }
1485 reason => {
1486 log::error!("Unhandled thread event reason {:?}", reason);
1487 }
1488 }
1489 self.invalidate_state(&ThreadsCommand.into());
1490 cx.notify();
1491 }
1492 Events::Output(event) => {
1493 if event
1494 .category
1495 .as_ref()
1496 .is_some_and(|category| *category == OutputEventCategory::Telemetry)
1497 {
1498 return;
1499 }
1500
1501 self.push_output(event);
1502 cx.notify();
1503 }
1504 Events::Breakpoint(event) => self.breakpoint_store.update(cx, |store, _| {
1505 store.update_session_breakpoint(self.session_id(), event.reason, event.breakpoint);
1506 }),
1507 Events::Module(event) => {
1508 match event.reason {
1509 dap::ModuleEventReason::New => {
1510 self.modules.push(event.module);
1511 }
1512 dap::ModuleEventReason::Changed => {
1513 if let Some(module) = self
1514 .modules
1515 .iter_mut()
1516 .find(|other| event.module.id == other.id)
1517 {
1518 *module = event.module;
1519 }
1520 }
1521 dap::ModuleEventReason::Removed => {
1522 self.modules.retain(|other| event.module.id != other.id);
1523 }
1524 }
1525
1526 // todo(debugger): We should only send the invalidate command to downstream clients.
1527 // self.invalidate_state(&ModulesCommand.into());
1528 }
1529 Events::LoadedSource(_) => {
1530 self.invalidate_state(&LoadedSourcesCommand.into());
1531 }
1532 Events::Capabilities(event) => {
1533 self.capabilities = self.capabilities.merge(event.capabilities);
1534
1535 // The adapter might've enabled new exception breakpoints (or disabled existing ones).
1536 let recent_filters = self
1537 .capabilities
1538 .exception_breakpoint_filters
1539 .iter()
1540 .flatten()
1541 .map(|filter| (filter.filter.clone(), filter.clone()))
1542 .collect::<BTreeMap<_, _>>();
1543 for filter in recent_filters.values() {
1544 let default = filter.default.unwrap_or_default();
1545 self.exception_breakpoints
1546 .entry(filter.filter.clone())
1547 .or_insert_with(|| (filter.clone(), default));
1548 }
1549 self.exception_breakpoints
1550 .retain(|k, _| recent_filters.contains_key(k));
1551 if self.is_started() {
1552 self.send_exception_breakpoints(cx);
1553 }
1554
1555 // Remove the ones that no longer exist.
1556 cx.notify();
1557 }
1558 Events::Memory(_) => {}
1559 Events::Process(_) => {}
1560 Events::ProgressEnd(_) => {}
1561 Events::ProgressStart(_) => {}
1562 Events::ProgressUpdate(_) => {}
1563 Events::Invalidated(_) => {}
1564 Events::Other(_) => {}
1565 }
1566 }
1567
1568 /// Ensure that there's a request in flight for the given command, and if not, send it. Use this to run requests that are idempotent.
1569 fn fetch<T: LocalDapCommand + PartialEq + Eq + Hash>(
1570 &mut self,
1571 request: T,
1572 process_result: impl FnOnce(&mut Self, Result<T::Response>, &mut Context<Self>) + 'static,
1573 cx: &mut Context<Self>,
1574 ) {
1575 const {
1576 assert!(
1577 T::CACHEABLE,
1578 "Only requests marked as cacheable should invoke `fetch`"
1579 );
1580 }
1581
1582 if !self.thread_states.any_stopped_thread()
1583 && request.type_id() != TypeId::of::<ThreadsCommand>()
1584 || self.is_session_terminated
1585 {
1586 return;
1587 }
1588
1589 let request_map = self
1590 .requests
1591 .entry(std::any::TypeId::of::<T>())
1592 .or_default();
1593
1594 if let Entry::Vacant(vacant) = request_map.entry(request.into()) {
1595 let command = vacant.key().0.clone().as_any_arc().downcast::<T>().unwrap();
1596
1597 let task = Self::request_inner::<Arc<T>>(
1598 &self.capabilities,
1599 &self.mode,
1600 command,
1601 |this, result, cx| {
1602 process_result(this, result, cx);
1603 None
1604 },
1605 cx,
1606 );
1607 let task = cx
1608 .background_executor()
1609 .spawn(async move {
1610 let _ = task.await?;
1611 Some(())
1612 })
1613 .shared();
1614
1615 vacant.insert(task);
1616 cx.notify();
1617 }
1618 }
1619
1620 fn request_inner<T: LocalDapCommand + PartialEq + Eq + Hash>(
1621 capabilities: &Capabilities,
1622 mode: &SessionState,
1623 request: T,
1624 process_result: impl FnOnce(
1625 &mut Self,
1626 Result<T::Response>,
1627 &mut Context<Self>,
1628 ) -> Option<T::Response>
1629 + 'static,
1630 cx: &mut Context<Self>,
1631 ) -> Task<Option<T::Response>> {
1632 if !T::is_supported(&capabilities) {
1633 log::warn!(
1634 "Attempted to send a DAP request that isn't supported: {:?}",
1635 request
1636 );
1637 let error = Err(anyhow::Error::msg(
1638 "Couldn't complete request because it's not supported",
1639 ));
1640 return cx.spawn(async move |this, cx| {
1641 this.update(cx, |this, cx| process_result(this, error, cx))
1642 .ok()
1643 .flatten()
1644 });
1645 }
1646
1647 let request = mode.request_dap(request);
1648 cx.spawn(async move |this, cx| {
1649 let result = request.await;
1650 this.update(cx, |this, cx| process_result(this, result, cx))
1651 .ok()
1652 .flatten()
1653 })
1654 }
1655
1656 fn request<T: LocalDapCommand + PartialEq + Eq + Hash>(
1657 &self,
1658 request: T,
1659 process_result: impl FnOnce(
1660 &mut Self,
1661 Result<T::Response>,
1662 &mut Context<Self>,
1663 ) -> Option<T::Response>
1664 + 'static,
1665 cx: &mut Context<Self>,
1666 ) -> Task<Option<T::Response>> {
1667 Self::request_inner(&self.capabilities, &self.mode, request, process_result, cx)
1668 }
1669
1670 fn invalidate_command_type<Command: LocalDapCommand>(&mut self) {
1671 self.requests.remove(&std::any::TypeId::of::<Command>());
1672 }
1673
1674 fn invalidate_generic(&mut self) {
1675 self.invalidate_command_type::<ModulesCommand>();
1676 self.invalidate_command_type::<LoadedSourcesCommand>();
1677 self.invalidate_command_type::<ThreadsCommand>();
1678 self.invalidate_command_type::<DataBreakpointInfoCommand>();
1679 self.invalidate_command_type::<ReadMemory>();
1680 let executor = self.as_running().map(|running| running.executor.clone());
1681 if let Some(executor) = executor {
1682 self.memory.clear(&executor);
1683 }
1684 }
1685
1686 fn invalidate_state(&mut self, key: &RequestSlot) {
1687 self.requests
1688 .entry((&*key.0 as &dyn Any).type_id())
1689 .and_modify(|request_map| {
1690 request_map.remove(&key);
1691 });
1692 }
1693
1694 fn push_output(&mut self, event: OutputEvent) {
1695 self.output.push_back(event);
1696 self.output_token.0 += 1;
1697 }
1698
1699 pub fn any_stopped_thread(&self) -> bool {
1700 self.thread_states.any_stopped_thread()
1701 }
1702
1703 pub fn thread_status(&self, thread_id: ThreadId) -> ThreadStatus {
1704 self.thread_states.thread_status(thread_id)
1705 }
1706
1707 pub fn threads(&mut self, cx: &mut Context<Self>) -> Vec<(dap::Thread, ThreadStatus)> {
1708 self.fetch(
1709 dap_command::ThreadsCommand,
1710 |this, result, cx| {
1711 let Some(result) = result.log_err() else {
1712 return;
1713 };
1714
1715 this.threads = result
1716 .into_iter()
1717 .map(|thread| (ThreadId(thread.id), Thread::from(thread.clone())))
1718 .collect();
1719
1720 this.invalidate_command_type::<StackTraceCommand>();
1721 cx.emit(SessionEvent::Threads);
1722 cx.notify();
1723 },
1724 cx,
1725 );
1726
1727 self.threads
1728 .values()
1729 .map(|thread| {
1730 (
1731 thread.dap.clone(),
1732 self.thread_states.thread_status(ThreadId(thread.dap.id)),
1733 )
1734 })
1735 .collect()
1736 }
1737
1738 pub fn modules(&mut self, cx: &mut Context<Self>) -> &[Module] {
1739 self.fetch(
1740 dap_command::ModulesCommand,
1741 |this, result, cx| {
1742 let Some(result) = result.log_err() else {
1743 return;
1744 };
1745
1746 this.modules = result;
1747 cx.emit(SessionEvent::Modules);
1748 cx.notify();
1749 },
1750 cx,
1751 );
1752
1753 &self.modules
1754 }
1755
1756 // CodeLLDB returns the size of a pointed-to-memory, which we can use to make the experience of go-to-memory better.
1757 pub fn data_access_size(
1758 &mut self,
1759 frame_id: Option<u64>,
1760 evaluate_name: &str,
1761 cx: &mut Context<Self>,
1762 ) -> Task<Option<u64>> {
1763 let request = self.request(
1764 EvaluateCommand {
1765 expression: format!("?${{sizeof({evaluate_name})}}"),
1766 frame_id,
1767
1768 context: Some(EvaluateArgumentsContext::Repl),
1769 source: None,
1770 },
1771 |_, response, _| response.ok(),
1772 cx,
1773 );
1774 cx.background_spawn(async move {
1775 let result = request.await?;
1776 result.result.parse().ok()
1777 })
1778 }
1779
1780 pub fn memory_reference_of_expr(
1781 &mut self,
1782 frame_id: Option<u64>,
1783 expression: String,
1784 cx: &mut Context<Self>,
1785 ) -> Task<Option<(String, Option<String>)>> {
1786 let request = self.request(
1787 EvaluateCommand {
1788 expression,
1789 frame_id,
1790
1791 context: Some(EvaluateArgumentsContext::Repl),
1792 source: None,
1793 },
1794 |_, response, _| response.ok(),
1795 cx,
1796 );
1797 cx.background_spawn(async move {
1798 let result = request.await?;
1799 result
1800 .memory_reference
1801 .map(|reference| (reference, result.type_))
1802 })
1803 }
1804
1805 pub fn write_memory(&mut self, address: u64, data: &[u8], cx: &mut Context<Self>) {
1806 let data = base64::engine::general_purpose::STANDARD.encode(data);
1807 self.request(
1808 WriteMemoryArguments {
1809 memory_reference: address.to_string(),
1810 data,
1811 allow_partial: None,
1812 offset: None,
1813 },
1814 |this, response, cx| {
1815 this.memory.clear(cx.background_executor());
1816 this.invalidate_command_type::<ReadMemory>();
1817 this.invalidate_command_type::<VariablesCommand>();
1818 cx.emit(SessionEvent::Variables);
1819 response.ok()
1820 },
1821 cx,
1822 )
1823 .detach();
1824 }
1825 pub fn read_memory(
1826 &mut self,
1827 range: RangeInclusive<u64>,
1828 cx: &mut Context<Self>,
1829 ) -> MemoryIterator {
1830 // This function is a bit more involved when it comes to fetching data.
1831 // Since we attempt to read memory in pages, we need to account for some parts
1832 // of memory being unreadable. Therefore, we start off by fetching a page per request.
1833 // In case that fails, we try to re-fetch smaller regions until we have the full range.
1834 let page_range = Memory::memory_range_to_page_range(range.clone());
1835 for page_address in PageAddress::iter_range(page_range) {
1836 self.read_single_page_memory(page_address, cx);
1837 }
1838 self.memory.memory_range(range)
1839 }
1840
1841 fn read_single_page_memory(&mut self, page_start: PageAddress, cx: &mut Context<Self>) {
1842 _ = maybe!({
1843 let builder = self.memory.build_page(page_start)?;
1844
1845 self.memory_read_fetch_page_recursive(builder, cx);
1846 Some(())
1847 });
1848 }
1849 fn memory_read_fetch_page_recursive(
1850 &mut self,
1851 mut builder: MemoryPageBuilder,
1852 cx: &mut Context<Self>,
1853 ) {
1854 let Some(next_request) = builder.next_request() else {
1855 // We're done fetching. Let's grab the page and insert it into our memory store.
1856 let (address, contents) = builder.build();
1857 self.memory.insert_page(address, contents);
1858
1859 return;
1860 };
1861 let size = next_request.size;
1862 self.fetch(
1863 ReadMemory {
1864 memory_reference: format!("0x{:X}", next_request.address),
1865 offset: Some(0),
1866 count: next_request.size,
1867 },
1868 move |this, memory, cx| {
1869 if let Ok(memory) = memory {
1870 builder.known(memory.content);
1871 if let Some(unknown) = memory.unreadable_bytes {
1872 builder.unknown(unknown);
1873 }
1874 // This is the recursive bit: if we're not yet done with
1875 // the whole page, we'll kick off a new request with smaller range.
1876 // Note that this function is recursive only conceptually;
1877 // since it kicks off a new request with callback, we don't need to worry about stack overflow.
1878 this.memory_read_fetch_page_recursive(builder, cx);
1879 } else {
1880 builder.unknown(size);
1881 }
1882 },
1883 cx,
1884 );
1885 }
1886
1887 pub fn ignore_breakpoints(&self) -> bool {
1888 self.ignore_breakpoints
1889 }
1890
1891 pub fn toggle_ignore_breakpoints(
1892 &mut self,
1893 cx: &mut App,
1894 ) -> Task<HashMap<Arc<Path>, anyhow::Error>> {
1895 self.set_ignore_breakpoints(!self.ignore_breakpoints, cx)
1896 }
1897
1898 pub(crate) fn set_ignore_breakpoints(
1899 &mut self,
1900 ignore: bool,
1901 cx: &mut App,
1902 ) -> Task<HashMap<Arc<Path>, anyhow::Error>> {
1903 if self.ignore_breakpoints == ignore {
1904 return Task::ready(HashMap::default());
1905 }
1906
1907 self.ignore_breakpoints = ignore;
1908
1909 if let Some(local) = self.as_running() {
1910 local.send_source_breakpoints(ignore, &self.breakpoint_store, cx)
1911 } else {
1912 // todo(debugger): We need to propagate this change to downstream sessions and send a message to upstream sessions
1913 unimplemented!()
1914 }
1915 }
1916
1917 pub fn data_breakpoints(&self) -> impl Iterator<Item = &DataBreakpointState> {
1918 self.data_breakpoints.values()
1919 }
1920
1921 pub fn exception_breakpoints(
1922 &self,
1923 ) -> impl Iterator<Item = &(ExceptionBreakpointsFilter, IsEnabled)> {
1924 self.exception_breakpoints.values()
1925 }
1926
1927 pub fn toggle_exception_breakpoint(&mut self, id: &str, cx: &App) {
1928 if let Some((_, is_enabled)) = self.exception_breakpoints.get_mut(id) {
1929 *is_enabled = !*is_enabled;
1930 self.send_exception_breakpoints(cx);
1931 }
1932 }
1933
1934 fn send_exception_breakpoints(&mut self, cx: &App) {
1935 if let Some(local) = self.as_running() {
1936 let exception_filters = self
1937 .exception_breakpoints
1938 .values()
1939 .filter_map(|(filter, is_enabled)| is_enabled.then(|| filter.clone()))
1940 .collect();
1941
1942 let supports_exception_filters = self
1943 .capabilities
1944 .supports_exception_filter_options
1945 .unwrap_or_default();
1946 local
1947 .send_exception_breakpoints(exception_filters, supports_exception_filters)
1948 .detach_and_log_err(cx);
1949 } else {
1950 debug_assert!(false, "Not implemented");
1951 }
1952 }
1953
1954 pub fn toggle_data_breakpoint(&mut self, id: &str, cx: &mut Context<'_, Session>) {
1955 if let Some(state) = self.data_breakpoints.get_mut(id) {
1956 state.is_enabled = !state.is_enabled;
1957 self.send_exception_breakpoints(cx);
1958 }
1959 }
1960
1961 fn send_data_breakpoints(&mut self, cx: &mut Context<Self>) {
1962 if let Some(mode) = self.as_running() {
1963 let breakpoints = self
1964 .data_breakpoints
1965 .values()
1966 .filter_map(|state| state.is_enabled.then(|| state.dap.clone()))
1967 .collect();
1968 let command = SetDataBreakpointsCommand { breakpoints };
1969 mode.request(command).detach_and_log_err(cx);
1970 }
1971 }
1972
1973 pub fn create_data_breakpoint(
1974 &mut self,
1975 context: Arc<DataBreakpointContext>,
1976 data_id: String,
1977 dap: dap::DataBreakpoint,
1978 cx: &mut Context<Self>,
1979 ) {
1980 if self.data_breakpoints.remove(&data_id).is_none() {
1981 self.data_breakpoints.insert(
1982 data_id,
1983 DataBreakpointState {
1984 dap,
1985 is_enabled: true,
1986 context,
1987 },
1988 );
1989 }
1990 self.send_data_breakpoints(cx);
1991 }
1992
1993 pub fn breakpoints_enabled(&self) -> bool {
1994 self.ignore_breakpoints
1995 }
1996
1997 pub fn loaded_sources(&mut self, cx: &mut Context<Self>) -> &[Source] {
1998 self.fetch(
1999 dap_command::LoadedSourcesCommand,
2000 |this, result, cx| {
2001 let Some(result) = result.log_err() else {
2002 return;
2003 };
2004 this.loaded_sources = result;
2005 cx.emit(SessionEvent::LoadedSources);
2006 cx.notify();
2007 },
2008 cx,
2009 );
2010
2011 &self.loaded_sources
2012 }
2013
2014 fn fallback_to_manual_restart(
2015 &mut self,
2016 res: Result<()>,
2017 cx: &mut Context<Self>,
2018 ) -> Option<()> {
2019 if res.log_err().is_none() {
2020 cx.emit(SessionStateEvent::Restart);
2021 return None;
2022 }
2023 Some(())
2024 }
2025
2026 fn empty_response(&mut self, res: Result<()>, _cx: &mut Context<Self>) -> Option<()> {
2027 res.log_err()?;
2028 Some(())
2029 }
2030
2031 fn on_step_response<T: LocalDapCommand + PartialEq + Eq + Hash>(
2032 thread_id: ThreadId,
2033 ) -> impl FnOnce(&mut Self, Result<T::Response>, &mut Context<Self>) -> Option<T::Response> + 'static
2034 {
2035 move |this, response, cx| match response.log_err() {
2036 Some(response) => {
2037 this.breakpoint_store.update(cx, |store, cx| {
2038 store.remove_active_position(Some(this.session_id()), cx)
2039 });
2040 Some(response)
2041 }
2042 None => {
2043 this.thread_states.stop_thread(thread_id);
2044 cx.notify();
2045 None
2046 }
2047 }
2048 }
2049
2050 fn clear_active_debug_line_response(
2051 &mut self,
2052 response: Result<()>,
2053 cx: &mut Context<Session>,
2054 ) -> Option<()> {
2055 response.log_err()?;
2056 self.clear_active_debug_line(cx);
2057 Some(())
2058 }
2059
2060 fn clear_active_debug_line(&mut self, cx: &mut Context<Session>) {
2061 self.breakpoint_store.update(cx, |store, cx| {
2062 store.remove_active_position(Some(self.id), cx)
2063 });
2064 }
2065
2066 pub fn pause_thread(&mut self, thread_id: ThreadId, cx: &mut Context<Self>) {
2067 self.request(
2068 PauseCommand {
2069 thread_id: thread_id.0,
2070 },
2071 Self::empty_response,
2072 cx,
2073 )
2074 .detach();
2075 }
2076
2077 pub fn restart_stack_frame(&mut self, stack_frame_id: u64, cx: &mut Context<Self>) {
2078 self.request(
2079 RestartStackFrameCommand { stack_frame_id },
2080 Self::empty_response,
2081 cx,
2082 )
2083 .detach();
2084 }
2085
2086 pub fn restart(&mut self, args: Option<Value>, cx: &mut Context<Self>) {
2087 if self.restart_task.is_some() || self.as_running().is_none() {
2088 return;
2089 }
2090
2091 let supports_dap_restart =
2092 self.capabilities.supports_restart_request.unwrap_or(false) && !self.is_terminated();
2093
2094 self.restart_task = Some(cx.spawn(async move |this, cx| {
2095 let _ = this.update(cx, |session, cx| {
2096 if supports_dap_restart {
2097 session
2098 .request(
2099 RestartCommand {
2100 raw: args.unwrap_or(Value::Null),
2101 },
2102 Self::fallback_to_manual_restart,
2103 cx,
2104 )
2105 .detach();
2106 } else {
2107 cx.emit(SessionStateEvent::Restart);
2108 }
2109 });
2110 }));
2111 }
2112
2113 pub fn shutdown(&mut self, cx: &mut Context<Self>) -> Task<()> {
2114 if self.is_session_terminated {
2115 return Task::ready(());
2116 }
2117
2118 self.is_session_terminated = true;
2119 self.thread_states.exit_all_threads();
2120 cx.notify();
2121
2122 let task = match &mut self.mode {
2123 SessionState::Running(_) => {
2124 if self
2125 .capabilities
2126 .supports_terminate_request
2127 .unwrap_or_default()
2128 {
2129 self.request(
2130 TerminateCommand {
2131 restart: Some(false),
2132 },
2133 Self::clear_active_debug_line_response,
2134 cx,
2135 )
2136 } else {
2137 self.request(
2138 DisconnectCommand {
2139 restart: Some(false),
2140 terminate_debuggee: Some(true),
2141 suspend_debuggee: Some(false),
2142 },
2143 Self::clear_active_debug_line_response,
2144 cx,
2145 )
2146 }
2147 }
2148 SessionState::Building(build_task) => {
2149 build_task.take();
2150 Task::ready(Some(()))
2151 }
2152 };
2153
2154 cx.emit(SessionStateEvent::Shutdown);
2155
2156 cx.spawn(async move |this, cx| {
2157 task.await;
2158 let _ = this.update(cx, |this, _| {
2159 if let Some(adapter_client) = this.adapter_client() {
2160 adapter_client.kill();
2161 }
2162 });
2163 })
2164 }
2165
2166 pub fn completions(
2167 &mut self,
2168 query: CompletionsQuery,
2169 cx: &mut Context<Self>,
2170 ) -> Task<Result<Vec<dap::CompletionItem>>> {
2171 let task = self.request(query, |_, result, _| result.log_err(), cx);
2172
2173 cx.background_executor().spawn(async move {
2174 anyhow::Ok(
2175 task.await
2176 .map(|response| response.targets)
2177 .context("failed to fetch completions")?,
2178 )
2179 })
2180 }
2181
2182 pub fn continue_thread(&mut self, thread_id: ThreadId, cx: &mut Context<Self>) {
2183 let supports_single_thread_execution_requests =
2184 self.capabilities.supports_single_thread_execution_requests;
2185 self.thread_states.continue_thread(thread_id);
2186 self.request(
2187 ContinueCommand {
2188 args: ContinueArguments {
2189 thread_id: thread_id.0,
2190 single_thread: supports_single_thread_execution_requests,
2191 },
2192 },
2193 Self::on_step_response::<ContinueCommand>(thread_id),
2194 cx,
2195 )
2196 .detach();
2197 }
2198
2199 pub fn adapter_client(&self) -> Option<Arc<DebugAdapterClient>> {
2200 match self.mode {
2201 SessionState::Running(ref local) => Some(local.client.clone()),
2202 SessionState::Building(_) => None,
2203 }
2204 }
2205
2206 pub fn has_ever_stopped(&self) -> bool {
2207 self.mode.has_ever_stopped()
2208 }
2209 pub fn step_over(
2210 &mut self,
2211 thread_id: ThreadId,
2212 granularity: SteppingGranularity,
2213 cx: &mut Context<Self>,
2214 ) {
2215 let supports_single_thread_execution_requests =
2216 self.capabilities.supports_single_thread_execution_requests;
2217 let supports_stepping_granularity = self
2218 .capabilities
2219 .supports_stepping_granularity
2220 .unwrap_or_default();
2221
2222 let command = NextCommand {
2223 inner: StepCommand {
2224 thread_id: thread_id.0,
2225 granularity: supports_stepping_granularity.then(|| granularity),
2226 single_thread: supports_single_thread_execution_requests,
2227 },
2228 };
2229
2230 self.thread_states.process_step(thread_id);
2231 self.request(
2232 command,
2233 Self::on_step_response::<NextCommand>(thread_id),
2234 cx,
2235 )
2236 .detach();
2237 }
2238
2239 pub fn step_in(
2240 &mut self,
2241 thread_id: ThreadId,
2242 granularity: SteppingGranularity,
2243 cx: &mut Context<Self>,
2244 ) {
2245 let supports_single_thread_execution_requests =
2246 self.capabilities.supports_single_thread_execution_requests;
2247 let supports_stepping_granularity = self
2248 .capabilities
2249 .supports_stepping_granularity
2250 .unwrap_or_default();
2251
2252 let command = StepInCommand {
2253 inner: StepCommand {
2254 thread_id: thread_id.0,
2255 granularity: supports_stepping_granularity.then(|| granularity),
2256 single_thread: supports_single_thread_execution_requests,
2257 },
2258 };
2259
2260 self.thread_states.process_step(thread_id);
2261 self.request(
2262 command,
2263 Self::on_step_response::<StepInCommand>(thread_id),
2264 cx,
2265 )
2266 .detach();
2267 }
2268
2269 pub fn step_out(
2270 &mut self,
2271 thread_id: ThreadId,
2272 granularity: SteppingGranularity,
2273 cx: &mut Context<Self>,
2274 ) {
2275 let supports_single_thread_execution_requests =
2276 self.capabilities.supports_single_thread_execution_requests;
2277 let supports_stepping_granularity = self
2278 .capabilities
2279 .supports_stepping_granularity
2280 .unwrap_or_default();
2281
2282 let command = StepOutCommand {
2283 inner: StepCommand {
2284 thread_id: thread_id.0,
2285 granularity: supports_stepping_granularity.then(|| granularity),
2286 single_thread: supports_single_thread_execution_requests,
2287 },
2288 };
2289
2290 self.thread_states.process_step(thread_id);
2291 self.request(
2292 command,
2293 Self::on_step_response::<StepOutCommand>(thread_id),
2294 cx,
2295 )
2296 .detach();
2297 }
2298
2299 pub fn step_back(
2300 &mut self,
2301 thread_id: ThreadId,
2302 granularity: SteppingGranularity,
2303 cx: &mut Context<Self>,
2304 ) {
2305 let supports_single_thread_execution_requests =
2306 self.capabilities.supports_single_thread_execution_requests;
2307 let supports_stepping_granularity = self
2308 .capabilities
2309 .supports_stepping_granularity
2310 .unwrap_or_default();
2311
2312 let command = StepBackCommand {
2313 inner: StepCommand {
2314 thread_id: thread_id.0,
2315 granularity: supports_stepping_granularity.then(|| granularity),
2316 single_thread: supports_single_thread_execution_requests,
2317 },
2318 };
2319
2320 self.thread_states.process_step(thread_id);
2321
2322 self.request(
2323 command,
2324 Self::on_step_response::<StepBackCommand>(thread_id),
2325 cx,
2326 )
2327 .detach();
2328 }
2329
2330 pub fn stack_frames(
2331 &mut self,
2332 thread_id: ThreadId,
2333 cx: &mut Context<Self>,
2334 ) -> Result<Vec<StackFrame>> {
2335 if self.thread_states.thread_status(thread_id) == ThreadStatus::Stopped
2336 && self.requests.contains_key(&ThreadsCommand.type_id())
2337 && self.threads.contains_key(&thread_id)
2338 // ^ todo(debugger): We need a better way to check that we're not querying stale data
2339 // We could still be using an old thread id and have sent a new thread's request
2340 // This isn't the biggest concern right now because it hasn't caused any issues outside of tests
2341 // But it very well could cause a minor bug in the future that is hard to track down
2342 {
2343 self.fetch(
2344 super::dap_command::StackTraceCommand {
2345 thread_id: thread_id.0,
2346 start_frame: None,
2347 levels: None,
2348 },
2349 move |this, stack_frames, cx| {
2350 let entry =
2351 this.threads
2352 .entry(thread_id)
2353 .and_modify(|thread| match &stack_frames {
2354 Ok(stack_frames) => {
2355 thread.stack_frames = stack_frames
2356 .iter()
2357 .cloned()
2358 .map(StackFrame::from)
2359 .collect();
2360 thread.stack_frames_error = None;
2361 }
2362 Err(error) => {
2363 thread.stack_frames.clear();
2364 thread.stack_frames_error = Some(error.cloned());
2365 }
2366 });
2367 debug_assert!(
2368 matches!(entry, indexmap::map::Entry::Occupied(_)),
2369 "Sent request for thread_id that doesn't exist"
2370 );
2371 if let Ok(stack_frames) = stack_frames {
2372 this.stack_frames.extend(
2373 stack_frames
2374 .into_iter()
2375 .filter(|frame| {
2376 // Workaround for JavaScript debug adapter sending out "fake" stack frames for delineating await points. This is fine,
2377 // except that they always use an id of 0 for it, which collides with other (valid) stack frames.
2378 !(frame.id == 0
2379 && frame.line == 0
2380 && frame.column == 0
2381 && frame.presentation_hint
2382 == Some(StackFramePresentationHint::Label))
2383 })
2384 .map(|frame| (frame.id, StackFrame::from(frame))),
2385 );
2386 }
2387
2388 this.invalidate_command_type::<ScopesCommand>();
2389 this.invalidate_command_type::<VariablesCommand>();
2390
2391 cx.emit(SessionEvent::StackTrace);
2392 },
2393 cx,
2394 );
2395 }
2396
2397 match self.threads.get(&thread_id) {
2398 Some(thread) => {
2399 if let Some(error) = &thread.stack_frames_error {
2400 Err(error.cloned())
2401 } else {
2402 Ok(thread.stack_frames.clone())
2403 }
2404 }
2405 None => Ok(Vec::new()),
2406 }
2407 }
2408
2409 pub fn scopes(&mut self, stack_frame_id: u64, cx: &mut Context<Self>) -> &[dap::Scope] {
2410 if self.requests.contains_key(&TypeId::of::<ThreadsCommand>())
2411 && self
2412 .requests
2413 .contains_key(&TypeId::of::<StackTraceCommand>())
2414 {
2415 self.fetch(
2416 ScopesCommand { stack_frame_id },
2417 move |this, scopes, cx| {
2418 let Some(scopes) = scopes.log_err() else {
2419 return
2420 };
2421
2422 for scope in scopes.iter() {
2423 this.variables(scope.variables_reference, cx);
2424 }
2425
2426 let entry = this
2427 .stack_frames
2428 .entry(stack_frame_id)
2429 .and_modify(|stack_frame| {
2430 stack_frame.scopes = scopes;
2431 });
2432
2433 cx.emit(SessionEvent::Variables);
2434
2435 debug_assert!(
2436 matches!(entry, indexmap::map::Entry::Occupied(_)),
2437 "Sent scopes request for stack_frame_id that doesn't exist or hasn't been fetched"
2438 );
2439 },
2440 cx,
2441 );
2442 }
2443
2444 self.stack_frames
2445 .get(&stack_frame_id)
2446 .map(|frame| frame.scopes.as_slice())
2447 .unwrap_or_default()
2448 }
2449
2450 pub fn variables_by_stack_frame_id(
2451 &self,
2452 stack_frame_id: StackFrameId,
2453 globals: bool,
2454 locals: bool,
2455 ) -> Vec<dap::Variable> {
2456 let Some(stack_frame) = self.stack_frames.get(&stack_frame_id) else {
2457 return Vec::new();
2458 };
2459
2460 stack_frame
2461 .scopes
2462 .iter()
2463 .filter(|scope| {
2464 (scope.name.to_lowercase().contains("local") && locals)
2465 || (scope.name.to_lowercase().contains("global") && globals)
2466 })
2467 .filter_map(|scope| self.variables.get(&scope.variables_reference))
2468 .flatten()
2469 .cloned()
2470 .collect()
2471 }
2472
2473 pub fn watchers(&self) -> &HashMap<SharedString, Watcher> {
2474 &self.watchers
2475 }
2476
2477 pub fn add_watcher(
2478 &mut self,
2479 expression: SharedString,
2480 frame_id: u64,
2481 cx: &mut Context<Self>,
2482 ) -> Task<Result<()>> {
2483 let request = self.mode.request_dap(EvaluateCommand {
2484 expression: expression.to_string(),
2485 context: Some(EvaluateArgumentsContext::Watch),
2486 frame_id: Some(frame_id),
2487 source: None,
2488 });
2489
2490 cx.spawn(async move |this, cx| {
2491 let response = request.await?;
2492
2493 this.update(cx, |session, cx| {
2494 session.watchers.insert(
2495 expression.clone(),
2496 Watcher {
2497 expression,
2498 value: response.result.into(),
2499 variables_reference: response.variables_reference,
2500 presentation_hint: response.presentation_hint,
2501 },
2502 );
2503 cx.emit(SessionEvent::Watchers);
2504 })
2505 })
2506 }
2507
2508 pub fn refresh_watchers(&mut self, frame_id: u64, cx: &mut Context<Self>) {
2509 let watches = self.watchers.clone();
2510 for (_, watch) in watches.into_iter() {
2511 self.add_watcher(watch.expression.clone(), frame_id, cx)
2512 .detach();
2513 }
2514 }
2515
2516 pub fn remove_watcher(&mut self, expression: SharedString) {
2517 self.watchers.remove(&expression);
2518 }
2519
2520 pub fn variables(
2521 &mut self,
2522 variables_reference: VariableReference,
2523 cx: &mut Context<Self>,
2524 ) -> Vec<dap::Variable> {
2525 let command = VariablesCommand {
2526 variables_reference,
2527 filter: None,
2528 start: None,
2529 count: None,
2530 format: None,
2531 };
2532
2533 self.fetch(
2534 command,
2535 move |this, variables, cx| {
2536 let Some(variables) = variables.log_err() else {
2537 return;
2538 };
2539
2540 this.variables.insert(variables_reference, variables);
2541
2542 cx.emit(SessionEvent::Variables);
2543 cx.emit(SessionEvent::InvalidateInlineValue);
2544 },
2545 cx,
2546 );
2547
2548 self.variables
2549 .get(&variables_reference)
2550 .cloned()
2551 .unwrap_or_default()
2552 }
2553
2554 pub fn data_breakpoint_info(
2555 &mut self,
2556 context: Arc<DataBreakpointContext>,
2557 mode: Option<String>,
2558 cx: &mut Context<Self>,
2559 ) -> Task<Option<dap::DataBreakpointInfoResponse>> {
2560 let command = DataBreakpointInfoCommand {
2561 context: context.clone(),
2562 mode,
2563 };
2564
2565 self.request(command, |_, response, _| response.ok(), cx)
2566 }
2567
2568 pub fn set_variable_value(
2569 &mut self,
2570 stack_frame_id: u64,
2571 variables_reference: u64,
2572 name: String,
2573 value: String,
2574 cx: &mut Context<Self>,
2575 ) {
2576 if self.capabilities.supports_set_variable.unwrap_or_default() {
2577 self.request(
2578 SetVariableValueCommand {
2579 name,
2580 value,
2581 variables_reference,
2582 },
2583 move |this, response, cx| {
2584 let response = response.log_err()?;
2585 this.invalidate_command_type::<VariablesCommand>();
2586 this.invalidate_command_type::<ReadMemory>();
2587 this.memory.clear(cx.background_executor());
2588 this.refresh_watchers(stack_frame_id, cx);
2589 cx.emit(SessionEvent::Variables);
2590 Some(response)
2591 },
2592 cx,
2593 )
2594 .detach();
2595 }
2596 }
2597
2598 pub fn evaluate(
2599 &mut self,
2600 expression: String,
2601 context: Option<EvaluateArgumentsContext>,
2602 frame_id: Option<u64>,
2603 source: Option<Source>,
2604 cx: &mut Context<Self>,
2605 ) -> Task<()> {
2606 let event = dap::OutputEvent {
2607 category: None,
2608 output: format!("> {expression}"),
2609 group: None,
2610 variables_reference: None,
2611 source: None,
2612 line: None,
2613 column: None,
2614 data: None,
2615 location_reference: None,
2616 };
2617 self.push_output(event);
2618 let request = self.mode.request_dap(EvaluateCommand {
2619 expression,
2620 context,
2621 frame_id,
2622 source,
2623 });
2624 cx.spawn(async move |this, cx| {
2625 let response = request.await;
2626 this.update(cx, |this, cx| {
2627 this.memory.clear(cx.background_executor());
2628 this.invalidate_command_type::<ReadMemory>();
2629 match response {
2630 Ok(response) => {
2631 let event = dap::OutputEvent {
2632 category: None,
2633 output: format!("< {}", &response.result),
2634 group: None,
2635 variables_reference: Some(response.variables_reference),
2636 source: None,
2637 line: None,
2638 column: None,
2639 data: None,
2640 location_reference: None,
2641 };
2642 this.push_output(event);
2643 }
2644 Err(e) => {
2645 let event = dap::OutputEvent {
2646 category: None,
2647 output: format!("{}", e),
2648 group: None,
2649 variables_reference: None,
2650 source: None,
2651 line: None,
2652 column: None,
2653 data: None,
2654 location_reference: None,
2655 };
2656 this.push_output(event);
2657 }
2658 };
2659 cx.notify();
2660 })
2661 .ok();
2662 })
2663 }
2664
2665 pub fn location(
2666 &mut self,
2667 reference: u64,
2668 cx: &mut Context<Self>,
2669 ) -> Option<dap::LocationsResponse> {
2670 self.fetch(
2671 LocationsCommand { reference },
2672 move |this, response, _| {
2673 let Some(response) = response.log_err() else {
2674 return;
2675 };
2676 this.locations.insert(reference, response);
2677 },
2678 cx,
2679 );
2680 self.locations.get(&reference).cloned()
2681 }
2682
2683 pub fn is_attached(&self) -> bool {
2684 let SessionState::Running(local_mode) = &self.mode else {
2685 return false;
2686 };
2687 local_mode.binary.request_args.request == StartDebuggingRequestArgumentsRequest::Attach
2688 }
2689
2690 pub fn disconnect_client(&mut self, cx: &mut Context<Self>) {
2691 let command = DisconnectCommand {
2692 restart: Some(false),
2693 terminate_debuggee: Some(false),
2694 suspend_debuggee: Some(false),
2695 };
2696
2697 self.request(command, Self::empty_response, cx).detach()
2698 }
2699
2700 pub fn terminate_threads(&mut self, thread_ids: Option<Vec<ThreadId>>, cx: &mut Context<Self>) {
2701 if self
2702 .capabilities
2703 .supports_terminate_threads_request
2704 .unwrap_or_default()
2705 {
2706 self.request(
2707 TerminateThreadsCommand {
2708 thread_ids: thread_ids.map(|ids| ids.into_iter().map(|id| id.0).collect()),
2709 },
2710 Self::clear_active_debug_line_response,
2711 cx,
2712 )
2713 .detach();
2714 } else {
2715 self.shutdown(cx).detach();
2716 }
2717 }
2718
2719 pub fn thread_state(&self, thread_id: ThreadId) -> Option<ThreadStatus> {
2720 self.thread_states.thread_state(thread_id)
2721 }
2722
2723 pub fn quirks(&self) -> SessionQuirks {
2724 self.quirks
2725 }
2726}