debugger: Fix restarting terminated child sessions (#29173)

Anthony Eid created

This fixes a bug where terminated child session failed to restart
because they were using the wrong configuration/binary to start a new
session

Release Notes:

- N/A

Change summary

crates/project/src/debugger/dap_store.rs | 26 ++++++++-----
crates/project/src/debugger/session.rs   | 50 ++++++++++---------------
2 files changed, 36 insertions(+), 40 deletions(-)

Detailed changes

crates/project/src/debugger/dap_store.rs 🔗

@@ -902,15 +902,6 @@ fn create_new_session(
                         this.shutdown_session(session_id, cx).detach_and_log_err(cx);
                     }
                     SessionStateEvent::Restart => {
-                        let Some((config, binary)) = session.read_with(cx, |session, _| {
-                            session
-                                .configuration()
-                                .map(|config| (config, session.binary().clone()))
-                        }) else {
-                            log::error!("Failed to get debug config from session");
-                            return;
-                        };
-
                         let mut curr_session = session;
                         while let Some(parent_id) = curr_session.read(cx).parent_id() {
                             if let Some(parent_session) = this.sessions.get(&parent_id).cloned() {
@@ -921,6 +912,15 @@ fn create_new_session(
                             }
                         }
 
+                        let Some((config, binary)) = curr_session.read_with(cx, |session, _| {
+                            session
+                                .configuration()
+                                .map(|config| (config, session.root_binary().clone()))
+                        }) else {
+                            log::error!("Failed to get debug config from session");
+                            return;
+                        };
+
                         let session_id = curr_session.read(cx).session_id();
 
                         let task = curr_session.update(cx, |session, cx| session.shutdown(cx));
@@ -931,7 +931,13 @@ fn create_new_session(
 
                             this.update(cx, |this, cx| {
                                 this.sessions.remove(&session_id);
-                                this.new_session(binary, config, worktree, None, cx)
+                                this.new_session(
+                                    binary.as_ref().clone(),
+                                    config,
+                                    worktree,
+                                    None,
+                                    cx,
+                                )
                             })?
                             .1
                             .await?;

crates/project/src/debugger/session.rs 🔗

@@ -164,6 +164,7 @@ pub struct LocalMode {
     client: Arc<DebugAdapterClient>,
     definition: DebugTaskDefinition,
     binary: DebugAdapterBinary,
+    root_binary: Option<Arc<DebugAdapterBinary>>,
     pub(crate) breakpoint_store: Entity<BreakpointStore>,
     tmp_breakpoint: Option<SourceBreakpoint>,
     worktree: WeakEntity<Worktree>,
@@ -194,36 +195,18 @@ impl LocalMode {
         binary: DebugAdapterBinary,
         messages_tx: futures::channel::mpsc::UnboundedSender<Message>,
         cx: AsyncApp,
-    ) -> Task<Result<Self>> {
-        Self::new_inner(
-            session_id,
-            parent_session,
-            breakpoint_store,
-            worktree,
-            config,
-            binary,
-            messages_tx,
-            async |_, _| {},
-            cx,
-        )
-    }
-
-    fn new_inner(
-        session_id: SessionId,
-        parent_session: Option<Entity<Session>>,
-        breakpoint_store: Entity<BreakpointStore>,
-        worktree: WeakEntity<Worktree>,
-        config: DebugTaskDefinition,
-        binary: DebugAdapterBinary,
-        messages_tx: futures::channel::mpsc::UnboundedSender<Message>,
-        on_initialized: impl AsyncFnOnce(&mut LocalMode, AsyncApp) + 'static,
-        cx: AsyncApp,
     ) -> Task<Result<Self>> {
         cx.spawn(async move |cx| {
             let message_handler = Box::new(move |message| {
                 messages_tx.unbounded_send(message).ok();
             });
 
+            let root_binary = if let Some(parent_session) = parent_session.as_ref() {
+                Some(parent_session.read_with(cx, |session, _| session.root_binary().clone())?)
+            } else {
+                None
+            };
+
             let client = Arc::new(
                 if let Some(client) = parent_session
                     .and_then(|session| cx.update(|cx| session.read(cx).adapter_client()).ok())
@@ -244,18 +227,15 @@ impl LocalMode {
                 },
             );
 
-            let mut session = Self {
+            Ok(Self {
                 client,
                 breakpoint_store,
                 worktree,
                 tmp_breakpoint: None,
                 definition: config,
+                root_binary,
                 binary,
-            };
-
-            on_initialized(&mut session, cx.clone()).await;
-
-            Ok(session)
+            })
         })
     }
 
@@ -840,6 +820,16 @@ impl Session {
         &self.capabilities
     }
 
+    pub(crate) fn root_binary(&self) -> Arc<DebugAdapterBinary> {
+        let Mode::Local(local_mode) = &self.mode else {
+            panic!("Session is not local");
+        };
+        local_mode
+            .root_binary
+            .clone()
+            .unwrap_or_else(|| Arc::new(local_mode.binary.clone()))
+    }
+
     pub fn binary(&self) -> &DebugAdapterBinary {
         let Mode::Local(local_mode) = &self.mode else {
             panic!("Session is not local");