debugger/setup/adapters/
cuda_gdb.rs1use crate::common::{Error, Result};
19use crate::setup::installer::{InstallMethod, InstallOptions, InstallResult, InstallStatus, Installer};
20use crate::setup::registry::{DebuggerInfo, Platform};
21use crate::setup::verifier::{verify_dap_adapter, VerifyResult};
22use async_trait::async_trait;
23use std::path::PathBuf;
24
25use super::gdb_common::{get_gdb_version, is_gdb_version_sufficient};
26
27static INFO: DebuggerInfo = DebuggerInfo {
28 id: "cuda-gdb",
29 name: "CUDA-GDB",
30 languages: &["cuda", "c", "cpp"],
31 platforms: &[Platform::Linux],
32 description: "NVIDIA CUDA debugger for GPU code",
33 primary: true,
34};
35
36pub struct CudaGdbInstaller;
37
38async fn has_native_dap_support(cuda_gdb_path: &PathBuf) -> bool {
40 if let Some(version) = get_gdb_version(cuda_gdb_path).await {
42 if !is_gdb_version_sufficient(&version) {
43 return false;
44 }
45 } else {
46 return false;
47 }
48
49 let output = tokio::process::Command::new(cuda_gdb_path)
52 .args(["-i=dap", "-batch", "-ex", "quit"])
53 .output()
54 .await;
55
56 match output {
57 Ok(result) => {
58 let stderr = String::from_utf8_lossy(&result.stderr);
60 !stderr.contains("unrecognized") && !stderr.contains("Interpreter")
61 }
62 Err(_) => false,
63 }
64}
65
66#[async_trait]
67impl Installer for CudaGdbInstaller {
68 fn info(&self) -> &DebuggerInfo {
69 &INFO
70 }
71
72 async fn status(&self) -> Result<InstallStatus> {
73 if Platform::current() != Platform::Linux {
74 return Ok(InstallStatus::NotInstalled);
75 }
76
77 let Some(cuda_gdb_path) = find_cuda_gdb() else {
79 return Ok(InstallStatus::NotInstalled);
80 };
81
82 let version = get_gdb_version(&cuda_gdb_path).await;
83
84 if has_native_dap_support(&cuda_gdb_path).await {
86 return Ok(InstallStatus::Installed {
87 path: cuda_gdb_path,
88 version,
89 });
90 }
91
92 if let Some(cdt_adapter) = find_cdt_gdb_adapter() {
94 return Ok(InstallStatus::Installed {
95 path: cdt_adapter,
96 version,
97 });
98 }
99
100 Ok(InstallStatus::Broken {
102 path: cuda_gdb_path,
103 reason: "cuda-gdb found but lacks native DAP support. Install cdt-gdb-adapter: npm install -g cdt-gdb-adapter".to_string(),
104 })
105 }
106
107 async fn best_method(&self) -> Result<InstallMethod> {
108 if Platform::current() != Platform::Linux {
109 return Ok(InstallMethod::NotSupported {
110 reason: "CUDA-GDB GPU debugging is only supported on Linux".to_string(),
111 });
112 }
113
114 let Some(cuda_gdb_path) = find_cuda_gdb() else {
116 return Ok(InstallMethod::NotSupported {
117 reason: "CUDA-GDB not found. Install NVIDIA CUDA Toolkit from https://developer.nvidia.com/cuda-downloads".to_string(),
118 });
119 };
120
121 if has_native_dap_support(&cuda_gdb_path).await {
123 return Ok(InstallMethod::AlreadyInstalled { path: cuda_gdb_path });
124 }
125
126 if let Some(cdt_adapter) = find_cdt_gdb_adapter() {
128 return Ok(InstallMethod::AlreadyInstalled { path: cdt_adapter });
129 }
130
131 Ok(InstallMethod::NotSupported {
132 reason: "cuda-gdb lacks native DAP support. Install cdt-gdb-adapter: npm install -g cdt-gdb-adapter".to_string(),
133 })
134 }
135
136 async fn install(&self, _opts: InstallOptions) -> Result<InstallResult> {
137 let method = self.best_method().await?;
138
139 match method {
140 InstallMethod::AlreadyInstalled { path } => {
141 let cuda_gdb_path = find_cuda_gdb().ok_or_else(|| {
142 Error::Internal("CUDA-GDB not found".to_string())
143 })?;
144 let version = get_gdb_version(&cuda_gdb_path).await;
145
146 if has_native_dap_support(&cuda_gdb_path).await {
148 Ok(InstallResult {
150 path: cuda_gdb_path,
151 version,
152 args: vec!["-i=dap".to_string()],
153 })
154 } else {
155 Ok(InstallResult {
157 path,
158 version,
159 args: vec![format!("--config={{\"gdb\":\"{}\"}}", cuda_gdb_path.display())],
160 })
161 }
162 }
163 InstallMethod::NotSupported { reason } => {
164 Err(Error::Internal(format!("Cannot install CUDA-GDB: {}", reason)))
165 }
166 _ => Err(Error::Internal("Unexpected installation method".to_string())),
167 }
168 }
169
170 async fn uninstall(&self) -> Result<()> {
171 println!("CUDA-GDB is part of NVIDIA CUDA Toolkit. Uninstall the toolkit to remove it.");
172 Ok(())
173 }
174
175 async fn verify(&self) -> Result<VerifyResult> {
176 let status = self.status().await?;
177
178 match status {
179 InstallStatus::Installed { path, .. } => {
180 let cuda_gdb_path = find_cuda_gdb().ok_or_else(|| {
181 Error::Internal("CUDA-GDB not found".to_string())
182 })?;
183
184 if has_native_dap_support(&cuda_gdb_path).await {
186 verify_dap_adapter(&path, &["-i=dap".to_string()]).await
188 } else {
189 verify_dap_adapter(
191 &path,
192 &[format!("--config={{\"gdb\":\"{}\"}}", cuda_gdb_path.display())],
193 ).await
194 }
195 }
196 InstallStatus::Broken { reason, .. } => Ok(VerifyResult {
197 success: false,
198 capabilities: None,
199 error: Some(reason),
200 }),
201 InstallStatus::NotInstalled => Ok(VerifyResult {
202 success: false,
203 capabilities: None,
204 error: Some("Not installed".to_string()),
205 }),
206 }
207 }
208}
209
210fn find_cuda_gdb() -> Option<PathBuf> {
214 if let Ok(entries) = std::fs::read_dir("/usr/local") {
217 let mut cuda_paths: Vec<_> = entries
218 .flatten()
219 .filter_map(|e| {
220 let name = e.file_name().to_string_lossy().to_string();
221 if name.starts_with("cuda-") {
222 let cuda_gdb = e.path().join("bin/cuda-gdb");
223 if cuda_gdb.exists() {
224 let version = name.strip_prefix("cuda-").unwrap_or("0.0").to_string();
226 return Some((version, cuda_gdb));
227 }
228 }
229 None
230 })
231 .collect();
232
233 cuda_paths.sort_by(|a, b| {
235 let parse_version = |s: &str| -> (u32, u32) {
236 let parts: Vec<&str> = s.split('.').collect();
237 let major = parts.first().and_then(|p| p.parse().ok()).unwrap_or(0);
238 let minor = parts.get(1).and_then(|p| p.parse().ok()).unwrap_or(0);
239 (major, minor)
240 };
241 parse_version(&b.0).cmp(&parse_version(&a.0))
242 });
243
244 if let Some((_, path)) = cuda_paths.first() {
245 return Some(path.clone());
246 }
247 }
248
249 let default_path = PathBuf::from("/usr/local/cuda/bin/cuda-gdb");
251 if default_path.exists() {
252 return Some(default_path);
253 }
254
255 let arch_path = PathBuf::from("/opt/cuda/bin/cuda-gdb");
257 if arch_path.exists() {
258 return Some(arch_path);
259 }
260
261 if let Ok(cuda_home) = std::env::var("CUDA_HOME") {
263 let cuda_home_path = PathBuf::from(cuda_home).join("bin/cuda-gdb");
264 if cuda_home_path.exists() {
265 return Some(cuda_home_path);
266 }
267 }
268
269 which::which("cuda-gdb").ok()
271}
272
273fn find_cdt_gdb_adapter() -> Option<PathBuf> {
277 if let Ok(path) = which::which("cdtDebugAdapter") {
279 return Some(path);
280 }
281
282 if let Ok(home) = std::env::var("HOME") {
284 let nvm_path = PathBuf::from(&home).join(".nvm/versions/node");
286 if nvm_path.exists() {
287 if let Ok(entries) = std::fs::read_dir(&nvm_path) {
288 for entry in entries.flatten() {
289 let bin_path = entry.path().join("bin/cdtDebugAdapter");
290 if bin_path.exists() {
291 return Some(bin_path);
292 }
293 }
294 }
295 }
296
297 let npm_global = PathBuf::from(&home).join(".npm-global/bin/cdtDebugAdapter");
299 if npm_global.exists() {
300 return Some(npm_global);
301 }
302
303 let npm_prefix = PathBuf::from(&home).join("node_modules/.bin/cdtDebugAdapter");
305 if npm_prefix.exists() {
306 return Some(npm_prefix);
307 }
308 }
309
310 let system_path = PathBuf::from("/usr/local/bin/cdtDebugAdapter");
312 if system_path.exists() {
313 return Some(system_path);
314 }
315
316 None
317}