Skip to main content

debugger/setup/adapters/
gdb_common.rs

1//! Shared utilities for GDB-based adapters (GDB and CUDA-GDB)
2
3/// Extracts version string from GDB --version output
4///
5/// Searches for "GNU gdb" line and extracts the version number.
6/// Handles cuda-gdb output which may have "exec:" wrapper on first line.
7pub fn parse_gdb_version(output: &str) -> Option<String> {
8    for line in output.lines() {
9        // Skip cuda-gdb exec wrapper line
10        if line.starts_with("exec:") {
11            continue;
12        }
13        // Look for "GNU gdb X.Y" pattern to get the base GDB version
14        if line.contains("GNU gdb") {
15            let parts: Vec<&str> = line.split_whitespace().collect();
16            for (i, part) in parts.iter().enumerate() {
17                if *part == "gdb" {
18                    if let Some(version) = parts.get(i + 1) {
19                        // Verify it starts with a digit (version number)
20                        if version.chars().next().map_or(false, |c| c.is_ascii_digit()) {
21                            return Some(version.to_string());
22                        }
23                    }
24                }
25            }
26        }
27    }
28    // Fallback: try first line with digit token (for non-GDB outputs)
29    output
30        .lines()
31        .next()
32        .and_then(|line| {
33            line.split_whitespace()
34                .find(|token| token.chars().next().map_or(false, |c| c.is_ascii_digit()))
35        })
36        .map(|s| s.to_string())
37}
38
39/// Checks if GDB version meets DAP support requirement (≥14.1)
40///
41/// Returns false on parse failure to prevent launching incompatible GDB
42pub fn is_gdb_version_sufficient(version: &str) -> bool {
43    let parts: Vec<&str> = version.split('.').collect();
44    let Some(major_str) = parts.get(0) else {
45        return false;
46    };
47    let Some(minor_str) = parts.get(1) else {
48        return false;
49    };
50    let Ok(major) = major_str.parse::<u32>() else {
51        return false;
52    };
53    let Ok(minor) = minor_str.parse::<u32>() else {
54        return false;
55    };
56
57    major > 14 || (major == 14 && minor >= 1)
58}
59
60/// Retrieves GDB version by executing --version flag
61///
62/// Returns None on exec failure or unparseable output
63pub async fn get_gdb_version(path: &std::path::PathBuf) -> Option<String> {
64    let output = tokio::process::Command::new(path)
65        .arg("--version")
66        .output()
67        .await
68        .ok()?;
69
70    if output.status.success() {
71        let stdout = String::from_utf8_lossy(&output.stdout);
72        parse_gdb_version(&stdout)
73    } else {
74        None
75    }
76}