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