gdb.rs

  1use std::{borrow::Cow, collections::HashMap, ffi::OsStr, sync::LazyLock};
  2
  3use anyhow::{Context as _, Result, bail};
  4use async_trait::async_trait;
  5use dap::{StartDebuggingRequestArguments, adapters::DebugTaskDefinition};
  6use gpui::AsyncApp;
  7use task::{DebugScenario, ZedDebugConfig};
  8
  9use crate::*;
 10
 11#[derive(Default)]
 12pub(crate) struct GdbDebugAdapter;
 13
 14impl GdbDebugAdapter {
 15    const ADAPTER_NAME: &'static str = "GDB";
 16}
 17
 18#[async_trait(?Send)]
 19impl DebugAdapter for GdbDebugAdapter {
 20    fn name(&self) -> DebugAdapterName {
 21        DebugAdapterName(Self::ADAPTER_NAME.into())
 22    }
 23
 24    async fn config_from_zed_format(&self, zed_scenario: ZedDebugConfig) -> Result<DebugScenario> {
 25        let mut obj = serde_json::Map::default();
 26
 27        match &zed_scenario.request {
 28            dap::DebugRequest::Attach(attach) => {
 29                obj.insert("request".into(), "attach".into());
 30                obj.insert("pid".into(), attach.process_id.into());
 31            }
 32
 33            dap::DebugRequest::Launch(launch) => {
 34                obj.insert("request".into(), "launch".into());
 35                obj.insert("program".into(), launch.program.clone().into());
 36
 37                if !launch.args.is_empty() {
 38                    obj.insert("args".into(), launch.args.clone().into());
 39                }
 40
 41                if !launch.env.is_empty() {
 42                    obj.insert("env".into(), launch.env_json());
 43                }
 44
 45                if let Some(stop_on_entry) = zed_scenario.stop_on_entry {
 46                    obj.insert(
 47                        "stopAtBeginningOfMainSubprogram".into(),
 48                        stop_on_entry.into(),
 49                    );
 50                }
 51                if let Some(cwd) = launch.cwd.as_ref() {
 52                    obj.insert("cwd".into(), cwd.to_string_lossy().into_owned().into());
 53                }
 54            }
 55        }
 56
 57        Ok(DebugScenario {
 58            adapter: zed_scenario.adapter,
 59            label: zed_scenario.label,
 60            build: None,
 61            config: serde_json::Value::Object(obj),
 62            tcp_connection: None,
 63        })
 64    }
 65
 66    fn dap_schema(&self) -> Cow<'static, serde_json::Value> {
 67        static SCHEMA: LazyLock<serde_json::Value> = LazyLock::new(|| {
 68            const RAW_SCHEMA: &str = include_str!("../schemas/GDB.json");
 69            serde_json::from_str(RAW_SCHEMA).unwrap()
 70        });
 71        Cow::Borrowed(&*SCHEMA)
 72    }
 73
 74    async fn get_binary(
 75        &self,
 76        delegate: &Arc<dyn DapDelegate>,
 77        config: &DebugTaskDefinition,
 78        user_installed_path: Option<std::path::PathBuf>,
 79        user_args: Option<Vec<String>>,
 80        _: &mut AsyncApp,
 81    ) -> Result<DebugAdapterBinary> {
 82        let user_setting_path = user_installed_path
 83            .filter(|p| p.exists())
 84            .and_then(|p| p.to_str().map(|s| s.to_string()));
 85
 86        let gdb_path = delegate
 87            .which(OsStr::new("gdb"))
 88            .await
 89            .and_then(|p| p.to_str().map(|s| s.to_string()))
 90            .context("Could not find gdb in path");
 91
 92        if gdb_path.is_err() && user_setting_path.is_none() {
 93            bail!("Could not find gdb path or it's not installed");
 94        }
 95
 96        let gdb_path = user_setting_path.unwrap_or(gdb_path?);
 97
 98        let mut configuration = config.config.clone();
 99        if let Some(configuration) = configuration.as_object_mut() {
100            configuration
101                .entry("cwd")
102                .or_insert_with(|| delegate.worktree_root_path().to_string_lossy().into());
103        }
104
105        Ok(DebugAdapterBinary {
106            command: Some(gdb_path),
107            arguments: user_args.unwrap_or_else(|| vec!["-i=dap".into()]),
108            envs: HashMap::default(),
109            cwd: Some(delegate.worktree_root_path().to_path_buf()),
110            connection: None,
111            request_args: StartDebuggingRequestArguments {
112                request: self.request_kind(&config.config).await?,
113                configuration,
114            },
115        })
116    }
117}